Você está na página 1de 4161

Dê a sua opinião sobre a experiência de download do PDF.

Documentação sobre os conceitos


básicos do .NET
Descubra os conceitos básicos do .NET, uma plataforma de desenvolvedor de código
aberto usada para criar vários tipos diferentes de aplicativos.

Conheça o .NET

a BAIXAR

Baixar o .NET

e VISÃO GERAL

O que é o .NET?

Introdução ao .NET

Linguagens .NET

p CONCEITO

.NET Standard

CLR (Common Language Runtime)

Política de suporte do .NET Core

h NOVIDADES

Novidades do .NET 9

Novidades do .NET 8

Novidades do .NET 7

Novidades no .NET 6

Instalar o .NET

e VISÃO GERAL

Selecione a versão do .NET a ser usada


c GUIA DE INSTRUÇÕES

Instalar o SDK do .NET

Instalar o runtime do .NET

Usar um gerenciador de pacotes do Linux para instalar

Verificar versões instaladas

i REFERÊNCIA

Dependências do SDK e do runtime do .NET

Introdução ao .NET

b COMEÇAR AGORA

Introdução ao .NET

Introdução ao ASP.NET Core

.NET no Q&A

Fóruns da comunidade técnica do .NET

q VIDEO

Tutorial: Olá, Mundo em 10 minutos

g TUTORIAL

Criar um aplicativo Olá, Mundo do C# no Visual Studio Code

Criar um aplicativo Olá, Mundo no Visual Studio

Colocar em contêiner um aplicativo .NET Core

p CONCEITO

Portar do .NET Framework para o .NET

` IMPLANTAR

Publicação de aplicativo
Publicar aplicativos .NET com GitHub Actions

Serializar dados

p CONCEITO

Serializar e desserializar JSON

c GUIA DE INSTRUÇÕES

Serializar e desserializar JSON usando C#

Migrar de Newtonsoft.Json para System.Text.Json

Gravar conversores personalizados para serialização JSON

s SAMPLE

Exemplos de serialização XML

Bibliotecas de runtime

e VISÃO GERAL

Visão geral das bibliotecas de runtime

p CONCEITO

Injeção de dependência no .NET

Configuração no .NET

Registro em log no .NET

Host genérico do .NET

Serviços de trabalho no .NET

Como armazenar em cache no .NET

HTTP no .NET

Localização no .NET

Globbing de arquivo no .NET


g TUTORIAL

Implementar um provedor de configuração personalizado

Compilar geração de origem de registro em log em tempo de compilação

Criar um Serviço Windows usando BackgroundService

Formatar e converter datas, números e cadeias de caracteres

p CONCEITO

Cadeias de caracteres de formato numérico

Cadeias de caracteres de formato de data e hora

Formatação de composição

Converter horários entre fusos horários

Cortar e remover caracteres de cadeias de caracteres

Expressões regulares no .NET

c GUIA DE INSTRUÇÕES

Converter cadeias de caracteres em DateTime

Preencher um número com zeros à esquerda

Exibir milissegundos em valores de data e hora

i REFERÊNCIA

Linguagem de expressão regular

Usar eventos e exceções

p CONCEITO

Práticas recomendadas para exceções

Manipular e gerar eventos

c GUIA DE INSTRUÇÕES
Usar um bloco try-catch para capturar exceções

Gerar e consumir eventos

E/S de arquivo e de fluxo

p CONCEITO

E/S de arquivo e de fluxo

Formatos de caminho de arquivo no Windows

c GUIA DE INSTRUÇÕES

Gravar texto em um arquivo

Ler texto de um arquivo

Compactar e extrair arquivos

Abrir um arquivo de log e fazer acréscimos a ele


Introdução ao .NET
Artigo • 10/05/2023

Este artigo ensina como criar e executar um aplicativo "Hello World!" com o .NET.

Criar um aplicativo
Primeiro, baixe e instale o SDK do .NET em seu computador.

Em seguida, abra um terminal, como o PowerShell, um prompt de comando ou o Bash.

Digite os seguintes comandos:

CLI do .NET

dotnet new console -o sample1


cd sample1
dotnet run

Você deve ver o seguinte resultado:

Saída

Hello World!

Parabéns! Você criou um aplicativo simples do .NET.

Próximas etapas
Comece a desenvolver aplicativos .NET seguindo um Tutorial passo a passo ou
assistindo vídeos do .NET 101 no YouTube.
Tutoriais para começar a usar o .NET
Artigo • 09/09/2023

Os tutoriais passo a passo a seguir são executados no Windows, no Linux ou no macOS,


exceto conforme observado.

Tutoriais para criar aplicativos


Criar um aplicativo do console
usando o Visual Studio Code
usando o Visual Studio (Windows)
Como criar um aplicativo Web
com a IU da Web do lado do servidor
com a IU da Web do lado do cliente
Criar uma API Web
Criar um aplicativo Web de chamada de procedimento remoto
Criar um aplicativo Web em tempo real
Criar uma função sem servidor na nuvem
Criar um aplicativo móvel para Android e iOS (Windows)
Criar um aplicativo da área de trabalho do Windows
WPF
Windows Forms
UWP (Plataforma Universal do Windows)
Criar um jogo usando o Unity
Criar um serviço do Windows

Tutoriais para criar bibliotecas de classes


Criar uma biblioteca de classes
usando o Visual Studio Code
usando o Visual Studio (Windows)

Recursos para aprender linguagens .NET


Introdução ao C#
Introdução ao F#
Introdução ao Visual Basic
Outros recursos de introdução
Os seguintes recursos introduzem você ao desenvolvimento de aplicativos .NET, mas
não são tutoriais passo a passo:

Internet das coisas (IoT)


Aprendizado de máquina

Próximas etapas
Para saber mais sobre o .NET, confira Introdução ao .NET.

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Instalar o .NET em Windows, Linux e
macOS
Saiba como instalar o .NET no Windows, no Linux e no macOS. Descubra as
dependências necessárias para desenvolver, implantar e executar aplicativos .NET.

Windows

e VISÃO GERAL

Instalar no Windows

Versões do Windows com suporte

Dependências

Instalar com o Visual Studio

Instalar junto com o Visual Studio Code

macOS

e VISÃO GERAL

Instalar no macOS

Versões do macOS com suporte

Instalar junto com o Visual Studio Code

Linux

e VISÃO GERAL

Visão geral do Linux

Alpine

CentOS

Debian

Fedora
openSUSE

Red Hat Enterprise Linux e CentOS Stream

SUSE Linux Enterprise Server

Ubuntu

Perguntas e respostas

b COMEÇAR AGORA

Instaladores autônomos

Instalador do Visual Studio

Feeds do Linux

Docker

Implantação corporativa
Instalar o .NET no Windows
Artigo • 22/12/2023

Neste artigo, você aprenderá a instalar o .NET no Windows. O .NET é composto pelo
runtime e pelo SDK. O runtime é usado para executar um aplicativo .NET e pode estar
incluído com o aplicativo. O SDK é usado para criar bibliotecas e aplicativos .NET. O
runtime do .NET é sempre instalado com o SDK.

A versão mais recente do .NET é a 8.0.

Baixar o .NET

Há dois tipos de versões com suporte: versões LTS (Suporte de Longo Prazo) e versões
STS (Suporte com Prazo Padrão). A qualidade de todas as versões é a mesma. A única
diferença é a duração do suporte. As versões LTS recebem suporte e patches gratuitos
por três anos. As versões STS recebem suporte e patches gratuitos por 18 meses. Para
obter mais informações, consulte a Política de Suporte do .NET .

A seguinte tabela lista o status de suporte de cada versão do .NET (e do .NET Core):

ノ Expandir a tabela

✔️Com suporte ❌ Sem suporte

8 (LTS) 5

7 (STS) 3.1

6 (LTS) 3.0

2.1

2,0

1,1

1.0

Instalar com Gerenciador de Pacotes do


Windows (winget)
Você pode instalar e gerenciar o .NET por meio do serviço de Gerenciador de Pacotes
do Windows, usando a ferramenta winget. Para obter mais informações sobre como
instalar e usar o winget, consulte Usar a ferramenta winget.

Se você estiver instalando o .NET em todo o sistema, instale com privilégios


administrativos.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Prompt de comando do Windows

winget install Microsoft.DotNet.SDK.8

Instalar o runtime
Há três runtimes diferentes do .NET que você pode instalar, no entanto, você deve
instalar o Runtime do .NET Desktop e o Runtime do ASP.NET Core para obter máxima
compatibilidade com todos os tipos de aplicativos .NET. A tabela a seguir descreve o
que está incluído em cada runtime:

ノ Expandir a tabela

Inclui o Runtime Inclui o Runtime do Inclui o Runtime do


do .NET .NET Desktop ASP.NET Core

Runtime do .NET Sim Não Não

Runtime do .NET Sim Sim Não


Desktop

Runtime do Não No Sim


ASP.NET Core

A lista a seguir fornece detalhes sobre cada runtime junto com os comandos winget
para instalá-los:

Runtime do .NET Desktop

Esse runtime dá suporte a aplicativos do WPF (Windows Presentation Foundation)


e do Windows Forms compilados com o .NET. Isso não é o mesmo que .NET
Framework, que vem com o Windows. Esse runtime inclui o Runtime do .NET, mas
não inclui o Runtime do ASP.NET Core, que deve ser instalado separadamente.
Prompt de comando do Windows

winget install Microsoft.DotNet.DesktopRuntime.8

Execução do .NET

Esse é o runtime base e contém apenas os componentes necessários para executar


um aplicativo de console. Normalmente, você instalaria o Runtime do .NET
Desktop e o Runtime do ASP.NET Core em vez deste.

Prompt de comando do Windows

winget install Microsoft.DotNet.Runtime.8

Runtime do ASP.NET Core

Esse runtime executa aplicativos de servidor Web e fornece muitas APIs


relacionadas à Web. O Runtime do ASP.NET Core permite executar aplicativos
criados com o .NET que não forneceram o runtime. Você deve instalar o Runtime
do .NET além deste runtime. Os comandos a seguir instalam Runtime ASP.NET
Core. No seu terminal, execute os seguintes comandos:

Prompt de comando do Windows

winget install Microsoft.DotNet.AspNetCore.8

Você pode instalar versões prévias dos runtimes substituindo o número de versão, como
6 , pela palavra Preview . O exemplo a seguir instala a versão prévia do Runtime da Área

de Trabalho do .NET:

Prompt de comando do Windows

winget install Microsoft.DotNet.DesktopRuntime.Preview

Instalar junto com o Visual Studio Code


Visual Studio Code é um editor de código-fonte leve e avançado que é executado em
sua área de trabalho. Visual Studio Code está disponível para Windows, macOS e Linux.

Embora Visual Studio Code não venha com um instalador automatizado do .NET Core
como o Visual Studio, adicionar suporte ao .NET Core é simples.
1. Baixar e instalar o Visual Studio Code .
2. Baixar e instalar o .NET SDK .
3. Instalar a extensão C# do Visual Studio Code Marketplace .

A extensão C# para o Visual Studio Code inclui o SDK do .NET mais recente e você não
precisa instalar nenhum runtime do .NET separadamente.

Instalar com o Windows Installer


Há três runtimes diferentes do .NET que você pode instalar, no entanto, você deve
instalar o Runtime do .NET Desktop e o Runtime do ASP.NET Core para obter máxima
compatibilidade com todos os tipos de aplicativos .NET. A tabela a seguir descreve o
que está incluído em cada runtime:

ノ Expandir a tabela

Inclui o Runtime Inclui o Runtime do Inclui o Runtime do


do .NET .NET Desktop ASP.NET Core

Runtime do .NET Sim Não Não

Runtime do .NET Sim Sim Não


Desktop

Runtime do Não No Sim


ASP.NET Core

O SDK do .NET permite que você crie aplicativos .NET e inclui todos os runtimes.

A página de download do .NET fornece executáveis do Windows Installer.

Se você quiser instalar o .NET silenciosamente, como em um ambiente de produção ou


para dar suporte à integração contínua, use as seguintes opções:

/install

Instala o .NET.

/quiet

Impede que qualquer interface do usuário e prompts sejam exibidas.

/norestart

Suprime todas as tentativas de reinicialização.

Console
dotnet-sdk-8.0.100-win-x64.exe /install /quiet /norestart

Para obter mais informações, consulte Opções de linha de comando do instalador


padrão.

 Dica

O instalador retorna um código de saída 0 em caso de sucesso e um código de


saída de 3010 para indicar que uma reinicialização é necessária. Qualquer outro
valor geralmente é um código de erro.

Instalar com a automação do PowerShell


Os scripts dotnet-install são usados para instalações de automação e não
administrativas do SDK e do Runtime. Você pode baixar o script na página de referência
de script dotnet-install.

O script usa como padrão a instalação da versão LTS (suporte de longo prazo) mais
recente, que é o .NET 8. Você pode escolher uma versão específica definindo a opção
Channel . Inclua a opção Runtime para instalar um runtime. Caso contrário, o script

instala o SDK.

O comando a seguir instala os runtimes do Desktop e do ASP.NET Core para obter


compatibilidade máxima.

PowerShell

dotnet-install.ps1 -Channel 8.0 -Runtime windowsdesktop


dotnet-install.ps1 -Channel 8.0 -Runtime aspnetcore

Instale o SDK omitindo a opção -Runtime . A opção -Channel é definida neste exemplo
como STS , que instala a versão mais recente do Suporte com Prazo Padrão, que é .NET
7.

PowerShell

dotnet-install.ps1 -Channel STS

Instalar com o Visual Studio


Se você estiver usando o Visual Studio para desenvolver aplicativos .NET, a tabela a
seguir descreverá a versão mínima necessária do Visual Studio com base na versão de
destino do SDK do .NET.

ノ Expandir a tabela

Versão do SDK do .NET Versão do Visual Studio

8 Visual Studio 2022 versão 17.8 ou superior.

7 Visual Studio 2022 versão 17.4 ou superior.

6 Visual Studio 2022 versão 17.0 ou superior.

5 Visual Studio 2019 versão 16.8 ou superior.

3.1 Visual Studio 2019 versão 16.4 ou superior.

3,0 Visual Studio 2019 versão 16.3 ou superior.

2,2 Visual Studio 2017 versão 15.9 ou superior.

2.1 Visual Studio 2017 versão 15.7 ou superior.

Se você já tiver o Visual Studio instalado, poderá verificar sua versão com as etapas a
seguir.

1. Abra o Visual Studio.


2. Selecione Ajuda>sobre o Microsoft Visual Studio.
3. Leia o número da versão da caixa de diálogo Sobre .

O Visual Studio pode instalar o SDK e o runtime mais recentes do .NET.

Baixar o Visual Studio .

Para obter mais informações, consulte o SDK do .NET, o MSBuild e o controle de versão
do Visual Studio.

Selecionar uma carga de trabalho


Ao instalar ou modificar o Visual Studio, selecione uma ou mais das seguintes cargas de
trabalho, dependendo do tipo de aplicativo que você está criando:

A carga de trabalho de desenvolvimento multiplataforma do .NET Core na seção


Outros Conjuntos de Ferramentas .
A carga de trabalhoASP.NET e desenvolvimento web na seção Web & Nuvem.
A carga de trabalho de desenvolvimento do Azure na seção Web & Nuvem
A carga de trabalho de desenvolvimento da área de trabalho do .NET na seção
Desktop & Mobile.

Versões com suporte


A tabela a seguir é uma lista de versões .NET atualmente com suporte e as versões do
Windows nas quais elas têm suporte. Essas versões permanecerão com suporte até que
a versão do .NET atinja o fim do suporte ou a versão do Windows atinja o fim da vida
útil .

Windows 10 datas de fim de serviço de versões são segmentadas por edição. Somente
as edições Home, Pro, Pro Education e Pro for Workstations são consideradas na
tabela a seguir. Verifique a folha de fatos do ciclo de vida do Windows para obter
detalhes específicos.

 Dica

Um símbolo + representa a versão mínima.

ノ Expandir a tabela

Sistema operacional .NET 8 .NET 7 .NET 6

Windows 11 ✔️ ✔️ ✔️

Windows Server 2022 ✔️ ✔️ ✔️


Sistema operacional .NET 8 .NET 7 .NET 6

Windows Server, versão 1903 ou posterior ✔️ ✔️ ✔️

Windows 10, versão 1607 ou posterior ✔️ ✔️ ✔️

Windows 8.1 ❌ ❌ ✔️

Windows 7 SP1 ESU ❌ ❌ ✔️

Windows Server 2019 ✔️


Windows Server 2016
Windows Server 2012 R2
Windows Server 2012

Windows Server Core 2012 R2 ✔️ ✔️ ✔️

Windows Server Core 2012 ✔️ ✔️ ✔️

Nano Server versão 1809+ ✔️ ✔️ ✔️

Nano Server versão 1803 ❌ ❌ ❌

Para obter mais informações sobre sistemas operacionais compatíveis com .NET 8,
distribuições e política de ciclo de vida, consulte Versões do sistema operacional com
suporte no .NET 8 .

Versões sem suporte


Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Verificar binários baixados


Depois de baixar um instalador, verifique se o arquivo não foi alterado ou corrompido.
Você pode obter a soma de verificação no computador e depois compará-la com o que
foi relatado no site de download.

Quando você baixa um instalador ou um binário de uma página de download oficial, a


soma de verificação do arquivo é exibida. Selecione o botão Copiar a fim de copiar o
valor de soma de verificação para a área de transferência.

Você pode usar o PowerShell ou o prompt de comando para validar a soma de


verificação do arquivo baixado. Por exemplo, o seguinte comando relata a soma de
verificação do arquivo dotnet-sdk-8.0.100-win-x64.exe:

Prompt de comando do Windows

> certutil -hashfile dotnet-sdk-8.0.100-win-x64.exe SHA512


SHA512 hash of dotnet-sdk-8.0.100-win-x64.exe:
248acec95b381e5302255310fb9396267fd74a4a2dc2c3a5989031969cb31f8270cbd14bda1b
c0352ac90f8138bddad1a58e4af1e56cc4a1613b1cf2854b518e
CertUtil: -hashfile command completed successfully.

PowerShell

> (Get-FileHash .\dotnet-sdk-8.0.100-win-x64.exe -Algorithm SHA512).Hash


248acec95b381e5302255310fb9396267fd74a4a2dc2c3a5989031969cb31f8270cbd14bda1b
c0352ac90f8138bddad1a58e4af1e56cc4a1613b1cf2854b518e

Compare a soma de verificação com o valor fornecido pelo site de download.

Usar o PowerShell e um arquivo de soma de verificação


para validação
As notas sobre a versão do .NET contêm um link para um arquivo de soma de
verificação que você pode usar para validar o arquivo baixado. As seguintes etapas
descrevem como baixar o arquivo de soma de verificação e validar um binário de
instalação do .NET:

1. A página de notas sobre a versão do .NET 8 no GitHub em


https://github.com/dotnet/core/tree/main/release-notes/8.0 contém uma seção
chamada Versões. A tabela nessa seção está vinculada aos arquivos de soma de
verificação e downloads de cada versão do .NET 8:

2. Selecione o link da versão do .NET que você baixou. A seção anterior usou o SDK
do .NET 8.0.100, que está na versão 8.0.0 do .NET.

 Dica

Se você não sabe qual versão do .NET contém o arquivo de soma de


verificação, explore os links até encontrá-lo.

3. Na página de lançamento, você pode ver a versão do Runtime do .NET e do SDK


do .NET e um link para o arquivo de soma de verificação:

4. Copie o link do arquivo de soma de verificação.

5. Use o script a seguir, mas substitua o link para baixar o arquivo de soma de
verificação apropriado:
PowerShell

Invoke-WebRequest
https://dotnetcli.blob.core.windows.net/dotnet/checksums/8.0.0-sha.txt
-OutFile 8.0.0-sha.txt

6. Com o arquivo de soma de verificação e o arquivo de versão do .NET baixados no


mesmo diretório, pesquise no arquivo de soma de verificação a soma de
verificação do download do .NET:

Quando a validação for aprovada, você verá True impresso:

PowerShell

> (Get-Content .\8.0.0-sha.txt | Select-String "dotnet-sdk-8.0.100-win-


x64.exe").Line -like (Get-FileHash .\dotnet-sdk-8.0.100-win-x64.exe -
Algorithm SHA512).Hash + "*"
True

Se False for impresso, o arquivo baixado não será válido e não deverá ser usado.

Informações de runtime
O runtime é usado para executar aplicativos criados com .NET. Quando um autor de
aplicativo publica um aplicativo, ele pode incluir o runtime com seu aplicativo. Se eles
não incluirem o runtime, cabe ao usuário instalar o runtime.

Há três runtimes diferentes do .NET que você pode instalar, no entanto, você deve
instalar o Runtime do .NET Desktop e o Runtime do ASP.NET Core para obter máxima
compatibilidade com todos os tipos de aplicativos .NET. A tabela a seguir descreve o
que está incluído em cada runtime:

ノ Expandir a tabela

Inclui o Runtime Inclui o Runtime do Inclui o Runtime do


do .NET .NET Desktop ASP.NET Core

Runtime do .NET Sim Não Não

Runtime do .NET Sim Sim Não


Desktop

Runtime do Não No Sim


ASP.NET Core
A lista a seguir fornece detalhes sobre cada runtime:

Runtime do Desktop
Executa os aplicativos da área de trabalho .NET WPF e Windows Forms para
Windows. Inclui o runtime do .NET.

Runtime do ASP.NET Core


Executa aplicativos ASP.NET Core.

Runtime do .NET
Esse runtime é o runtime mais simples e não inclui nenhum outro runtime. Instale
o Runtime do ASP.NET Core e o Runtime do Desktop para obter a melhor
compatibilidade com aplicativos .NET.

Baixar o runtime do .NET

Informações do SDK
O SDK é usado para compilar e publicar aplicativos e bibliotecas .NET. A instalação do
SDK inclui os três runtimes: ASP.NET Core, Desktop e .NET.

Baixar o SDK do .NET

Computadores Windows baseados em Arm


As seções a seguir descrevem as coisas que você deve considerar ao instalar o .NET em
um computador Windows baseado em Arm.

O que tem suporte


A tabela a seguir descreve quais versões do .NET têm suporte em um computador
Windows baseado em Arm:

ノ Expandir a tabela

Versão .NET Arquitetura . Runtime Conflito de caminhos

8 Arm64 Sim Sim Não

8 x64 Sim Sim Não

7 Arm64 Sim Sim Não


Versão .NET Arquitetura . Runtime Conflito de caminhos

7 x64 Sim Sim Não

6 Arm64 Sim Sim Não

6 x64 Sim Sim Não

5 Arm64 Sim Sim Sim

5 x64 No Sim Sim

As versões x64 e Arm64 do SDK do .NET existem de modo independente uma da outra.
Se uma nova versão for lançada, cada instalação precisará ser atualizada.

Diferenças de caminho
Em um computador Windows baseado em Arm, todas as versões do Arm64 do .NET são
instaladas na pasta C:\Program Files\dotnet\ normal. No entanto, a versão x64 do SDK
do .NET é instalada na pasta C:\Arquivos de Programas\dotnet\x64\.

Conflitos de caminho
O SDK do .NET x64 é instalado em seu próprio diretório, conforme descrito na seção
anterior. Isso permite que as versões Arm64 e x64 do SDK do .NET 6 existam no mesmo
computador. No entanto, não há suporte a nenhum SDK x64 antes do 6, e ele é
instalado no mesmo local da versão Arm64, a pasta C:\Program Files\dotnet\. Se você
quiser instalar um SDK x64 sem suporte, desinstale primeiro a versão do Arm64. Isso
também vale para o oposto. Você precisará desinstalar o SDK x64 sem suporte para
instalar a versão do Arm64.

Variáveis de caminho
Variáveis de ambiente que adicionam .NET ao caminho do sistema, como a variável
PATH , podem precisar ser alteradas se você tiver as versões x64 e Arm64 do SDK do

.NET 6 instaladas. Além disso, algumas ferramentas dependem da variável de ambiente


DOTNET_ROOT , que também precisaria ser atualizada para apontar para a pasta de

instalação apropriada do SDK do .NET 6.

Dependências
.NET 8
As seguintes versões do Windows têm suporte no .NET 8:

7 Observação

Um símbolo + representa a versão mínima.

ノ Expandir a tabela

Sistema operacional Versão Arquiteturas

Windows 11 22000+ x64, x86, Arm64

Cliente do Windows 10 1607+ x64, x86, Arm64

Windows Server 2012+ x64, x86

Núcleo do Windows Server 2012+ x64, x86

Nano Server 1809+ x64

Para obter mais informações sobre sistemas operacionais compatíveis com .NET 8,
distribuições e política de ciclo de vida, consulte Versões do sistema operacional
com suporte no .NET 8 .

Windows 7/8.1/Server 2012


Mais dependências serão necessárias se você estiver instalando o SDK do .NET ou o
runtime nas seguintes versões do Windows:

ノ Expandir a tabela

Sistema operacional Pré-requisitos

Windows 7 SP1 ESU - Microsoft Visual C++ 2015-2019 redistribuível 64-bit / 32-bit
- KB3063858 64-bit / 32-bit
- Autoridade de certificado raiz da Microsoft 2011 (.NET Core 2.1
offline installer only)

Windows 8.1 Microsoft Visual C++ 2015-2019 redistribuível 64-bit / 32-bit

Windows Server 2012 Microsoft Visual C++ 2015-2019 redistribuível 64-bit / 32-bit

Windows Server 2012 Microsoft Visual C++ 2015-2019 redistribuível 64-bit / 32-bit
R2
Os requisitos anteriores também serão necessários se você receber um erro relacionado
a qualquer uma das seguintes dlls:

api-ms-win-crt-runtime-l1-1-0.dll
api-ms-win-cor-timezone-l1-1-0.dll
hostfxr.dll

Docker
Os contêineres fornecem uma maneira leve de isolar seu aplicativo do restante do
sistema host. Os contêineres no mesmo computador compartilham apenas o kernel e
usam recursos fornecidos ao seu aplicativo.

O .NET pode ser executado em um contêiner do Docker. As imagens oficiais do Docker


do .NET Core são publicadas no MCR (Registro de Contêiner da Microsoft) e podem ser
encontradas no repositório do Docker Hub do .NET Core da Microsoft . Cada
repositório contém imagens para diferentes combinações do .NET (SDK ou Runtime) e
do sistema operacional que você pode usar.

A Microsoft fornece imagens personalizadas para cenários específicos. Por exemplo, o


repositório do ASP.NET Core fornece imagens que são criadas para a execução de
aplicativos ASP.NET Core na produção.

Para obter mais informações sobre como usar o .NET em um contêiner do Docker,
consulte Introdução ao .NET e docker e Exemplos .

Solução de problemas
Depois de instalar o SDK do .NET, você pode ter problemas ao tentar executar
comandos da CLI do .NET. Esta seção coleta esses problemas comuns e fornece
soluções.

Nenhum SDK do .NET foi encontrado

Nenhum SDK do .NET foi encontrado


Provavelmente, você instalou as versões x86 (32 bits) e x64 (64 bits) do SDK do .NET.
Isso está causando um conflito porque quando você executa o comando dotnet , ele
está resolvendo para a versão x86 quando ele deve ser resolvido para a versão x64. Isso
geralmente é corrigido ajustando a variável %PATH% para resolver a versão x64 primeiro.
1. Verifique se você tem as duas versões instaladas executando o comando where.exe
dotnet . Se tiver, você deverá ver uma entrada para as pastas Arquivos de

Programas e Arquivos de Programas (x86)\ . Se a pasta Arquivos de Programas


(x86)\ for a primeira, conforme mostrado no exemplo a seguir, ela estará incorreta
e você deverá continuar para a próxima etapa.

Prompt de comando do Windows

> where.exe dotnet


C:\Program Files (x86)\dotnet\dotnet.exe
C:\Program Files\dotnet\dotnet.exe

Se estiver correto e os Arquivos de Programas\ estiverem em primeiro lugar, você


não terá o problema que esta seção está discutindo e você deve criar um
problema de solicitação de ajuda do .NET no GitHub

2. Pressione o botão do Windows e digite "Editar as variáveis de ambiente do


sistema" na pesquisa. Selecione Editar as variáveis de ambiente do sistema.
3. A janela Propriedades do Sistema é aberta para a guia Avançado. Selecione
Variáveis de Ambiente.

4. Na janela Variáveis de Ambiente, no grupo variáveis do sistema , selecione a linha


Caminho* e, em seguida, o botão Editar .
5. Use os botões Mover para cima e mover para baixo para mover a entrada
C:\Arquivos de Programas\dotnet\ acima de C:\Arquivos de Programas
(x86)\dotnet\.
Próximas etapas
Como verificar se o .NET já está instalado.
Tutorial: Tutorial Hello World.
Tutorial: Criar um aplicativo com o Visual Studio Code.
Tutorial: Colocar um aplicativo .NET Core em contêineres

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o .NET no macOS
Artigo • 08/01/2024

Neste artigo, você aprenderá a instalar o .NET no macOS. O .NET é composto pelo
runtime e pelo SDK. O runtime é usado para executar um aplicativo .NET e pode ou não
ser incluído com o aplicativo. O SDK é usado para criar bibliotecas e aplicativos .NET. O
runtime do .NET é sempre instalado com o SDK.

A versão mais recente do .NET é a 8.

Baixar o .NET

Versões com suporte


Há dois tipos de versões com suporte: versões LTS (Suporte de Longo Prazo) e versões
STS (Suporte com Prazo Padrão). A qualidade de todas as versões é a mesma. A única
diferença é a duração do suporte. As versões LTS recebem suporte e patches gratuitos
por três anos. As versões STS recebem suporte e patches gratuitos por 18 meses. Para
obter mais informações, confira a Política de Suporte do .NET .

A tabela a seguir é uma lista atualizada de versões do .NET com suporte e as versões do
macOS com suporte:

ノ Expandir a tabela

Sistema operacional .NET 8 (LTS) .NET 7 (STS) .NET 6 (LTS)

macOS 14.0 "Sonoma" ✔️8.0 ✔️7.0 ✔️6.0

macOS 13.0 "Ventura" ✔️8.0 ✔️7.0 ✔️6.0

macOS 12.0 "Monterey" ✔️8.0 ✔️7.0 ✔️6.0

macOS 11.0 "Big Sur" ❌ ✔️7.0 ✔️6.0

macOS 10.15 "Catalina" ❌ ✔️7.0 ✔️6.0

Para obter uma lista completa de versões do .NET e seu ciclo de vida de suporte,
consulte Política de Suporte do .NET .

Versões sem suporte


Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Informações de runtime
O runtime é usado para executar aplicativos criados com .NET. Quando um autor de
aplicativo publica um aplicativo, ele pode incluir o runtime com seu aplicativo. Se eles
não incluírem o runtime, cabe ao usuário instalar o runtime.

Há dois runtimes diferentes que você pode instalar no macOS:

Runtime do ASP.NET Core


Executa aplicativos ASP.NET Core. Inclui o runtime do .NET.

Runtime do .NET
Esse runtime é o runtime mais simples e não inclui nenhum outro runtime. É
altamente recomendável que você instale o ASP.NET Core runtime para obter a
melhor compatibilidade com aplicativos .NET.

Baixar o runtime do .NET

Informações do SDK
O SDK é usado para compilar e publicar aplicativos e bibliotecas .NET. A instalação do
SDK inclui os dois runtimes: ASP.NET Core e .NET.

Autenticação
A partir do macOS Catalina (versão 10.15), todos os softwares criados após 1º de junho
de 2019 e distribuídos com a ID do Desenvolvedor devem ser autenticados. Esse
requisito se aplica ao runtime do .NET, ao SDK do .NET e ao software criado com o .NET.

Os instaladores do runtime e SDK do .NET são autenticados desde 18 de fevereiro de


2020. As versões anteriores lançadas não são autenticadas. Se você executar um
aplicativo não autenticado, verá um erro semelhante à seguinte imagem:
Para saber mais sobre como a autenticação imposta afeta o .NET (e seus aplicativos
.NET), confira Trabalhar com a autenticação do macOS Catalina.

libgdiplus
Os aplicativos .NET que usam o assembly System.Drawing.Common exigem que o
libgdiplus seja instalado.

Uma maneira fácil de obter o libgdiplus é usando o gerenciador de pacotes Homebrew


("brew") para macOS. Depois de instalar o brew, instale o libgdiplus executando os
seguintes comandos em um prompt (de comando) do Terminal:

Console

brew update
brew install mono-libgdiplus

Instalação automatizada
O macOS tem instaladores independentes que podem ser usados para instalar o .NET:

✔️Downloads do .NET 8
✔️Downloads do .NET 7
✔️Downloads do .NET 6

Instalação manual
Como alternativa aos instaladores do macOS para .NET, você pode baixar e instalar
manualmente o SDK ou o runtime. A instalação manual costuma ser executada como
parte do teste de integração contínua. Para um desenvolvedor ou usuário, geralmente é
melhor usar um instalador .

Baixe uma versão binária do SDK ou do runtime em um dos sites a seguir. O SDK do
.NET inclui o runtime correspondente:

✔️Downloads do .NET 8
✔️Downloads do .NET 7
✔️Downloads do .NET 6
Todos os downloads de .NET

Extraia o arquivo baixado e use o comando export para definir DOTNET_ROOT como o
local da pasta extraída e certifique-se de que o .NET esteja em PATH. A exportação de
DOTNET_ROOT disponibiliza os comandos da CLI do .NET no terminal. Para saber mais

sobre as variáveis de ambiente do .NET, confira Variáveis de ambiente da CLI e do SDK


do .NET.

Diferentes versões do .NET podem ser extraídas para a mesma pasta, que coexistem
lado a lado.

Exemplo
Os comandos a seguir usam o Bash para definir a variável de ambiente DOTNET_ROOT
para o diretório de trabalho atual seguida de .dotnet . Se não existir, o diretório será
criado. A variável de ambiente DOTNET_FILE é o nome do arquivo da versão binária do
.NET que você quer instalar. Esse arquivo é extraído para o diretório DOTNET_ROOT . O
diretório DOTNET_ROOT e seu subdiretório tools são adicionados à variável de ambiente
PATH .

) Importante

Se você executar esses comandos, lembre-se de alterar o valor DOTNET_FILE para o


nome do binário do .NET que você baixou.

Bash

DOTNET_FILE=dotnet-sdk-8.0.100-osx-x64.tar.gz
export DOTNET_ROOT=$(pwd)/.dotnet

mkdir -p "$DOTNET_ROOT" && tar zxf "$DOTNET_FILE" -C "$DOTNET_ROOT"


export PATH=$PATH:$DOTNET_ROOT

Você pode instalar mais de uma versão do .NET na mesma pasta.

Você também pode instalar o .NET no diretório inicial identificado pela variável HOME ou
caminho ~ :

Bash

export DOTNET_ROOT=$HOME/.dotnet

Verificar binários baixados


Depois de baixar um instalador, verifique se o arquivo não foi alterado ou corrompido.
Você pode obter a soma de verificação no computador e depois compará-la com o que
foi relatado no site de download.

Quando você baixa um instalador ou um binário de uma página de download oficial, a


soma de verificação do arquivo é exibida. Selecione o botão Copiar a fim de copiar o
valor de soma de verificação para a área de transferência.

Use o comando sha512sum para imprimir a soma de verificação do arquivo que você
baixou. Por exemplo, o comando a seguir relata a soma de verificação do arquivo
dotnet-sdk-8.0.100-linux-x64.tar.gz:

Bash

$ sha512sum dotnet-sdk-8.0.100-linux-x64.tar.gz
13905ea20191e70baeba50b0e9bbe5f752a7c34587878ee104744f9fb453bfe439994d389697
22bdae7f60ee047d75dda8636f3ab62659450e9cd4024f38b2a5 dotnet-sdk-8.0.100-
linux-x64.tar.gz

Compare a soma de verificação com o valor fornecido pelo site de download.

) Importante

Embora um arquivo Linux seja mostrado nesses exemplos, essas informações se


aplicam igualmente ao macOS.

Usar um arquivo de soma de verificação para validar


As notas sobre a versão do .NET contêm um link para um arquivo de soma de
verificação que você pode usar para validar o arquivo baixado. As seguintes etapas
descrevem como baixar o arquivo de soma de verificação e validar um binário de
instalação do .NET:

1. A página de notas sobre a versão do .NET 8 no GitHub em


https://github.com/dotnet/core/tree/main/release-notes/8.0 contém uma seção
chamada Versões. A tabela nessa seção está vinculada aos arquivos de soma de
verificação e downloads de cada versão do .NET 8:

2. Selecione o link da versão do .NET que você baixou. A seção anterior usou o SDK
do .NET 8.0.100, que está na versão 8.0.0 do .NET.

3. Na página de lançamento, você pode ver a versão do Runtime do .NET e do SDK


do .NET e um link para o arquivo de soma de verificação:
4. Copie o link do arquivo de soma de verificação.

5. Use o script a seguir, mas substitua o link para baixar o arquivo de soma de
verificação apropriado:

Bash

curl -O https://dotnetcli.blob.core.windows.net/dotnet/checksums/8.0.0-
sha.txt

6. Com o arquivo de soma de verificação e o arquivo de versão do .NET baixado para


o mesmo diretório, use o comando sha512sum -c {file} --ignore-missing para
validar o arquivo baixado.

Quando a validação for aprovada, você verá o arquivo impresso com o status OK:

Bash

$ sha512sum -c 8.0.0-sha.txt --ignore-missing


dotnet-sdk-8.0.100-linux-x64.tar.gz: OK

Se você vir o arquivo marcado como COM FALHA, o arquivo baixado não será
válido e não deverá ser usado.

Bash

$ sha512sum -c 8.0.0-sha.txt --ignore-missing


dotnet-sdk-8.0.100-linux-x64.tar.gz: FAILED
sha512sum: WARNING: 1 computed checksum did NOT match
sha512sum: 8.0.0-sha.txt: no file was verified
Definir variáveis de ambiente em todo o
sistema
Se você usou as instruções na seção exemplo de instalação manual, as variáveis
definidas se aplicam apenas à sessão do terminal atual. Adicione-as ao perfil de shell. Há
muitos shells diferentes disponíveis para macOS e cada um tem um perfil diferente. Por
exemplo:

Shell Bash: ~/.profile, /etc/profile


Korn Shell: ~/.kshrc ou .profile
Z Shell: ~/.zshrc ou .zprofile

Defina as duas variáveis de ambiente a seguir no perfil de shell:

DOTNET_ROOT

Essa variável é definida como a pasta na qual o .NET foi instalado, como
$HOME/.dotnet :

Bash

export DOTNET_ROOT=$HOME/.dotnet

PATH

Essa variável deve incluir tanto a pasta DOTNET_ROOT quanto a pasta


DOTNET_ROOT/tools :

Bash

export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools

Macs baseados em Arm


As seções a seguir descrevem as coisas que você deve considerar ao instalar o .NET em
um Mac baseado em Arm.

O que tem suporte


A tabela a seguir descreve quais versões do .NET têm suporte em um Mac baseado em
Arm:
ノ Expandir a tabela

Versão .NET Arquitetura . Runtime Conflito de caminhos

8 Arm64 Sim Sim Não

8 x64 Sim Sim Não

7 Arm64 Sim Sim Não

7 x64 Sim Sim Não

6 Arm64 Sim Sim Não

6 x64 Sim Sim Não

A partir do .NET 6, as versões x64 e Arm64 do SDK do .NET existem independentemente


umas das outras. Se uma nova versão for lançada, cada instalação precisará ser
atualizada.

Diferenças de caminho
Em um Mac baseado em Arm, todas as versões do Arm64 do .NET são instaladas na
pasta normal /usr/local/share/dotnet/. No entanto, quando você instala a versão x64 do
SDK do .NET, ela é instalada na pasta /usr/local/share/dotnet/x64/dotnet/.

Conflitos de caminho
A partir do .NET 6, a versão x64 do SDK do .NET é instalada em seu próprio diretório,
conforme descrito na seção anterior. Assim, as versões Arm64 e x64 do SDK do .NET 6
podem existir no mesmo computador. No entanto, não há suporte a nenhum SDK x64
antes do .NET 6, e instalado no mesmo local da versão do Arm64, a pasta
/usr/local/share/dotnet/. Se você quiser instalar um SDK x64 sem suporte, precisará
primeiro desinstalar a versão do Arm64. O oposto também é verdadeiro. Você precisará
desinstalar o SDK x64 sem suporte para instalar a versão do Arm64.

Variáveis de caminho
Variáveis de ambiente que adicionam .NET ao caminho do sistema, como a variável
PATH , talvez precisem ser alteradas se você tiver as versões x64 e Arm64 do SDK do .NET

6 instaladas. Além disso, algumas ferramentas dependem da variável de ambiente


DOTNET_ROOT , que também precisaria ser atualizada para apontar para a pasta de

instalação apropriada do SDK do .NET 6.


Instalar com o Visual Studio para Mac
O Visual Studio para Mac instala o SDK do .NET quando a carga de trabalho do .NET é
selecionada. Para começar a usar o desenvolvimento do .NET no macOS, confira Instalar
o Visual Studio 2019 para Mac.

ノ Expandir a tabela

Versão do SDK do .NET Versão do Visual Studio

8.0 Visual Studio 2022 para Mac 17.8 ou posterior.

7.0 Visual Studio 2022 para Mac 17.4 ou posterior.

6,0 Versão prévia 3 17.0 ou superior do Visual Studio 2022 para Mac.

) Importante

A Microsoft anunciou a desativação do Visual Studio para Mac. O Visual Studio


para Mac não terá mais suporte a partir de 31 de agosto de 2024. As alternativas
incluem:
O Visual Studio Code com o Kit de Desenvolvimento em C# e extensões
relacionadas, como .NET MAUI e Unity .
Visual Studio em execução no Windows em uma VM no Mac.
Visual Studio em execução no Windows em uma VM na Nuvem .

Para obter mais informações, confira o comunicado de desativação do Visual


Studio para Mac .

Instalar junto com o Visual Studio Code


Visual Studio Code é um editor de código-fonte leve e avançado que é executado em
sua área de trabalho. Visual Studio Code está disponível para Windows, macOS e Linux.

Embora Visual Studio Code não venha com um instalador automatizado do .NET como
o Visual Studio, adicionar suporte ao .NET é simples.

1. Baixar e instalar o Visual Studio Code .


2. Baixar e instalar o .NET SDK .
3. Instalar a extensão C# do Visual Studio Code Marketplace .

Instalar com a automação bash


Os scripts dotnet-install são usados para automação e instalações não administrativas
do runtime. Você pode baixar o script na página de referência de script dotnet-install.

O script usa como padrão a instalação da versão LTS (suporte de longo prazo) mais
recente, que é o .NET 8. Você pode escolher uma versão específica definindo a opção
channel . Inclua a opção runtime para instalar um runtime. Caso contrário, o script

instala o SDK.

O comando a seguir instala o runtime do ASP.NET Core para compatibilidade máxima. O


runtime ASP.NET Core também inclui o runtime padrão do .NET.

Bash

./dotnet-install.sh --channel 8.0 --runtime aspnetcore

Docker
Os contêineres fornecem uma maneira leve de isolar seu aplicativo do restante do
sistema host. Os contêineres no mesmo computador compartilham apenas o kernel e
usam recursos fornecidos ao seu aplicativo.

O .NET pode ser executado em um contêiner do Docker. As imagens oficiais do Docker


do .NET Core são publicadas no MCR (Registro de Contêiner da Microsoft) e podem ser
encontradas no repositório do Docker Hub do .NET Core da Microsoft . Cada
repositório contém imagens para diferentes combinações do .NET (SDK ou Runtime) e
do sistema operacional que você pode usar.

A Microsoft fornece imagens personalizadas para cenários específicos. Por exemplo, o


repositório do ASP.NET Core fornece imagens que são criadas para a execução de
aplicativos ASP.NET Core na produção.

Para obter mais informações sobre como usar o .NET em um contêiner do Docker,
confira Introdução ao .NET e docker e Exemplos .

Próximas etapas
Como verificar se o .NET já está instalado.
Trabalhando com a autenticação do macOS Catalina.
Tutorial: Introdução no macOS.
Tutorial: Criar um aplicativo com o Visual Studio Code.
Tutorial: Tutorial: Colocar um aplicativo .NET em contêiner.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o .NET no Linux
Artigo • 15/12/2023

Este artigo detalha como instalar o .NET em várias distribuições do Linux manualmente,
por meio de um gerenciador de pacotes ou de um contêiner.

Instalação manual
Você pode instalar o .NET manualmente das seguintes formas:

Instalação manual
Instalar com script

Talvez seja necessário instalar as dependências do .NET se você instalar o .NET


manualmente.

Pacotes
O .NET está disponível em arquivos de pacotes oficiais para várias distribuições do
Linux e packages.microsoft.com .

Alpine
CentOS
Debian
Fedora
openSUSE
SLES
Ubuntu

O .NET é compatível com a Microsoft quando baixado de uma fonte da Microsoft. O


suporte de melhor esforço é oferecido pela Microsoft quando baixado de outro lugar.
Se precisar, você poderá abrir problemas no dotnet/core .

Próximas etapas
Como verificar se o .NET já está instalado.
Tutorial: Criar um aplicativo com o Visual Studio Code.
Tutorial: Tutorial: Colocar um aplicativo .NET em contêiner.
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o runtime do
.NET no Ubuntu
Artigo • 24/01/2024

Este artigo descreve como instalar o .NET no Ubuntu. O repositório de pacotes da


Microsoft contém todas as versões do .NET compatíveis com o Ubuntu agora ou
anteriormente. Desde o Ubuntu 22.04, algumas versões do .NET estão disponíveis no
feed de pacotes do Ubuntu. Para saber mais sobre as versões disponíveis, confira a
seção Distribuições compatíveis.

2 Aviso

É recomendável que você escolha um único repositório para obter pacotes .NET.
Não misture pacotes .NET de vários repositórios de pacotes, pois isso leva a
problemas quando os aplicativos tentam resolver uma versão específica do .NET.

ノ Expandir a tabela

Método Vantagens Desvantagens

Gerenciador de Versões compatíveis Requer o registro do repositório de


pacotes sempre disponíveis. pacotes da Microsoft.
(Feed da Os patches estão As versões prévias não estão
Microsoft) disponíveis disponíveis.
imediatamente. Dá suporte apenas ao Ubuntu x64.
As dependências estão
incluídas.
Remoção fácil.

Gerenciador de Normalmente, a versão As versões do .NET disponíveis variam


pacotes mais recente está de acordo com a versão do Ubuntu.
(Feed do disponível. As versões prévias não estão
Ubuntu) Os patches estão disponíveis.
disponíveis Dá suporte apenas ao Ubuntu x64.
imediatamente. (Com exceção do Ubuntu 23.04+, que
As dependências estão também tem suporte para Arm64)
incluídas.
Remoção fácil.

Extração Controle onde o .NET é Instale manualmente as atualizações.


manual/via instalado. Instale manualmente as dependências.
script Remoção manual.
Método Vantagens
As versões prévias estão Desvantagens
disponíveis.

Decida como instalar o .NET


Quando sua versão do Ubuntu dá suporte ao .NET por meio do feed interno do Ubuntu,
o suporte a essas compilações do .NET é fornecido pela Canonical e elas podem ser
otimizadas para diferentes cargas de trabalho. A Microsoft dá suporte aos pacotes no
feed do próprio repositório de pacotes.

Confira as seguintes seções para determinar como instalar o .NET:

Estou usando o Ubuntu 22.04 ou posterior e só preciso do .NET


Estou usando uma versão do Ubuntu anterior à 22.04
Estou usando outros pacotes da Microsoft, como powershell, mdatp ou mssql
Desejo criar um aplicativo .NET
Desejo executar um aplicativo .NET em um cenário de contêiner, nuvem ou
integração contínua
Minha distribuição do Ubuntu não inclui a versão desejada do .NET ou preciso de
uma versão dele que não tem mais suporte
Desejo instalar uma versão prévia
Não desejo usar a APT
Estou usando uma CPU baseada em Arm

Estou usando o Ubuntu 22.04 ou posterior e só preciso


do .NET
Instale o .NET por meio do feed do Ubuntu. Para obter mais informações, consulte as
seguintes páginas:

Instalar o .NET no Ubuntu 22.04.


Instalar o .NET no Ubuntu 22.10.
Instalar o .NET no Ubuntu 23.04.
Instalar o .NET no Ubuntu 23.10.

) Importante

As versões do SDK do .NET oferecidas pela Canonical estão sempre na faixa de


recursos da .1xx. Se você quiser usar uma versão mais recente da banda de versão,
use o feed da Microsoft para instalar o SDK. Verifique se você examinou as
informações no artigo mix ups de pacote do .NET no Linux para entender as
implicações da comutação entre feeds de repositório.

Para instalar outros pacotes do repositório da Microsoft para usar outros pacotes da
Microsoft, como powershell , mdatp ou mssql , você precisará despriorizar os pacotes do
.NET fornecidos por ele. Para saber como fazer isso, confira Minha distribuição do Linux
fornece pacotes do .NET e desejo usá-los.

Estou usando uma versão do Ubuntu anterior à 22.04


Siga as instruções na página específica da versão do Ubuntu.

20.04 (LTS)
18.04 (LTS)
16.04 (LTS)

Confira a seção Distribuições compatíveis para saber quais versões do .NET são
compatíveis com sua versão do Ubuntu. Para instalar uma versão sem suporte, confira
Registrar o repositório de pacotes da Microsoft.

Estou usando outros pacotes da Microsoft, como


powershell , mdatp ou mssql

Se sua versão do Ubuntu der suporte ao .NET por meio do feed interno do Ubuntu,
decida qual feed deve instalar o .NET. A seção Distribuições compatíveis fornece uma
tabela que lista as versões do .NET disponíveis nos feeds de pacotes.

Para obter os pacotes do .NET no feed do Ubuntu, você precisará despriorizar os


pacotes do .NET fornecidos pelo repositório da Microsoft. Para saber como fazer isso,
confira Minha distribuição do Linux fornece pacotes do .NET e desejo usá-los.

Desejo criar um aplicativo .NET


Use para o SDK as mesmas origens de pacote usadas para o runtime. Por exemplo, ao
usar o Ubuntu 22.04 e o .NET 6, mas não o .NET 7, é recomendado instalar o .NET por
meio do feed interno do Ubuntu. Se, no entanto, você migrar para o .NET 7, que não é
fornecido pela Canonical para Ubuntu 22.04, desinstale o .NET e reinstale-o com o
repositório de pacotes da Microsoft. Para obter mais informações, confira Registro e
instalação com o repositório de pacotes da Microsoft. Além disso, examine as outras
sugestões na seção Decidir como instalar o .NET.
Desejo executar um aplicativo .NET em um cenário de
contêiner, nuvem ou integração contínua
Se sua versão do Ubuntu fornecer a versão do .NET necessária, instale-a por meio do
feed interno. Caso contrário, registre o repositório de pacotes da Microsoft e instale o
.NET por meio dele. Confira as informações na seção Distribuições compatíveis.

Se a versão do .NET que você deseja não estiver disponível, tente usar o script dotnet-
install.

Minha distribuição do Ubuntu não inclui a versão


desejada do .NET ou preciso de uma versão dele que não
tem mais suporte
É recomendado usar a APT e o repositório de pacotes da Microsoft. Para saber mais,
confira a seção Realizar o registro e a instalação com o repositório de pacotes da
Microsoft.

Desejo instalar uma versão prévia


Use uma das seguintes abordagens para instalar o .NET:

Instale o .NET com o script install-dotnet.


Instalar o .NET manualmente

Não desejo usar a APT


Para obter uma instalação automatizada, use o script de instalação do Linux.

Para obter controle total sobre a experiência de instalação do .NET, baixe um tarball e
instale o .NET manualmente. Para saber mais, confira Instalação manual.

Estou usando uma CPU baseada em Arm


Use uma das seguintes abordagens para instalar o .NET:

Instale o .NET com o script install-dotnet.


Instalar o .NET manualmente

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no momento e as versões
do Ubuntu compatíveis com elas. Cada link leva à página específica da versão do
Ubuntu com instruções específicas sobre como instalar o .NET nela.

ノ Expandir a tabela

Ubuntu Versões do .NET com Disponível no feed do Disponível no feed da


suporte Ubuntu Microsoft

23.10 8.0, 7.0, 6.0 8.0, 7.0, 6.0 8.0, 7.0, 6.0

23.04 8.0, 7.0, 6.0 7.0, 6.0 8.0, 7.0, 6.0

22.10 7.0, 6.0 7.0, 6.0 7.0, 6.0, 3.1

22.04 8.0, 7.0, 6.0 7.0, 6.0 8.0, 7.0, 6.0, 3.1
(LTS)

20.04 8.0, 7.0, 6.0 Nenhum 8.0, 7.0. 6.0, 5.0, 3.1, 2.1
(LTS)

18.04 7.0, 6.0 Nenhum 7.0. 6.0, 5.0, 3.1, 2.2, 2.1
(LTS)

16.04 6,0 Nenhum 6.0, 5.0, 3.1, 3.0, 2.2, 2.1, 2.0
(LTS)

Quando uma versão do Ubuntu ficar sem suporte, não haverá mais suporte dessa
versão no .NET.

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Registrar o repositório de pacotes da Microsoft


O repositório de pacotes da Microsoft contém todas as versões do .NET que eram ou
atualmente são compatíveis com sua versão do Ubuntu. Se sua versão do Ubuntu
fornecer pacotes do .NET, você precisará despriorizar os pacotes do Ubuntu e usar o
repositório da Microsoft. Para saber como fazer isso, confira Preciso de uma versão do
.NET que não é fornecida pela minha distribuição do Linux.
) Importante

As instalações do gerenciador de pacotes só são compatíveis com a arquitetura


x64. Em outras arquiteturas, como o Arm, o .NET precisa ser instalado por outros
meios, como o script do instalador ou a instalação manual.

As versões prévias não estão disponíveis no repositório de pacotes da Microsoft. Para


saber mais, confira Instalar versões prévias.

U Cuidado

É recomendado usar somente um repositório para gerenciar todas as instalações


do .NET. Se você já tiver instalado o .NET anteriormente com o repositório do
Ubuntu, será necessário limpar o sistema de pacotes do .NET e configurar a APT
para ignorar o feed do Ubuntu. Para saber como fazer isso, confira Preciso de uma
versão do .NET que não é fornecida pela minha distribuição do Linux.

A instalação com a APT pode ser feita com alguns comandos. Antes de instalar o .NET,
execute os comandos a seguir para adicionar a chave de assinatura de pacote da
Microsoft à lista de chaves confiáveis e adicionar o repositório de pacotes.

Abra um terminal e execute os seguintes comandos:

Bash

# Get Ubuntu version


declare repo_version=$(if command -v lsb_release &> /dev/null; then
lsb_release -r -s; else grep -oP '(?<=^VERSION_ID=).+' /etc/os-release | tr
-d '"'; fi)

# Download Microsoft signing key and repository


wget https://packages.microsoft.com/config/ubuntu/$repo_version/packages-
microsoft-prod.deb -O packages-microsoft-prod.deb

# Install Microsoft signing key and repository


sudo dpkg -i packages-microsoft-prod.deb

# Clean up
rm packages-microsoft-prod.deb

# Update packages
sudo apt update

 Dica
O script anterior foi escrito para o Ubuntu e poderá não funcionar se você estiver
usando uma distribuição derivada, como o Linux Mint. É provável que a variável
$repo_version não receba o valor correto, tornando o URI do comando wget

inválido. Essa variável é mapeada para a versão específica do Ubuntu para a qual
você deseja obter pacotes, como 22.10 ou 23.04.

Você pode usar um navegador da Web e navegar para


https://packages.microsoft.com/config/ubuntu/ para ver quais versões do
Ubuntu estão disponíveis para uso como o valor $repo_version .

Instalar o .NET
Depois de registrar o repositório de pacotes da Microsoft ou se sua versão do feed
padrão do Ubuntu der suporte ao pacote do .NET, será possível instalar o .NET por meio
do gerenciador de pacotes com o comando sudo apt install <package-name> . Substitua
<package-name> pelo nome do pacote do .NET que você deseja instalar. Por exemplo,

para instalar o SDK do .NET 8.0, use o comando sudo apt install dotnet-sdk-8.0 . A
seguinte tabela lista os pacotes do .NET com suporte no momento:

ノ Expandir a tabela

Produto Type Pacote

8.0 ASP.NET Core Runtime aspnetcore-runtime-8.0

8.0 .NET Runtime dotnet-runtime-8.0

8.0 .NET . dotnet-sdk-8.0

7.0 ASP.NET Core Runtime aspnetcore-runtime-7.0

7.0 .NET Runtime dotnet-runtime-7.0

7.0 .NET . dotnet-sdk-7.0

6.0 ASP.NET Core Runtime aspnetcore-runtime-6.0

6.0 .NET Runtime dotnet-runtime-6.0

6.0 .NET . dotnet-sdk-6.0

Para instalar uma versão sem suporte do .NET, verifique a seção Distribuições
compatíveis para ver se ela está disponível. Em seguida, substitua a versão do .NET que
você deseja instalar. Por exemplo, para instalar o ASP.NET Core 2.1, use o nome de
pacote aspnetcore-runtime-2.1 .

 Dica

Se você não estiver criando aplicativos .NET, instale o runtime do ASP.NET Core
porque ele inclui o runtime do .NET e também dá suporte a aplicativos ASP.NET
Core.

Algumas variáveis de ambiente afetam como o .NET é executado após a instalação. Para
saber mais, confira Variáveis de ambiente da CLI e do SDK do .NET.

Desinstalar o .NET
Se você tiver instalado o .NET por meio de um gerenciador de pacotes, desinstale-o da
mesma forma com o comando apt-get remove :

Bash

sudo apt-get remove dotnet-sdk-6.0

Para saber mais, confira Desinstalar o .NET.

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.
Usar o APT para atualizar o .NET
Se você tiver instalado o .NET por meio de um gerenciador de pacotes, atualize o
pacote com o comando apt upgrade . Por exemplo, os seguintes comandos atualizam o
pacote dotnet-sdk-7.0 com a versão mais recente:

Bash

sudo apt update


sudo apt upgrade dotnet-sdk-7.0

 Dica

Se você já atualizou a distribuição do Linux desde a instalação do .NET, talvez seja


necessário reconfigurar o repositório de pacotes da Microsoft. Execute as
instruções de instalação da versão de distribuição atual para atualizar para o
repositório de pacote apropriado para atualizações do .NET.

Solução de problemas
A partir do Ubuntu 22.04, você pode encontrar uma situação em que parece que apenas
uma parte do .NET está disponível. Por exemplo, se você tiver instalado o runtime e o
SDK, mas, ao executar dotnet --info , somente o runtime está listado. Essa situação
pode estar relacionada ao uso de duas fontes de pacote diferentes. Os feeds de pacotes
internos do Ubuntu 22.04 e do Ubuntu 22.10 incluem algumas versões do .NET, mas não
todas, e também é possível que você tenha instalado o .NET por meio dos feeds da
Microsoft. Para saber como corrigir esse problema, confira Solucionar erros do .NET
relacionados a arquivos ausentes no Linux.

Problemas de APT
Esta seção fornece informações sobre erros comuns que podem ocorrer quando o APT
é usado para instalar o .NET.

Não é possível localizar o pacote

) Importante
O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de
pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar o .NET manualmente.

Não foi possível localizar\Alguns pacotes não puderam ser


instalados

7 Observação

Essas informações só se aplicam quando o .NET é instalado no feed de pacotes da


Microsoft.

Se você receber uma mensagem de erro semelhante a Não foi possível localizar o
pacote {dotnet-package} ou Alguns pacotes não puderam ser instalados, execute os
comandos a seguir.

Há dois espaços reservados no conjunto de comandos a seguir.

{dotnet-package}

Isso representa o pacote do .NET que você está instalando, como aspnetcore-
runtime-8.0 . Isso é usado no comando sudo apt-get install a seguir.

{os-version}

Isso representa a versão de distribuição em que você está. Isso é usado no


comando wget abaixo. A versão de distribuição é o valor numérico, como 20.04
no Ubuntu ou 10 no Debian.

Primeiro, tente limpar a lista de pacotes:

Bash

sudo dpkg --purge packages-microsoft-prod && sudo dpkg -i packages-


microsoft-prod.deb
sudo apt-get update
Depois, tente instalar o .NET novamente. Se isso não funcionar, você poderá executar
uma instalação manual com os seguintes comandos:

Bash

sudo apt-get install -y gpg


wget -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor
-o microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/
wget https://packages.microsoft.com/config/ubuntu/{os-version}/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list
sudo apt-get update && \
sudo apt-get install -y {dotnet-package}

Falha na busca

Ao instalar o pacote do .NET, pode ocorrer um erro semelhante a Failed to fetch ...
File has unexpected size ... Mirror sync in progress? . Esse erro pode significar que o

feed de pacotes do .NET está sendo atualizado com versões mais recentes do pacote e
que você deve tentar novamente mais tarde. Durante uma atualização, o feed de
pacotes não deve ficar disponível por até 30 minutos. Se você continuar recebendo esse
erro por mais de 30 minutos, registre um problema em
https://github.com/dotnet/core/issues .

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. No entanto, se você instalar o .NET manualmente ou
publicar um aplicativo autossuficiente, instale as seguintes dependências para executar
o aplicativo:

libc6
libgcc1 (para 16.x e 18.x)
libgcc-s1 (para 20.x ou posterior)
libgssapi-krb5-2
libicu55 (para 16.x)
libicu60 (para 18.x)
libicu66 (para 20.x)
libicu70 (para 22.04)
libicu71 (para 22.10)
libicu72 (para 23.04)
liblttng-ust1 (para 22.x)
libssl1.0.0 (para 16.x)
libssl1.1 (para 18.x, 20.x)
libssl3 (para 22.x)
libstdc++6
libunwind8 (para 22.x)
zlib1g

As dependências podem ser instaladas por meio do comando apt install . O snippet a
seguir demonstra a instalação da biblioteca zlib1g :

Bash

sudo apt install zlib1g

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Próximas etapas
Como habilitar o preenchimento com a tecla Tab para a CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instale o .NET SDK ou .NET Runtime no
Ubuntu
Artigo • 03/04/2024

Esse artigo discute como instalar o .NET no Ubuntu.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft. Use o feed do
Ubuntu ou instale manualmente o .NET. Tenha cuidado com problemas de
confusão de pacotes ao usar vários feeds. Para obter mais informações, confira
Combinações de pacotes do .NET no Linux.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar o .NET manualmente.

As dependências podem ser instaladas por meio do comando apt install . O snippet a
seguir demonstra a instalação da biblioteca zlib1g :

Bash

sudo apt install zlib1g

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Próximas etapas
Como habilitar o preenchimento de TAB para a CLI do .NET.
Tutorial: Crie um aplicativo de console com .NET SDK usando Visual Studio Code.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o Runtime do
.NET no Alpine
Artigo • 09/01/2024

) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

O .NET tem suporte no Alpine e este artigo descreve como instalar o .NET no Alpine.
Quando uma versão do Alpine ficar sem suporte, não haverá mais suporte dessa versão
no .NET.

Se você estiver usando o Docker, considere usar imagens oficiais do Docker para .NET
em vez de instalar o .NET por conta própria.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

O gerenciador de pacotes alpine dá suporte à instalação de algumas versões do .NET.


Se o pacote .NET não estiver disponível, você precisará instalar o .NET de uma das
seguintes maneiras alternativas:

Use o script de instalação do .NET.


Baixe e instale o .NET manualmente.

Instalar o .NET 8
O .NET 8 ainda não está disponível no repositório oficial de pacotes da Alpine. Use uma
das seguintes abordagens para instalar o .NET 8:

Use o script de instalação do .NET.


Baixe e instale o .NET manualmente.
Instalar o .NET 7

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo apk add dotnet7-sdk

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo apk add aspnetcore7-runtime

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore7-runtime no comando
anterior por dotnet7-runtime :

Bash

sudo apk add dotnet7-runtime

Instalar o .NET 6

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash
sudo apk add dotnet6-sdk

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo apk add aspnetcore6-runtime

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core. Para instalá-lo, substitua aspnetcore6-runtime
no comando anterior por dotnet6-runtime :

Bash

sudo apk add dotnet6-runtime

Distribuições com suporte


A tabela a seguir é uma lista de versões .NET atualmente com suporte e as versões do
Alpine nas quais elas têm suporte. Essas versões permanecerão com suporte até que a
versão do .NET atinja o fim do suporte ou a versão do Alpine atinja o fim da vida
útil .

ノ Expandir a tabela

Alpine Versão compatível Disponível no Gerenciador de Pacotes

3,18 .NET 7.0, .NET 6.0 .NET 8.0, .NET 7.0, .NET 6.0

3,17 .NET 7.0, .NET 6.0 .NET 8.0, .NET 7.0, .NET 6.0

3.16 .NET 7.0, .NET 6.0 .NET 6.0

3,15 .NET 7.0, .NET 6.0 Nenhum

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Arquiteturas com suporte


A tabela a seguir é uma lista de versões do .NET atualmente compatíveis e a arquitetura
do Alpine em que elas têm suporte. Essas versões permanecem compatíveis até que a
versão do .NET atinja o fim do suporte ou a arquitetura do Alpine seja compatível# .
Observe que somente x86_64 , armv7 e aarch64 são compatíveis oficialmente com a
Microsoft. Outras arquiteturas são compatíveis pelos mantenedores da distribuição e
podem ser instaladas usando o gerenciador de pacotes apk .

ノ Expandir a tabela

Arquitetura .NET 6 .NET 7 .NET 8

x86_64 3.16, 3.17, 3.18 3.17, 3.18 3.17, 3.18

x86 Nenhum Nenhum Nenhum

aarch64 3.16, 3.17, 3.18 3.17, 3.18 3.17, 3.18

armv7 3.16, 3.17, 3.18 3.17, 3.18 3.17, 3.18

armhf Nenhum Nenhum Nenhum

s390x 3,17 3,17 3,17

ppc64le Nenhum Nenhum Nenhum

riscv64 Nenhum Nenhum Nenhum

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual
Remover versões prévias
Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

icu-libs
krb5-libs
libgcc
libgdiplus (se o aplicativo .NET exigir o assembly System.Drawing.Common)
libintl
libssl1.1 (para 3.14.x e versões anteriores)
libssl3 (para 3.15.x e mais recente)
libstdc++
zlib

Para instalar os requisitos necessários, execute o seguinte comando:

Bash

apk add bash icu-libs krb5-libs libgcc libintl libssl1.1 libstdc++ zlib

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Para instalar o libgdiplus no Alpine 3.16 ou posterior, execute:

Bash

apk add libgdiplus


Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o Runtime do
.NET no CentOS Linux
Artigo • 10/01/2024

O .NET tem suporte no CentOS Linux. Este artigo descreve como instalar o .NET no
CentOS Linux. Se você precisar instalar o .NET no CentOS Stream, consulte Instalar o
SDK do .NET ou o Runtime do .NET no RHEL e no CentOS Stream.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar o .NET manualmente.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no CentOS Linux 7. Essas
versões permanecem com suporte até que o suporte à versão do .NET expire ou a
distribuição do CentOS Linux não tenha mais suporte.

ノ Expandir a tabela

CentOS Linux .NET

7 7, 6
2 Aviso

O CentOS Linux 8 chegou ao EOL (Fim da Vida) em 31 de dezembro de 2021. Para


obter mais informações, consulte a página oficial do CentOS Linux EOL . Por isso,
o .NET não tem suporte no CentOS Linux 8.

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar manualmente o .NET.

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

CentOS Linux 7
Antes de instalar o .NET, execute os comandos a seguir para adicionar a chave de
assinatura de pacote da Microsoft à lista de chaves confiáveis e adicionar o repositório
de pacotes da Microsoft. Abra um terminal e execute os seguintes comandos:

Bash

sudo rpm -Uvh https://packages.microsoft.com/config/centos/7/packages-


microsoft-prod.rpm

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo yum install dotnet-sdk-7.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo yum install aspnetcore-runtime-7.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-7.0 no comando
anterior por dotnet-runtime-7.0 :

Bash
sudo yum install dotnet-runtime-7.0

Como instalar outras versões


Todas as versões do .NET estão disponíveis para download,
https://dotnet.microsoft.com/download/dotnet mas exigem a instalação manual. Você
pode tentar usar o gerenciador de pacotes para instalar uma versão diferente do .NET.
No entanto, a versão solicitada pode não estar disponível.

Os pacotes adicionados aos feeds do gerenciador de pacotes são nomeados em um


formato hackeável, por exemplo: {product}-{type}-{version} .

product
O tipo de produto .NET a ser instalado. As opções válidas são:
dotnet
aspnetcore

tipo
Escolhe o SDK ou o runtime. As opções válidas são:
SDK (disponível somente para o produto dotnet)
runtime

version
A versão do SDK ou do runtime a ser instalada. Este artigo sempre fornecerá as
instruções para a última versão com suporte. As opções válidas são qualquer
versão lançada, como:
8.0
6,0
3.1
2.1

É possível que o SDK/runtime que você está tentando baixar não esteja disponível
para sua distribuição do Linux. Para obter uma lista de distribuições com suporte,
confira Instalar o .NET no Linux.

Exemplos
Instale o runtime do ASP.NET Core 8.0: aspnetcore-runtime-8.0
Instalar o runtime do .NET Core 2.1: dotnet-runtime-2.1
Instalar o SDK do .NET 5: dotnet-sdk-5.0
Instalar o SDK do .NET Core 3.1: dotnet-sdk-3.1

Ausência de pacote
Se a combinação pacote-versão não funcionar, ela não estará disponível. Por exemplo,
não existe um SDK do ASP.NET Core, os componentes do SDK estão incluídos no SDK
do .NET. O valor aspnetcore-sdk-8.0 está incorreto e deve ser dotnet-sdk-8.0 . Para
obter uma lista de distribuições do Linux com suporte no .NET, confira Dependências e
requisitos do .NET.

Solucionar problemas do gerenciador de


pacotes
Esta seção fornece informações sobre erros comuns que podem ocorrer quando o
gerenciador de pacotes é usado para instalar o .NET.

Não é possível localizar o pacote

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar manualmente o .NET.

Falha na busca
Ao instalar o pacote do .NET, pode ocorrer um erro semelhante a signature
verification failed for file 'repomd.xml' from repository 'packages-microsoft-com-

prod' . De modo geral, esse erro significa que o feed de pacotes do .NET está sendo

atualizado com versões de pacote mais recentes e que você deve tentar novamente
mais tarde. Durante uma atualização, o feed de pacotes não fica disponível por até duas
horas. Se você receber esse erro continuamente por mais de duas horas, registre um
problema em https://github.com/dotnet/core/issues .
Erros relacionados à falta de fxr , libhostfxr.so ou
FrameworkList.xml

Para obter mais informações sobre como resolver esses problemas, consulte Solucionar
problemas fxr, libhostfxr.so e erros FrameworkList.xml.

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

krb5-libs
libicu
openssl-libs
zlib

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


você precisará instalar compat-openssl10 .

As dependências podem ser instaladas com o comando yum install . O snippet a seguir
demonstra a instalação da biblioteca libicu :

Bash

sudo yum install libicu

Para obter mais informações sobre as dependências, confira Aplicativos autossuficientes


do Linux .

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o Runtime do
.NET no RHEL e no CentOS Stream
Artigo • 11/12/2023

) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

O .NET tem suporte no RHEL (Red Hat Enterprise Linux). Este artigo descreve como
instalar o .NET no RHEL e no CentOS Stream.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

Registrar a sua assinatura do Red Hat


Para instalar o .NET do Red Hat no RHEL, é necessário se registrar usando o Gerenciador
de Assinaturas do Red Hat. Se isso não tiver sido feito em seu sistema ou se você não
tiver certeza, consulte a Documentação do produto Red Hat para .NET .

) Importante

Isso não se aplica ao CentOS Stream.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no RHEL e no CentOS
Stream. Essas versões permanecem com suporte até que o suporte à versão do .NET
expire ou a distribuição do Linux não seja mais compatível.
ノ Expandir a tabela

Distribuição .NET

RHEL 9 (9.1) 8, 7, 6

RHEL 8 (8.7) 8, 7, 6

RHEL 7 6

CentOS Stream 9 8, 7, 6

CentOS Stream 8 8, 7, 6

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

RHEL 9
O .NET está incluído nos repositórios do AppStream para o RHEL 9.

) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo dnf install dotnet-runtime-8.0

RHEL 8
O .NET está incluído nos repositórios do AppStream para RHEL 8.
) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo dnf install dotnet-runtime-8.0

RHEL 7 ❌ .NET 8
O .NET 8 não é compatível com o RHEL 7 e não funciona.

RHEL 7 ❌ .NET 7
O .NET 7 não tem suporte oficial do RHEL 7. Para instalar o .NET 7, confira Instalar o
.NET no Linux usando um script de instalação ou extraindo binários.

RHEL 7 ✔️.NET 6
O comando a seguir instala o pacote scl-utils :

Bash

sudo yum install scl-utils

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute os comandos a seguir:

Bash

subscription-manager repos --enable=rhel-7-server-dotnet-rpms


yum install rh-dotnet60 -y
scl enable rh-dotnet60 bash

O Red Hat não recomenda a habilitação permanente de rh-dotnet60 porque pode


afetar outros programas. Para habilitar rh-dotnet permanentemente, adicione a linha a
seguir ao arquivo ~/.bashrc.

Bash

source scl_source enable rh-dotnet60

Instalar o runtime
O Runtime do .NET permite executar aplicativos feitos com o .NET que não incluíram o
runtime. Os comandos abaixo instalam o Runtime do ASP.NET Core, que é o runtime
mais compatível para .NET Core. Em seu terminal, execute os seguintes comandos.

Bash

subscription-manager repos --enable=rhel-7-server-dotnet-rpms


yum install rh-dotnet60-aspnetcore-runtime-6.0 -y
scl enable rh-dotnet60 bash

O Red Hat não recomenda a habilitação permanente de rh-dotnet60 porque pode


afetar outros programas. Para habilitar rh-dotnet60 permanentemente, adicione a linha
a seguir ao arquivo ~/.bashrc.

Bash

source scl_source enable rh-dotnet60

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua rh-dotnet60-aspnetcore-runtime-6.0
no comando anterior por rh-dotnet60-dotnet-runtime-6.0 .

CentOS Stream 9 ✔️
O .NET está incluído nos repositórios do AppStream para o CentOS Stream 9.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo dnf install dotnet-runtime-8.0

CentOS Stream 8 ✔️
Use o repositório da Microsoft para instalar o .NET:

Bash

sudo rpm -Uvh https://packages.microsoft.com/config/centos/8/packages-


microsoft-prod.rpm
sudo yum install dotnet-sdk-8.0

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

krb5-libs
libicu
openssl-libs
zlib

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


você precisará instalar compat-openssl10 .

As dependências podem ser instaladas com o comando yum install . O snippet a seguir
demonstra a instalação da biblioteca libicu :

Bash

sudo yum install libicu

Para obter mais informações sobre as dependências, confira Aplicativos autossuficientes


do Linux .
Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também
precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Como instalar outras versões


Consulte a documentação do Red Hat para .NET sobre as etapas necessárias para
instalar outras versões do .NET.

Solucionar problemas do gerenciador de


pacotes
Esta seção fornece informações sobre erros comuns que podem ocorrer ao usar o
gerenciador de pacotes para instalar o .NET ou o .NET Core.

Erros relacionados à falta de fxr , libhostfxr.so ou


FrameworkList.xml

Para obter mais informações sobre como resolver esses problemas, consulte Solucionar
problemas fxr, libhostfxr.so e erros FrameworkList.xml.

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
 Fornecer comentários sobre o
guia para colaboradores.
produto
Instalar o SDK do .NET ou o runtime do
.NET no Debian
Artigo • 03/04/2024

Este artigo descreve como instalar o .NET no Debian. Quando uma versão do Debian
ficar sem suporte, não haverá mais suporte dessa versão no .NET. No entanto, essas
instruções poderão ajudar você a executar o .NET nessas versões, mesmo sem o
suporte.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar o .NET manualmente.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no momento e as versões
do Debian nas quais há suporte. Essas versões permanecerão com suporte até que a
versão do .NET atinja o fim do suporte ou que a versão do Debian atinja o fim da vida
útil .

ノ Expandir a tabela
Debian .NET

12 8, 7, 6

11 8, 7, 6

10 7, 6

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

Debian 12
A instalação com a APT pode ser feita com alguns comandos. Antes de instalar o .NET,
execute os comandos a seguir para adicionar a chave de assinatura de pacote da
Microsoft à lista de chaves confiáveis e adicionar o repositório de pacotes.

Abra um terminal e execute os seguintes comandos:


Bash

wget https://packages.microsoft.com/config/debian/12/packages-microsoft-
prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute os seguintes comandos:

Bash

sudo apt-get update && \


sudo apt-get install -y dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. Em seu terminal, execute os seguintes comandos:

Bash

sudo apt-get update && \


sudo apt-get install -y aspnetcore-runtime-8.0

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo apt-get install -y dotnet-runtime-8.0

Debian 11
A instalação com a APT pode ser feita com alguns comandos. Antes de instalar o .NET,
execute os comandos a seguir para adicionar a chave de assinatura de pacote da
Microsoft à lista de chaves confiáveis e adicionar o repositório de pacotes.

Abra um terminal e execute os seguintes comandos:

Bash

wget https://packages.microsoft.com/config/debian/11/packages-microsoft-
prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute os seguintes comandos:

Bash

sudo apt-get update && \


sudo apt-get install -y dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. Em seu terminal, execute os seguintes comandos:

Bash

sudo apt-get update && \


sudo apt-get install -y aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo apt-get install -y dotnet-runtime-8.0

Debian 10
A instalação com a APT pode ser feita com alguns comandos. Antes de instalar o .NET,
execute os comandos a seguir para adicionar a chave de assinatura de pacote da
Microsoft à lista de chaves confiáveis e adicionar o repositório de pacotes.

Abra um terminal e execute os seguintes comandos:

Bash

wget https://packages.microsoft.com/config/debian/10/packages-microsoft-
prod.deb -O packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb
rm packages-microsoft-prod.deb

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute os seguintes comandos:

Bash

sudo apt-get update && \


sudo apt-get install -y dotnet-sdk-7.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. Em seu terminal, execute os seguintes comandos:

Bash

sudo apt-get update && \


sudo apt-get install -y aspnetcore-runtime-7.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-7.0 no comando
anterior por dotnet-runtime-7.0 :

Bash

sudo apt-get install -y dotnet-runtime-7.0


Como instalar outras versões
Todas as versões do .NET estão disponíveis para download,
https://dotnet.microsoft.com/download/dotnet mas exigem a instalação manual. Você
pode tentar usar o gerenciador de pacotes para instalar uma versão diferente do .NET.
No entanto, a versão solicitada pode não estar disponível.

Os pacotes adicionados aos feeds do gerenciador de pacotes são nomeados em um


formato hackeável, por exemplo: {product}-{type}-{version} .

product
O tipo de produto .NET a ser instalado. As opções válidas são:
dotnet

aspnetcore

tipo
Escolhe o SDK ou o runtime. As opções válidas são:
sdk (disponível apenas para o produto dotnet)

runtime

version
A versão do SDK ou do runtime a ser instalada. As opções válidas são qualquer
versão lançada, como:
8.0

6.0
3.1

2.1

É possível que o SDK/tempo de execução que você está tentando baixar não esteja
disponível para sua distribuição Linux. Para obter uma lista de distribuições com
suporte, confira Instalar o .NET no Linux.

Exemplos
Instale o runtime do ASP.NET Core 8.0: aspnetcore-runtime-8.0
Instalar o runtime do .NET Core 2.1: dotnet-runtime-2.1
Instalar o SDK do .NET 5: dotnet-sdk-5.0
Instalar o SDK do .NET Core 3.1: dotnet-sdk-3.1

7 Observação
Alguns pacotes podem não estar disponíveis em sua distribuição Linux.

Ausência de pacote
Se a combinação pacote-versão não funcionar, ela não estará disponível. Por exemplo,
não existe um SDK do ASP.NET Core. Os componentes do SDK para ASP.NET Core estão
incluídos no SDK do .NET. O valor aspnetcore-sdk-8.0 está incorreto e deve ser dotnet-
sdk-8.0 . Para obter uma lista de distribuições do Linux com suporte no .NET, confira

Dependências e requisitos do .NET.

Usar o APT para atualizar o .NET


Quando uma nova versão de patch estiver disponível para .NET, você poderá
simplesmente atualizá-lo por meio do APT com os seguintes comandos:

Bash

sudo apt-get update


sudo apt-get upgrade

Se você já atualizou a distribuição do Linux desde a instalação do .NET, talvez seja


necessário reconfigurar o repositório de pacotes da Microsoft. Execute as instruções de
instalação da versão de distribuição atual para atualizar para o repositório de pacote
apropriado para atualizações do .NET.

Solução de problemas
Esta seção fornece informações sobre erros comuns que podem ocorrer quando o APT
é usado para instalar o .NET.

Não é possível localizar o pacote

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.
Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar o .NET manualmente.

Não foi possível localizar\Alguns pacotes não puderam


ser instalados
Se você receber uma mensagem de erro semelhante a Não foi possível localizar o
pacote {dotnet-package} ou Alguns pacotes não puderam ser instalados, execute os
comandos a seguir.

Há dois espaços reservados no conjunto de comandos a seguir.

{dotnet-package}

Isso representa o pacote do .NET que você está instalando, como aspnetcore-
runtime-8.0 . Isso é usado no comando sudo apt-get install a seguir.

Primeiro, tente limpar a lista de pacotes:

Bash

sudo dpkg --purge packages-microsoft-prod && sudo dpkg -i packages-


microsoft-prod.deb
sudo apt-get update

Depois, tente instalar o .NET novamente. Se isso não funcionar, você poderá executar
uma instalação manual com os seguintes comandos:

Se você estiver usando o Debian 12 ou posterior, tente os seguintes comandos:

Bash

# Define the OS version, name, and codename


source /etc/os-release

# Download the Microsoft keys


sudo apt-get install -y gpg wget
wget https://packages.microsoft.com/keys/microsoft.asc
cat microsoft.asc | gpg --dearmor -o microsoft.asc.gpg

# Add the Microsoft repository to the system's sources list


wget https://packages.microsoft.com/config/$ID/$VERSION_ID/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list

# Move the key to the appropriate place


sudo mv microsoft.asc.gpg $(cat /etc/apt/sources.list.d/microsoft-prod.list
| grep -oP "(?<=signed-by=).*(?=\])")

# Update packages and install .NET


sudo apt-get update && \
sudo apt-get install -y {dotnet-package}

Se você estiver usando uma versão Debian anterior à 12, tente os seguintes comandos:

Bash

# Define the OS version, name, and codename


source /etc/os-release

# Download the Microsoft keys


sudo apt-get install -y gpg wget
wget https://packages.microsoft.com/keys/microsoft.asc
cat microsoft.asc | gpg --dearmor -o microsoft.asc.gpg
sudo mv microsoft.asc.gpg /etc/apt/trusted.gpg.d/

# Add the Microsoft repository to the system's sources list


wget https://packages.microsoft.com/config/$ID/$VERSION_ID/prod.list
sudo mv prod.list /etc/apt/sources.list.d/microsoft-prod.list

# Set ownership
sudo chown root:root /etc/apt/trusted.gpg.d/microsoft.asc.gpg
sudo chown root:root /etc/apt/sources.list.d/microsoft-prod.list

# Update packages and install .NET


sudo apt-get update && \
sudo apt-get install -y {dotnet-package}

Falha na busca
Ao instalar o pacote do .NET, pode ocorrer um erro semelhante a Failed to fetch ...
File has unexpected size ... Mirror sync in progress? . Esse erro pode significar que o

feed de pacotes do .NET está sendo atualizado com versões mais recentes do pacote e
que você deve tentar novamente mais tarde. Durante uma atualização, o feed de
pacotes não deve ficar disponível por até 30 minutos. Se você continuar recebendo esse
erro por mais de 30 minutos, registre um problema em
https://github.com/dotnet/core/issues .

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

libc6
libgcc1 (para 10.x)
libgcc-s1 (para 11.x e 12.x)
libgssapi-krb5-2
libicu63 (para 10.x)
libicu67 (para 11.x)
libicu72 (para 12.x)
libssl1.1
libstdc++6
zlib1g

As dependências podem ser instaladas por meio do comando apt install . O snippet a
seguir demonstra a instalação da biblioteca libc6 :

Bash

sudo apt install libc6

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais
documentação
informações, confira o nosso
guia para colaboradores.
 Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o Runtime do
.NET no Fedora
Artigo • 14/11/2023

O .NET tem suporte no Fedora e este artigo descreve como instalar o .NET no Fedora.
Quando uma versão do Fedora fica sem suporte, o .NET deixa de ter suporte naquela
versão.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Instalar o SDK do .NET ou o Runtime do .NET com um script.


Instalar o SDK do .NET ou o Runtime do .NET manualmente.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no momento e as versões
do Fedora nas quais há suporte. Essas versões permanecerão com suporte até que a
versão do .NET atinja o fim do suporte ou que a versão do Fedora atinja o fim da vida
útil .

ノ Expand table

Fedora .NET

39 8, 7, 6

38 8, 7, 6

37 8, 7, 6

Não ❌ há mais suporte para as seguintes versões do .NET:


.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Instalar o .NET 8

) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash
sudo dnf install dotnet-runtime-8.0

Instalar o .NET 7

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-7.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-7.0

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-7.0 no comando
anterior por dotnet-runtime-7.0 :

Bash

sudo dnf install dotnet-runtime-7.0

Instalar o .NET 6

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-6.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-6.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-6.0 no comando
anterior por dotnet-runtime-6.0 :

Bash

sudo dnf install dotnet-runtime-6.0

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

krb5-libs
libicu
openssl-libs
zlib

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


você precisará instalar compat-openssl10 .

As dependências podem ser instaladas com o comando yum install . O snippet a seguir
demonstra a instalação da biblioteca libicu :

Bash

sudo yum install libicu

Para obter mais informações sobre as dependências, confira Aplicativos autossuficientes


do Linux .

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Instalar em distribuições mais antigas


As versões mais antigas do Fedora não contêm o .NET Core nos repositórios de pacote
padrão. É possível instalar o .NET com o script dotnet-install.sh ou por meio do
repositório da Microsoft:
1. Primeiro, adicione a chave de assinatura da Microsoft à lista de chaves confiáveis.

Bash

sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc

2. Em seguida, adicione o repositório de pacotes da Microsoft. A origem do


repositório se baseia na sua versão do Fedora.

ノ Expand table

Versão do Fedora Repositório de pacotes

36 https://packages.microsoft.com/config/fedora/36/prod.repo

35 https://packages.microsoft.com/config/fedora/35/prod.repo

34 https://packages.microsoft.com/config/fedora/34/prod.repo

33 https://packages.microsoft.com/config/fedora/33/prod.repo

32 https://packages.microsoft.com/config/fedora/32/prod.repo

31 https://packages.microsoft.com/config/fedora/31/prod.repo

30 https://packages.microsoft.com/config/fedora/30/prod.repo

29 https://packages.microsoft.com/config/fedora/29/prod.repo

28 https://packages.microsoft.com/config/fedora/28/prod.repo

27 https://packages.microsoft.com/config/fedora/27/prod.repo

Bash

sudo wget -O /etc/yum.repos.d/microsoft-prod.repo


https://packages.microsoft.com/config/fedora/31/prod.repo

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash
sudo dnf install dotnet-sdk-7.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-7.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-7.0 no comando
anterior por dotnet-runtime-7.0 :

Bash

sudo dnf install dotnet-runtime-7.0

Como instalar outras versões


Todas as versões do .NET estão disponíveis para download,
https://dotnet.microsoft.com/download/dotnet mas exigem a instalação manual. Você
pode tentar usar o gerenciador de pacotes para instalar uma versão diferente do .NET.
No entanto, a versão solicitada pode não estar disponível.

Os pacotes adicionados aos feeds do gerenciador de pacotes são nomeados em um


formato hackeável, por exemplo: {product}-{type}-{version} .

product
O tipo de produto .NET a ser instalado. As opções válidas são:
dotnet
aspnetcore

tipo
Escolhe o SDK ou o runtime. As opções válidas são:
SDK (disponível somente para o produto dotnet)
runtime
version
A versão do SDK ou do runtime a ser instalada. Este artigo sempre fornecerá as
instruções para a última versão com suporte. As opções válidas são qualquer
versão lançada, como:
8.0
6,0
3.1
2.1

É possível que o SDK/runtime que você está tentando baixar não esteja disponível
para sua distribuição do Linux. Para obter uma lista de distribuições com suporte,
confira Instalar o .NET no Linux.

Exemplos
Instale o runtime do ASP.NET Core 8.0: aspnetcore-runtime-8.0
Instalar o runtime do .NET Core 2.1: dotnet-runtime-2.1
Instalar o SDK do .NET 5: dotnet-sdk-5.0
Instalar o SDK do .NET Core 3.1: dotnet-sdk-3.1

Ausência de pacote
Se a combinação pacote-versão não funcionar, ela não estará disponível. Por exemplo,
não existe um SDK do ASP.NET Core, os componentes do SDK estão incluídos no SDK
do .NET. O valor aspnetcore-sdk-8.0 está incorreto e deve ser dotnet-sdk-8.0 . Para
obter uma lista de distribuições do Linux com suporte no .NET, confira Dependências e
requisitos do .NET.

Solucionar problemas do gerenciador de


pacotes
Esta seção fornece informações sobre erros comuns que podem ocorrer ao usar o
gerenciador de pacotes para instalar o .NET ou o .NET Core.

Não é possível localizar o pacote


Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Instalar o SDK do .NET ou o Runtime do .NET com um script.


Instalar o SDK do .NET ou o Runtime do .NET manualmente.

Falha na busca
Ao instalar o pacote do .NET, pode ocorrer um erro semelhante a signature
verification failed for file 'repomd.xml' from repository 'packages-microsoft-com-
prod' . De modo geral, esse erro significa que o feed de pacotes do .NET está sendo

atualizado com versões de pacote mais recentes e que você deve tentar novamente
mais tarde. Durante uma atualização, o feed de pacotes não fica disponível por até duas
horas. Se você receber esse erro continuamente por mais de duas horas, registre um
problema em https://github.com/dotnet/core/issues .

Erros relacionados à falta de fxr , libhostfxr.so ,


FrameworkList.xml ou /usr/share/dotnet

Para obter mais informações sobre como resolver esses problemas, consulte Solucionar
problemas fxr, libhostfxr.so e erros FrameworkList.xml.

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o runtime do
.NET no openSUSE
Artigo • 10/01/2024

Há suporte para o .NET no openSUSE. Este artigo descreve como instalar o .NET no
openSUSE.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar o .NET manualmente.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no openSUSE 15. Essas
versões permanecem com suporte até que a versão do .NET atinja o fim do suporte
ou não haja mais suporte para a versão do openSUSE.

ノ Expandir a tabela

openSUSE .NET

15.4+ 8, 7, 6
Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

openSUSE 15
Antes de instalar o .NET, execute os comandos a seguir para adicionar a chave de
assinatura de pacote da Microsoft à lista de chaves confiáveis e adicionar o repositório
de pacotes da Microsoft. Abra um terminal e execute os seguintes comandos:

Bash

sudo zypper install libicu


sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc
wget https://packages.microsoft.com/config/opensuse/15/prod.repo
sudo mv prod.repo /etc/zypp/repos.d/microsoft-prod.repo
sudo chown root:root /etc/zypp/repos.d/microsoft-prod.repo
Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo zypper install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo zypper install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo zypper install dotnet-runtime-8.0

Como instalar outras versões


Todas as versões do .NET estão disponíveis para download,
https://dotnet.microsoft.com/download/dotnet mas exigem a instalação manual. Você
pode tentar usar o gerenciador de pacotes para instalar uma versão diferente do .NET.
No entanto, a versão solicitada pode não estar disponível.

Os pacotes adicionados aos feeds do gerenciador de pacotes são nomeados em um


formato hackeável, por exemplo: {product}-{type}-{version} .

product
O tipo de produto .NET a ser instalado. As opções válidas são:
dotnet
aspnetcore

tipo
Escolhe o SDK ou o runtime. As opções válidas são:
SDK (disponível somente para o produto dotnet)
runtime

version
A versão do SDK ou do runtime a ser instalada. Este artigo sempre fornecerá as
instruções para a última versão com suporte. As opções válidas são qualquer
versão lançada, como:
8.0
6,0
3.1
2.1

É possível que o SDK/runtime que você está tentando baixar não esteja disponível
para sua distribuição do Linux. Para obter uma lista de distribuições com suporte,
confira Instalar o .NET no Linux.

Exemplos
Instale o runtime do ASP.NET Core 8.0: aspnetcore-runtime-8.0
Instalar o runtime do .NET Core 2.1: dotnet-runtime-2.1
Instalar o SDK do .NET 5: dotnet-sdk-5.0
Instalar o SDK do .NET Core 3.1: dotnet-sdk-3.1

Ausência de pacote
Se a combinação pacote-versão não funcionar, ela não estará disponível. Por exemplo,
não existe um SDK do ASP.NET Core, os componentes do SDK estão incluídos no SDK
do .NET. O valor aspnetcore-sdk-8.0 está incorreto e deve ser dotnet-sdk-8.0 . Para
obter uma lista de distribuições do Linux com suporte no .NET, confira Dependências e
requisitos do .NET.

Solucionar problemas do gerenciador de


pacotes
Esta seção fornece informações sobre erros comuns que podem ocorrer quando o
gerenciador de pacotes é usado para instalar o .NET.
Não é possível localizar o pacote

) Importante

O uso de um gerenciador de pacotes para instalar o .NET por meio do feed de


pacote da Microsoft só dá suporte à arquitetura x64. Outras arquiteturas, como o
Arm, não são compatíveis com o feed do pacote da Microsoft.

Para obter mais informações de como instalar o .NET sem um gerenciador de pacotes,
confira um dos seguintes artigos:

Use o script install-dotnet para instalar o .NET.


Instalar manualmente o .NET.

Falha na busca
Ao instalar o pacote do .NET, pode ocorrer um erro semelhante a signature
verification failed for file 'repomd.xml' from repository 'packages-microsoft-com-
prod' . De modo geral, esse erro significa que o feed de pacotes do .NET está sendo

atualizado com versões de pacote mais recentes e que você deve tentar novamente
mais tarde. Durante uma atualização, o feed de pacotes não fica disponível por até duas
horas. Se você receber esse erro continuamente por mais de duas horas, registre um
problema em https://github.com/dotnet/core/issues .

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

krb5
libicu
libopenssl1_0_0

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


você precisará instalar compat-openssl10 .

As dependências podem ser instaladas com o comando zypper install . O snippet a


seguir demonstra a instalação da biblioteca krb5 :
Bash

sudo zypper install krb5

Para obter mais informações sobre as dependências, confira Aplicativos autossuficientes


do Linux .

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o SDK do .NET ou o Runtime do
.NET no RHEL e no CentOS Stream
Artigo • 11/12/2023

) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

O .NET tem suporte no RHEL (Red Hat Enterprise Linux). Este artigo descreve como
instalar o .NET no RHEL e no CentOS Stream.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

Registrar a sua assinatura do Red Hat


Para instalar o .NET do Red Hat no RHEL, é necessário se registrar usando o Gerenciador
de Assinaturas do Red Hat. Se isso não tiver sido feito em seu sistema ou se você não
tiver certeza, consulte a Documentação do produto Red Hat para .NET .

) Importante

Isso não se aplica ao CentOS Stream.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no RHEL e no CentOS
Stream. Essas versões permanecem com suporte até que o suporte à versão do .NET
expire ou a distribuição do Linux não seja mais compatível.
ノ Expandir a tabela

Distribuição .NET

RHEL 9 (9.1) 8, 7, 6

RHEL 8 (8.7) 8, 7, 6

RHEL 7 6

CentOS Stream 9 8, 7, 6

CentOS Stream 8 8, 7, 6

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

RHEL 9
O .NET está incluído nos repositórios do AppStream para o RHEL 9.

) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo dnf install dotnet-runtime-8.0

RHEL 8
O .NET está incluído nos repositórios do AppStream para RHEL 8.
) Importante

O .NET 8 foi lançado em 14 de novembro de 2023. Os pacotes podem levar algum


tempo para serem exibidos nos feeds do gerenciador de pacotes.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo dnf install dotnet-runtime-8.0

RHEL 7 ❌ .NET 8
O .NET 8 não é compatível com o RHEL 7 e não funciona.

RHEL 7 ❌ .NET 7
O .NET 7 não tem suporte oficial do RHEL 7. Para instalar o .NET 7, confira Instalar o
.NET no Linux usando um script de instalação ou extraindo binários.

RHEL 7 ✔️.NET 6
O comando a seguir instala o pacote scl-utils :

Bash

sudo yum install scl-utils

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute os comandos a seguir:

Bash

subscription-manager repos --enable=rhel-7-server-dotnet-rpms


yum install rh-dotnet60 -y
scl enable rh-dotnet60 bash

O Red Hat não recomenda a habilitação permanente de rh-dotnet60 porque pode


afetar outros programas. Para habilitar rh-dotnet permanentemente, adicione a linha a
seguir ao arquivo ~/.bashrc.

Bash

source scl_source enable rh-dotnet60

Instalar o runtime
O Runtime do .NET permite executar aplicativos feitos com o .NET que não incluíram o
runtime. Os comandos abaixo instalam o Runtime do ASP.NET Core, que é o runtime
mais compatível para .NET Core. Em seu terminal, execute os seguintes comandos.

Bash

subscription-manager repos --enable=rhel-7-server-dotnet-rpms


yum install rh-dotnet60-aspnetcore-runtime-6.0 -y
scl enable rh-dotnet60 bash

O Red Hat não recomenda a habilitação permanente de rh-dotnet60 porque pode


afetar outros programas. Para habilitar rh-dotnet60 permanentemente, adicione a linha
a seguir ao arquivo ~/.bashrc.

Bash

source scl_source enable rh-dotnet60

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua rh-dotnet60-aspnetcore-runtime-6.0
no comando anterior por rh-dotnet60-dotnet-runtime-6.0 .

CentOS Stream 9 ✔️
O .NET está incluído nos repositórios do AppStream para o CentOS Stream 9.

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:

Bash

sudo dnf install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo dnf install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo dnf install dotnet-runtime-8.0

CentOS Stream 8 ✔️
Use o repositório da Microsoft para instalar o .NET:

Bash

sudo rpm -Uvh https://packages.microsoft.com/config/centos/8/packages-


microsoft-prod.rpm
sudo yum install dotnet-sdk-8.0

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

krb5-libs
libicu
openssl-libs
zlib

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


você precisará instalar compat-openssl10 .

As dependências podem ser instaladas com o comando yum install . O snippet a seguir
demonstra a instalação da biblioteca libicu :

Bash

sudo yum install libicu

Para obter mais informações sobre as dependências, confira Aplicativos autossuficientes


do Linux .
Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também
precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Você pode instalar uma versão recente de libgdiplus ao adicionar o repositório do Mono
ao sistema .

Como instalar outras versões


Consulte a documentação do Red Hat para .NET sobre as etapas necessárias para
instalar outras versões do .NET.

Solucionar problemas do gerenciador de


pacotes
Esta seção fornece informações sobre erros comuns que podem ocorrer ao usar o
gerenciador de pacotes para instalar o .NET ou o .NET Core.

Erros relacionados à falta de fxr , libhostfxr.so ou


FrameworkList.xml

Para obter mais informações sobre como resolver esses problemas, consulte Solucionar
problemas fxr, libhostfxr.so e erros FrameworkList.xml.

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
 Fornecer comentários sobre o
guia para colaboradores.
produto
Instalar o SDK do .NET ou o runtime do
.NET no SLES
Artigo • 14/11/2023

O .NET é compatível com o SLES. Este artigo descreve como instalar o .NET no SLES.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

Distribuições com suporte


A tabela a seguir é uma lista de versões do .NET com suporte no SLES 12 SP2 e no SLES
15. Essas versões permanecem com suporte até que o suporte à versão do .NET
expire ou a distribuição do SLES não tenha mais suporte.

ノ Expandir a tabela

SLES .NET

15 8, 7, 6

12 SP5 8, 7, 6

Não ❌ há mais suporte para as seguintes versões do .NET:

.NET 5
.NET Core 3.1
.NET Core 3.0
.NET Core 2.2
.NET Core 2.1
.NET Core 2.0

Instalar versões prévias


As versões prévias e Release Candidate do .NET não estão disponíveis em repositórios
de pacotes. É possível instalar versões prévias e Release Candidate do .NET das
seguintes maneiras:

Instalação por script com install-dotnet.sh


Extração binária manual

Remover versões prévias


Ao usar um gerenciador de pacotes para gerenciar a instalação do .NET, pode ocorrer
um conflito quando já existe uma versão prévia instalada. O gerenciador de pacotes
pode interpretar a versão que não é prévia como uma versão anterior do .NET. Para
instalar a versão que não é prévia, desinstale as versões prévias. Para obter mais
informações de como desinstalar o .NET, confira Como remover o SDK e o runtime do
.NET.

SLES 15
Antes de instalar o .NET, execute os comandos a seguir para adicionar a chave de
assinatura de pacote da Microsoft à lista de chaves confiáveis e adicionar o repositório
de pacotes da Microsoft. Abra um terminal e execute os seguintes comandos:

Bash

sudo rpm -Uvh https://packages.microsoft.com/config/sles/15/packages-


microsoft-prod.rpm

Atualmente, o pacote de instalação do repositório Microsoft SLES 15 instala o arquivo


microsoft-prod.repo no diretório errado, impedindo que o zypper localize os pacotes do
.NET. Para corrigir esse problema, crie um link simbólico (symlink) no diretório correto.

Bash

sudo ln -s /etc/yum.repos.d/microsoft-prod.repo /etc/zypp/repos.d/microsoft-


prod.repo

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:
Bash

sudo zypper install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo zypper install aspnetcore-runtime-8.0

Como alternativa ao runtime do ASP.NET Core, você pode instalar o runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo zypper install dotnet-runtime-8.0

SLES 12
O .NET requer o SP2 no mínimo para a família SLES 12.

Antes de instalar o .NET, execute os comandos a seguir para adicionar a chave de


assinatura de pacote da Microsoft à lista de chaves confiáveis e adicionar o repositório
de pacotes da Microsoft. Abra um terminal e execute os seguintes comandos:

Bash

sudo rpm -Uvh https://packages.microsoft.com/config/sles/12/packages-


microsoft-prod.rpm

Instalar o SDK
O SDK do .NET permite que você desenvolva aplicativos com o .NET. Se você instalar o
SDK do .NET, não será necessário instalar o runtime correspondente. Para instalar o SDK
do .NET, execute o seguinte comando:
Bash

sudo zypper install dotnet-sdk-8.0

Instalar o runtime
O Runtime do ASP.NET Core permite executar aplicativos feitos com o .NET que não
forneceram o runtime. O comando a seguir instala o runtime do ASP.NET Core, que é o
runtime mais compatível com o .NET. No terminal, execute o seguinte comando:

Bash

sudo zypper install aspnetcore-runtime-8.0

Como alternativa ao Runtime do ASP.NET Core, você pode instalar o Runtime do .NET,
que não inclui suporte ao ASP.NET Core: substitua aspnetcore-runtime-8.0 no comando
anterior por dotnet-runtime-8.0 :

Bash

sudo zypper install dotnet-runtime-8.0

Como instalar outras versões


Todas as versões do .NET estão disponíveis para download,
https://dotnet.microsoft.com/download/dotnet mas exigem a instalação manual. Você
pode tentar usar o gerenciador de pacotes para instalar uma versão diferente do .NET.
No entanto, a versão solicitada pode não estar disponível.

Os pacotes adicionados aos feeds do gerenciador de pacotes são nomeados em um


formato hackeável, por exemplo: {product}-{type}-{version} .

product
O tipo de produto .NET a ser instalado. As opções válidas são:
dotnet
aspnetcore

tipo
Escolhe o SDK ou o runtime. As opções válidas são:
SDK (disponível somente para o produto dotnet)
runtime
version
A versão do SDK ou do runtime a ser instalada. Este artigo sempre fornecerá as
instruções para a última versão com suporte. As opções válidas são qualquer
versão lançada, como:
8.0
6,0
3.1
2.1

É possível que o SDK/runtime que você está tentando baixar não esteja disponível
para sua distribuição do Linux. Para obter uma lista de distribuições com suporte,
confira Instalar o .NET no Linux.

Exemplos
Instale o runtime do ASP.NET Core 8.0: aspnetcore-runtime-8.0
Instalar o runtime do .NET Core 2.1: dotnet-runtime-2.1
Instalar o SDK do .NET 5: dotnet-sdk-5.0
Instalar o SDK do .NET Core 3.1: dotnet-sdk-3.1

Ausência de pacote
Se a combinação pacote-versão não funcionar, ela não estará disponível. Por exemplo,
não existe um SDK do ASP.NET Core, os componentes do SDK estão incluídos no SDK
do .NET. O valor aspnetcore-sdk-8.0 está incorreto e deve ser dotnet-sdk-8.0 . Para
obter uma lista de distribuições do Linux com suporte no .NET, confira Dependências e
requisitos do .NET.

Solucionar problemas do gerenciador de


pacotes
Esta seção fornece informações sobre erros comuns que podem ocorrer quando o
gerenciador de pacotes é usado para instalar o .NET.

Falha na busca
Ao instalar o pacote do .NET, pode ocorrer um erro semelhante a signature
verification failed for file 'repomd.xml' from repository 'packages-microsoft-com-

prod' . De modo geral, esse erro significa que o feed de pacotes do .NET está sendo
atualizado com versões de pacote mais recentes e que você deve tentar novamente
mais tarde. Durante uma atualização, o feed de pacotes não fica disponível por até duas
horas. Se você receber esse erro continuamente por mais de duas horas, registre um
problema em https://github.com/dotnet/core/issues .

Dependências
Quando você faz a instalação com um gerenciador de pacotes, essas bibliotecas são
instaladas automaticamente. Porém, se você instalar o .NET manualmente ou publicar
um aplicativo autossuficiente, será necessário verificar se estas bibliotecas estão
instaladas:

krb5
libicu
libopenssl1_1

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


você precisará instalar compat-openssl10 .

As dependências podem ser instaladas com o comando zypper install . O snippet a


seguir demonstra a instalação da biblioteca krb5 :

Bash

sudo zypper install krb5

Para obter mais informações sobre as dependências, confira Aplicativos autossuficientes


do Linux .

Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também


precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Instalar o .NET no Linux usando um
script de instalação ou extraindo
binários
Artigo • 02/01/2024

Este artigo demonstra como instalar o SDK ou o Runtime do .NET no Linux usando o
script de instalação ou extraindo os binários. Para obter uma lista de distribuições que
dê suporte ao gerenciador de pacotes interno, confira Instalar o .NET no Linux.

Instale o SDK (que inclui o runtime) se quiser desenvolver aplicativos .NET. Ou, se você
precisar apenas executar aplicativos, instale o runtime. Se você estiver instalando o
runtime, sugerimos que você instale o Runtime do ASP.NET Core, pois ele inclui
runtimes do .NET e do ASP.NET Core.

Use os comandos dotnet --list-sdks e dotnet --list-runtimes para ver quais versões
estão instaladas. Para obter mais informações, confira Como verificar se o .NET já está
instalado.

Versões do .NET
Há dois tipos de versões com suporte, versões LTS (Suporte de Longo Prazo) ou STS
(Suporte com Prazo Padrão). A qualidade de todas as versões é a mesma. A única
diferença é a duração do suporte. As versões LTS recebem suporte e patches gratuitos
por 3 anos. As versões STS recebem suporte e patches gratuitos por 18 meses. Para
obter mais informações, consulte a Política de Suporte do .NET .

A seguinte tabela lista o status de suporte de cada versão do .NET (e do .NET Core):

ノ Expandir a tabela

✔️Com suporte ❌ Sem suporte

8 (LTS) 5

7 (STS) 3.1

6 (LTS) 3,0

2,2

2.1
✔️Com suporte ❌ Sem suporte

2,0

1,1

1.0

Dependências
É possível que, ao instalar o .NET, dependências específicas não sejam instaladas, como
durante a instalação manual. A lista a seguir fornece detalhes sobre as distribuições do
Linux com suporte da Microsoft e com dependências que talvez você precise instalar.
Verifique a página de distribuição para obter mais informações:

Alpine
Debian
CentOS
Fedora
RHEL e CentOS Stream
SLES
Ubuntu

Para obter informações genéricas sobre as dependências, confira Aplicativos autônomos


do Linux .

Dependências de RPM
Se a distribuição não tiver sido listada anteriormente e for baseada em RPM, talvez você
precise das seguintes dependências:

krb5-libs
libicu
openssl-libs

Se a versão do OpenSSL do ambiente de runtime de destino for 1.1 ou mais recente,


instale compat-openssl10 .

Dependências de DEB
Se a distribuição não houver sido listada anteriormente e for baseada em Debian, talvez
você precise das seguintes dependências:
libc6
libgcc1
libgssapi-krb5-2
libicu67
libssl1.1
libstdc++6
zlib1g

Dependências comuns
Se o aplicativo .NET usar o assembly System.Drawing.Common, o libgdiplus também
precisará ser instalado. Como System.Drawing.Common não tem mais suporte no Linux,
isso só funciona no .NET 6 e requer a definição da alternância de configuração de
runtime System.Drawing.EnableUnixSupport .

Normalmente, é possível instalar uma versão recente do libgdiplusadicionando o


repositório Mono ao sistema .

Instalação com script


Os scripts dotnet-install são usados para instalações de automação e não
administrativas do SDK e do Runtime. É possível baixar o script em
https://dot.net/v1/dotnet-install.sh . Quando o .NET é instalado dessa forma, você
deverá instalar as dependências exigidas pela distribuição do Linux. Confira os links no
artigo Instalar o .NET no Linux para sua distribuição específica do Linux.

) Importante

O Bash é necessário para executar o script.

É possível baixar o script com wget :

Bash

wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh

Antes de executar esse script, certifique-se de conceder permissão para que esse script
seja executado como um executável:

Bash
chmod +x ./dotnet-install.sh

O script usa como padrão a instalação da versão mais recente do SDK do suporte a
longo prazo (LTS) , que é o .NET 8. Para instalar a versão mais recente, que pode não
ser uma versão LTS, use o parâmetro --version latest .

Bash

./dotnet-install.sh --version latest

Para instalar o .NET Runtime em vez do SDK, use o parâmetro --runtime .

Bash

./dotnet-install.sh --version latest --runtime aspnetcore

Você pode instalar uma versão principal específica com o parâmetro --channel a fim de
indicar a versão desejada. O comando a seguir instala o SDK do .NET 8.0.

Bash

./dotnet-install.sh --channel 8.0

Para obter mais informações, confira a referência de scripts dotnet-install.

Para habilitar o .NET na linha de comando, confira Definir variáveis de ambiente em


todo o sistema.

Instalação manual
Como alternativa aos gerenciadores de pacotes, você pode baixar e instalar
manualmente o SDK e o runtime. A instalação manual é usada com frequência como
parte do teste de integração contínua ou em uma distribuição Linux sem suporte. Para
desenvolvedores ou usuários, é melhor usar um gerenciador de pacotes.

Baixe uma versão binária do SDK ou do runtime em um dos sites a seguir. O SDK do
.NET inclui o runtime correspondente:

✔️Downloads do .NET 8
✔️Downloads do .NET 7
✔️Downloads do .NET 6
Todos os downloads do .NET Core

Extraia o arquivo baixado e use o comando export para definir DOTNET_ROOT como o
local da pasta extraída e certifique-se de que o .NET esteja em PATH. A exportação de
DOTNET_ROOT disponibiliza os comandos da CLI do .NET no terminal. Para saber mais

sobre as variáveis de ambiente do .NET, confira Variáveis de ambiente da CLI e do SDK


do .NET.

Diferentes versões do .NET podem ser extraídas para a mesma pasta, que coexistem
lado a lado.

Exemplo
Os comandos a seguir usam o Bash para definir a variável de ambiente DOTNET_ROOT
para o diretório de trabalho atual seguida de .dotnet . Se não existir, o diretório será
criado. A variável de ambiente DOTNET_FILE é o nome do arquivo da versão binária do
.NET que você quer instalar. Esse arquivo é extraído para o diretório DOTNET_ROOT . O
diretório DOTNET_ROOT e seu subdiretório tools são adicionados à variável de ambiente
PATH .

) Importante

Se você executar esses comandos, lembre-se de alterar o valor DOTNET_FILE para o


nome do binário do .NET que você baixou.

Bash

DOTNET_FILE=dotnet-sdk-8.0.100-linux-x64.tar.gz
export DOTNET_ROOT=$(pwd)/.dotnet

mkdir -p "$DOTNET_ROOT" && tar zxf "$DOTNET_FILE" -C "$DOTNET_ROOT"

export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools

Você pode instalar mais de uma versão do .NET na mesma pasta.

Você também pode instalar o .NET no diretório inicial identificado pela variável HOME ou
caminho ~ :

Bash

export DOTNET_ROOT=$HOME/.dotnet
Verificar binários baixados
Depois de baixar um instalador, verifique se o arquivo não foi alterado ou corrompido.
Você pode obter a soma de verificação no computador e depois compará-la com o que
foi relatado no site de download.

Quando você baixa um instalador ou um binário de uma página de download oficial, a


soma de verificação do arquivo é exibida. Selecione o botão Copiar a fim de copiar o
valor de soma de verificação para a área de transferência.

Use o comando sha512sum para imprimir a soma de verificação do arquivo que você
baixou. Por exemplo, o comando a seguir relata a soma de verificação do arquivo
dotnet-sdk-8.0.100-linux-x64.tar.gz:

Bash

$ sha512sum dotnet-sdk-8.0.100-linux-x64.tar.gz
13905ea20191e70baeba50b0e9bbe5f752a7c34587878ee104744f9fb453bfe439994d389697
22bdae7f60ee047d75dda8636f3ab62659450e9cd4024f38b2a5 dotnet-sdk-8.0.100-
linux-x64.tar.gz

Compare a soma de verificação com o valor fornecido pelo site de download.

) Importante

Embora um arquivo Linux seja mostrado nesses exemplos, essas informações se


aplicam igualmente ao macOS.

Usar um arquivo de soma de verificação para validar


As notas sobre a versão do .NET contêm um link para um arquivo de soma de
verificação que você pode usar para validar o arquivo baixado. As seguintes etapas
descrevem como baixar o arquivo de soma de verificação e validar um binário de
instalação do .NET:

1. A página de notas sobre a versão do .NET 8 no GitHub em


https://github.com/dotnet/core/tree/main/release-notes/8.0 contém uma seção
chamada Versões. A tabela nessa seção está vinculada aos arquivos de soma de
verificação e downloads de cada versão do .NET 8:

2. Selecione o link da versão do .NET que você baixou. A seção anterior usou o SDK
do .NET 8.0.100, que está na versão 8.0.0 do .NET.

3. Na página de lançamento, você pode ver a versão do Runtime do .NET e do SDK


do .NET e um link para o arquivo de soma de verificação:

4. Copie o link do arquivo de soma de verificação.


5. Use o script a seguir, mas substitua o link para baixar o arquivo de soma de
verificação apropriado:

Bash

curl -O https://dotnetcli.blob.core.windows.net/dotnet/checksums/8.0.0-
sha.txt

6. Com o arquivo de soma de verificação e o arquivo de versão do .NET baixado para


o mesmo diretório, use o comando sha512sum -c {file} --ignore-missing para
validar o arquivo baixado.

Quando a validação for aprovada, você verá o arquivo impresso com o status OK:

Bash

$ sha512sum -c 8.0.0-sha.txt --ignore-missing


dotnet-sdk-8.0.100-linux-x64.tar.gz: OK

Se você vir o arquivo marcado como COM FALHA, o arquivo baixado não será
válido e não deverá ser usado.

Bash

$ sha512sum -c 8.0.0-sha.txt --ignore-missing


dotnet-sdk-8.0.100-linux-x64.tar.gz: FAILED
sha512sum: WARNING: 1 computed checksum did NOT match
sha512sum: 8.0.0-sha.txt: no file was verified

Definir variáveis de ambiente em todo o


sistema
Se você usou o script de instalação anterior, as variáveis definidas se aplicam somente à
sessão do terminal atual. Adicione-as ao perfil de shell. Há vários shells diferentes
disponíveis para Linux, cada qual com um perfil distinto. Por exemplo:

Bash Shell: ~/.bash_profile ou ~/.bashrc


Korn Shell: ~/.kshrc ou .profile
Z Shell: ~/.zshrc ou .zprofile

Defina as duas variáveis de ambiente a seguir no perfil de shell:

DOTNET_ROOT
Essa variável é definida como a pasta na qual o .NET foi instalado, como
$HOME/.dotnet :

Bash

export DOTNET_ROOT=$HOME/.dotnet

PATH

Essa variável deve incluir tanto a pasta DOTNET_ROOT quanto a pasta


DOTNET_ROOT/tools :

Bash

export PATH=$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools

Próximas etapas
Como habilitar o preenchimento com Tab na CLI do .NET
Tutorial: criar um aplicativo de console com o SDK do .NET usando o Visual Studio
Code

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Atualizar para uma nova versão do .NET
Artigo • 01/04/2024

Novas versões do .NET são lançadas a cada ano . Muitos desenvolvedores iniciam o
processo de atualização assim que a nova versão está disponível, enquanto outros
esperam até que a versão que estão usando não tenha mais suporte. Há vários aspectos
que devem ser considerados no processo de atualização.

Motivos comuns para atualizar para uma nova versão do .NET:

Não há mais suporte para a versão do .NET usada no momento


A nova versão dá suporte a um novo sistema operacional
A nova versão tem um recurso importante de API, desempenho ou segurança

Atualizar ambiente de desenvolvimento


Para atualizar para uma nova versão do .NET, o SDK do .NET é o componente principal a
ser instalado. Ele inclui uma versão da CLI do .NET atualizada, do sistema de build e do
runtime.

O site do .NET oferece instaladores e arquivos que você pode baixar e usar em
qualquer sistema operacional e arquitetura com suporte.

Alguns sistemas operacionais têm um gerenciador de pacotes que você também pode
usar para instalar uma nova versão do .NET, que você pode preferir.

macOS
Linux
Windows

O Visual Studio instala novas versões do SDK do .NET automaticamente. Para usuários
do Visual Studio, é suficiente atualizar para uma versão mais recente do Visual Studio.

Atualizar código-fonte
A única alteração necessária para atualizar um aplicativo é atualizar a propriedade
TargetFramework em um arquivo de projeto para a versão mais recente do .NET.

Veja como fazer isso:

Abra o arquivo de projeto (o arquivo *.csproj , *.vbproj ou *.fsproj ).


Altere o valor da propriedade <TargetFramework> de, por exemplo, net6.0 para
net8.0 .

O mesmo padrão se aplica à propriedade <TargetFrameworks> se ela estiver sendo


usada.

O Assistente de Atualização pode fazer essas alterações automaticamente.

A próxima etapa é criar o projeto (ou solução) com o novo SDK. Se forem necessárias
alterações adicionais, o SDK fornecerá avisos e erros que o orientarão.

Talvez seja necessário executar dotnet workload restore para restaurar cargas de
trabalho com a nova versão do SDK.

Mais recursos:

Alterações interruptivas no .NET 8


Migrar do ASP.NET Core no .NET 7 para o .NET 8
Atualizar .NET MAUI do .NET 7 para o .NET 8

Atualizar a CI (integração contínua)


Os pipelines de CI seguem um processo de atualização semelhante como arquivos de
projeto e Dockerfiles. Normalmente, você pode atualizar pipelines de CI alterando
apenas os valores de versão.

Atualizar o ambiente de hospedagem


Há muitos padrões que são usados para hospedar aplicativos. Se o ambiente de
hospedagem incluir o runtime do .NET, a nova versão do runtime do .NET precisará ser
instalada. No Linux, dependências precisam ser instaladas, no entanto, elas
normalmente não são alteradas nas versões do .NET.

Para contêineres, instruções FROM precisam ser alteradas para incluir novos números
de versão.

O exemplo do Dockerfile a seguir demonstra como efetuar pull de uma imagem do


ASP.NET Core 8.0.

dockerfile=

FROM mcr.microsoft.com/dotnet/aspnet:8.0
Em um serviço de nuvem como o Serviço de Aplicativo do Azure, é necessário alterar a
configuração.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como remover o SDK e o runtime do
.NET
Artigo • 06/12/2023

Ao longo do tempo, ao instalar as versões atualizadas do runtime e do SDK do .NET,


convém remover as versões desatualizadas do .NET do computador. A desinstalação de
versões mais antigas do runtime pode alterar o runtime escolhido para executar os
aplicativos de estrutura compartilhada, conforme detalhado no artigo Seleção da versão
do .NET.

Devo remover uma versão?


Os comportamentos de seleção de versão do .NET e a compatibilidade do runtime do
.NET entre as atualizações permitem a remoção segura das versões anteriores. As
atualizações de runtime do .NET são compatíveis em uma banda de versão principal,
como 7.x e 6.x. Além disso, as versões mais recentes do SDK do .NET geralmente
mantêm a capacidade de compilar aplicativos destinados a versões anteriores do
runtime de um modo compatível.

Em geral, você precisa apenas do SDK mais recente e da versão de patch mais recente
dos runtimes necessários para seu aplicativo. As instâncias em que convém manter as
versões mais antigas do SDK ou do runtime incluem a manutenção de aplicativos
baseados no project.json. A menos que seu aplicativo tenha motivos específicos para
runtimes ou SDKs anteriores, você pode remover com segurança as versões mais
antigas.

Determinar o que está instalado


A CLI do .NET tem opções que você pode usar para listar as versões do SDK e do
runtime que estão instaladas no computador. Use dotnet --list-sdks para ver a lista de
SDKs instalados e dotnet --list-runtimes para ver a lista de runtimes. Para obter mais
informações, confira Como verificar se o .NET já está instalado.

Desinstalar o .NET
O .NET usa a caixa de diálogo Aplicativos e recursos do Windows para remover versões
do runtime e do SDK do .NET. A figura a seguir mostra a caixa de diálogo Recursos e
aplicativos. Você pode procurar core ou .net para filtrar e mostrar as versões instaladas
do .NET.

Selecione todas as versões que você quer remover do computador e clique em


Desinstalar.

Ferramenta de Desinstalação do .NET


A Ferramenta de desinstalação do .NET ( dotnet-core-uninstall ) permite remover SDKs
e runtimes do .NET de um sistema. Uma coleção de opções está disponível para
especificar quais versões devem ser desinstaladas.

Dependência do Visual Studio em versões do


SDK do .NET
Antes do Visual Studio 2019 versão 16.3, os instaladores do Visual Studio chamavam o
instalador do SDK autônomo para o .NET Core versão 2.1 ou 2.2. Como resultado, as
versões do SDK aparecem na caixa de diálogo Aplicativos e recursos do Windows. A
remoção dos SDKs do .NET que foram instalados pelo Visual Studio usando o instalador
autônomo pode interromper o Visual Studio. Se o Visual Studio tiver problemas após a
desinstalação dos SDKs, execute Repair nessa versão específica do Visual Studio. A
seguinte tabela mostra algumas das dependências do Visual Studio em versões do SDK
do .NET Core:

ノ Expand table

Versão do Visual Studio Versão do SDK do .NET Core

Visual Studio 2019 versão 16.2 SDK do .NET Core 2.2.4xx, 2.1.8xx

Visual Studio 2019 versão 16.1 SDK do .NET Core 2.2.3xx, 2.1.7xx

Visual Studio 2019 versão 16.0 SDK do .NET Core 2.2.2xx, 2.1.6xx

Visual Studio 2017 versão 15.9 SDK do .NET Core 2.2.1xx, 2.1.5xx

Visual Studio 2017 versão 15.8 SDK do .NET Core 2.1.4xx

Do Visual Studio 2019 versão 16.3 em diante, o Visual Studio é responsável pela própria
cópia do SDK do .NET. Por esse motivo, você não verá mais essas versões do SDK na
caixa de diálogo Aplicativos e recursos.

Remover o diretório de fallback do NuGet


Antes do SDK do .NET Core 3.0, os instaladores do SDK do .NET Core usavam um
diretório chamado NuGetFallbackFolder para armazenar um cache dos pacotes do
NuGet. Esse cache era usado durante operações como dotnet restore ou dotnet build
/t:Restore . O NuGetFallbackFolder estava na pasta sdk em que o .NET era instalado. Por

exemplo, eles podem estar em C:\Program Files\dotnet\sdk\NuGetFallbackFolder no


Windows e em /usr/local/share/dotnet/sdk/NuGetFallbackFolder no macOS.

É possível remover esse diretório nos seguintes casos:

Estiver desenvolvendo usando o SDK do .NET Core 3.0 ou do .NET 5 e versões


posteriores.
Estiver desenvolvendo usando versões do SDK do .NET Core anteriores à 3.0, mas
puder trabalhar online.

Se você deseja remover o diretório de fallback do NuGet, será possível excluí-lo, mas
serão necessários privilégios de administrador para isso.

Não é recomendado excluir o diretório dotnet. Isso removeria todas as ferramentas


globais já instaladas. Além disso, no Windows:

Você interromperia o Visual Studio 2019 versão 16.3 e versões posteriores. Nesse
caso, você poderá executar Repair para recuperá-lo.
Se houver entradas do SDK do .NET Core na caixa de diálogo Aplicativos e
recursos, elas ficarão órfãs.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Gerenciar modelos de projeto e de item
do .NET
Artigo • 16/06/2023

O .NET fornece um sistema de modelos que permite que os usuários instalem ou


desinstalem pacotes que contêm modelos do NuGet, um arquivo de pacote NuGet ou
um diretório do sistema de arquivos. Este artigo descreve como gerenciar modelos do
.NET por meio da CLI do SDK do .NET.

Para obter mais informações sobre como criar modelos, confira Tutorial: criar modelos.

Instalar modelo
Os pacotes de modelos são instalados por meio do comando do SDK instalar o dotnet
new. Você pode fornecer o identificador do pacote NuGet de um pacote de modelos ou
uma pasta que contenha os arquivos de modelo.

Pacote NuGet hospedado


Os pacotes de modelos da CLI do .NET são carregados no NuGet para distribuição
ampla. Os pacotes de modelos também podem ser instalados por um feed privado. Em
vez de carregar um pacote de modelos em um feed do NuGet, os arquivos de modelo
nupkg podem ser distribuídos e instalados manualmente, conforme descrito na seção
Pacote NuGet local.

Para obter mais informações sobre como configurar feeds do NuGet, confira dotnet
nuget add source.

Para instalar um pacote de modelos do feed padrão do NuGet, use o comando dotnet
new install {package-id} :

CLI do .NET

dotnet new install Microsoft.DotNet.Web.Spa.ProjectTemplates

Para instalar um pacote de modelos do feed padrão do NuGet com uma versão
específica, use o comando dotnet new install {package-id}::{version} :

CLI do .NET
dotnet new install Microsoft.DotNet.Web.Spa.ProjectTemplates::2.2.6

Pacote NuGet local


Quando um pacote de modelos é criado, um arquivo nupkg é gerado. Se você tiver um
arquivo nupkg contendo modelos, poderá instalá-lo com o comando dotnet new
install {path-to-package} :

CLI do .NET

dotnet new install c:\code\nuget-packages\Some.Templates.1.0.0.nupkg

Pasta
Como alternativa à instalação do modelo por um arquivo nupkg, você também pode
instalar modelos por uma pasta diretamente com o comando dotnet new install
{folder-path} . A pasta especificada é tratada como o identificador do pacote de
modelos de qualquer modelo encontrado. Qualquer modelo encontrado na hierarquia
da pasta especificada é instalado.

CLI do .NET

dotnet new install c:\code\nuget-packages\some-folder\

O {folder-path} especificado no comando torna-se o identificador do pacote de


modelos de todos os modelos encontrados. Conforme especificado na seção Listar
pacotes de modelos, você pode obter uma lista de pacotes de modelos instalados com
o comando dotnet new uninstall . Neste exemplo, o identificador do pacote de
modelos é mostrado como a pasta usada na instalação:

Console

dotnet new uninstall


Currently installed items:

... cut to save space ...

c:\code\nuget-packages\some-folder
Templates:
A Template Console Class (templateconsole) C#
Project for some technology (contosoproject) C#
Uninstall Command:
dotnet new uninstall c:\code\nuget-packages\some-folder

Desinstalar o pacote de modelos


Os pacotes de modelos são desinstalados por meio do comando do SDK desinstalar o
dotnet new. Você pode fornecer o identificador do pacote NuGet de um pacote de
modelos ou uma pasta que contenha os arquivos de modelo.

Pacote NuGet
Depois que um pacote de modelos NuGet for instalado, por um feed do NuGet ou um
arquivo nupkg, você poderá desinstalá-lo fazendo referência ao identificador do pacote
NuGet.

Para desinstalar um pacote de modelos, use o comando dotnet new uninstall


{package-id} :

CLI do .NET

dotnet new uninstall Microsoft.DotNet.Web.Spa.ProjectTemplates

Pasta
Quando os modelos são instalados por meio de um caminho de pasta, esse caminho se
torna o identificador do pacote de modelos.

Para desinstalar um pacote de modelos, use o comando dotnet new uninstall


{package-folder-path} :

CLI do .NET

dotnet new uninstall c:\code\nuget-packages\some-folder

Listar pacotes de modelos


Usando o comando de desinstalação padrão sem um identificador do pacote, você
pode ver uma lista de pacotes de modelos instalados juntamente com o comando que
desinstala cada pacote de modelos.
Console

dotnet new uninstall


Currently installed items:

... cut to save space ...

c:\code\nuget-packages\some-folder
Templates:
A Template Console Class (templateconsole) C#
Project for some technology (contosoproject) C#
Uninstall Command:
dotnet new uninstall c:\code\nuget-packages\some-folder

Instalar pacotes de modelos de outros SDKs


Se você instalou cada versão do SDK sequencialmente, por exemplo, instalou o SDK 6.0,
o SDK 7.0 e assim por diante, terá todos os modelos do SDK instalados. No entanto, se
você começar com uma versão posterior do SDK, como a 7.0, somente os modelos
dessa versão serão incluídos. Modelos de qualquer outra versão não serão incluídos.

Os modelos do .NET estão disponíveis no NuGet e você pode instalá-los como qualquer
outro modelo. Para obter mais informações, confira Instalar pacote NuGet hospedado.

. Identificador do pacote NuGet

.NET Core 2.1 Microsoft.DotNet.Common.ProjectTemplates.2.1

.NET Core 2.2 Microsoft.DotNet.Common.ProjectTemplates.2.2

.NET Core 3.0 Microsoft.DotNet.Common.ProjectTemplates.3.0

.NET Core 3.1 Microsoft.DotNet.Common.ProjectTemplates.3.1

.NET 5.0 Microsoft.DotNet.Common.ProjectTemplates.5.0

.NET 6.0 Microsoft.DotNet.Common.ProjectTemplates.6.0

.NET 7.0 Microsoft.DotNet.Common.ProjectTemplates.7.0

ASP.NET Core 2.1 Microsoft.DotNet.Web.ProjectTemplates.2.1

ASP.NET Core 2.2 Microsoft.DotNet.Web.ProjectTemplates.2.2

ASP.NET Core 3.0 Microsoft.DotNet.Web.ProjectTemplates.3.0

ASP.NET Core 3.1 Microsoft.DotNet.Web.ProjectTemplates.3.1

ASP.NET Core 5.0 Microsoft.DotNet.Web.ProjectTemplates.5.0


. Identificador do pacote NuGet

ASP.NET Core 6.0 Microsoft.DotNet.Web.ProjectTemplates.6.0

ASP.NET Core 7.0 Microsoft.DotNet.Web.ProjectTemplates.7.0

Por exemplo, o SDK do .NET 7 inclui modelos de um aplicativo de console direcionado


para o .NET 7. Para direcionar para o .NET Core 3.1, você precisaria instalar o pacote de
modelos 3.1.

1. Tente criar um aplicativo direcionado para o .NET Core 3.1.

CLI do .NET

dotnet new console --framework netcoreapp3.1

Se você encontrar uma mensagem de erro, precisará instalar os modelos.

2. Instale os modelos de projeto do .NET Core 3.1.

CLI do .NET

dotnet new install Microsoft.DotNet.Common.ProjectTemplates.3.1

3. Tente criar o aplicativo uma segunda vez.

CLI do .NET

dotnet new console --framework netcoreapp3.1

E você verá uma mensagem indicando que o projeto foi criado.

O modelo "Aplicativo de Console" foi criado com êxito.

Processando ações pós-criação... Executando 'dotnet restore' em path-to-


project-file.csproj... Determinando projetos a serem restaurados... Restauração
concluída em 1,05 segundo para path-to-project-file.csproj.

A restauração foi bem-sucedida.

Confira também
Tutorial: Criar um modelo de item
dotnet new
dotnet nuget add source
MacOS Catalina Notarization e o
impacto nos downloads e projetos do
.NET
Artigo • 10/05/2023

A partir do macOS Catalina (versão 10.15), todos os softwares criados após 1º de junho
de 2019 e distribuídos com a ID do Desenvolvedor devem ser autenticados. Esse
requisito se aplica ao runtime do .NET, ao SDK do .NET e ao software criado com o .NET.
Este artigo descreve os cenários comuns que você pode enfrentar com a autenticação
de .NET e macOS.

Instalando o .NET
Os instaladores do .NET (runtime e SDK) foram autenticados desde 18 de fevereiro de
2020. As versões anteriores lançadas não são autenticadas. Você pode instalar
manualmente uma versão não autenticada do .NET baixando primeiro o instalador e, em
seguida, usando o comando sudo installer . Para obter mais informações, consulte
Baixar e instalar manualmente no macOS.

AppHost nativo
No SDK do .NET 7 e versões posteriores, um appHost, que é um executável nativo de
Mach-O, é produzido para seu aplicativo. Esse executável geralmente é invocado pelo
.NET quando seu projeto compila, publica ou é executado com o comando dotnet run .
A versão não appHost do aplicativo é um arquivo dll que pode ser invocado pelo
comando dotnet <app.dll> .

Quando executado localmente, o SDK assina o apphost usando a assinatura ad hoc ,


que permite que o aplicativo seja executado localmente. Ao distribuir seu aplicativo,
você precisará assinar corretamente seu aplicativo de acordo com as diretrizes da Apple.

Você também pode distribuir seu aplicativo sem o apphost e contar com usuários para
executar seu aplicativo usando dotnet . Para desativar a geração appHost , adicione a
configuração booliana UseAppHost no arquivo de projeto e defina-a como false . Você
também pode alternar o appHost com o parâmetro -p:UseAppHost na linha de comando
para o comando dotnet específico executado:

Arquivo de projeto
XML

<PropertyGroup>
<UseAppHost>false</UseAppHost>
</PropertyGroup>

Parâmetro de linha de comando

CLI do .NET

dotnet run -p:UseAppHost=false

Um appHost é necessário quando você publica seu aplicativo autossuficientee não pode
desabilitá-lo.

Para obter mais informações sobre a configuração UseAppHost , consulte Propriedades


do MSBuild para Microsoft.NET.Sdk.

Contexto do appHost
Quando o appHost está habilitado em seu projeto e você usa o comando dotnet run
para executar seu aplicativo, o aplicativo é invocado no contexto do appHost e não no
host padrão (o host padrão é o comando dotnet ). Se o appHost estiver desabilitado em
seu projeto, o comando dotnet run executará seu aplicativo no contexto do host
padrão. Mesmo que o appHost esteja desabilitado, publicar seu aplicativo como
autônomo gera um executável appHost e os usuários usam esse executável para
executar seu aplicativo. Executar seu aplicativo com dotnet <filename.dll> invoca o
aplicativo com o host padrão, o runtime compartilhado.

Quando um aplicativo que usa o appHost é invocado, a partição de certificado acessada


pelo aplicativo é diferente do host padrão autenticado. Se o aplicativo precisar acessar
os certificados instalados por meio do host padrão, use o comando dotnet run para
executar seu aplicativo no arquivo de projeto ou use o comando dotnet <filename.dll>
para iniciar o aplicativo diretamente.

Mais informações sobre esse cenário são fornecidas na seção ASP.NET Core e macOS e
certificados.

ASP.NET Core, macOS e certificados


O .NET fornece a capacidade de gerenciar certificados no conjunto de chaves do macOS
com a classe System.Security.Cryptography.X509Certificates. O acesso ao conjunto de
chaves do macOS usa a identidade dos aplicativos como a chave primária ao decidir
qual partição considerar. Por exemplo, aplicativos não assinados armazenam segredos
na partição não assinada, mas os aplicativos assinados armazenam seus segredos em
partições apenas eles podem acessar. A origem da execução que invoca seu aplicativo
decide qual partição usar.

O .NET fornece três fontes de execução: appHost, host padrão (o comando dotnet ) e
um host personalizado. Cada modelo de execução pode ter identidades diferentes,
assinadas ou não assinadas, e tem acesso a partições diferentes dentro do conjunto de
chaves. Certificados importados por um modo podem não estar acessíveis a partir de
outro. Por exemplo, as versões autenticadas do .NET têm um host padrão assinado. Os
certificados são importados para uma partição segura com base em sua identidade.
Esses certificados não podem ser acessados por meio de um appHost gerado, pois o
appHost está assinado ad hoc.

Outro exemplo, por padrão, ASP.NET Core importa um certificado SSL padrão por meio
do host padrão. Aplicativos ASP.NET Core que usam um appHost não terão acesso a
esse certificado e receberão um erro quando o .NET detectar que o certificado não está
acessível. A mensagem de erro fornece instruções sobre como corrigir esse problema.

Se o compartilhamento de certificados for necessário, o macOS fornecerá opções de


configuração com o utilitário security .

Para obter mais informações sobre como solucionar problemas de certificado ASP.NET
Core, consulte Impor HTTPS em ASP.NET Core.

Direitos padrão
O host padrão do .NET (o comando dotnet ) tem um conjunto de direitos padrão. Esses
direitos são necessários para a operação adequada do .NET. É possível que seu
aplicativo precise de direitos adicionais, nesse caso, você precisará gerar e usar um
appHost e, em seguida, adicionar os direitos necessários localmente.

Conjunto padrão de direitos para .NET:

com.apple.security.cs.allow-jit

com.apple.security.cs.allow-unsigned-executable-memory
com.apple.security.cs.allow-dyld-environment-variables

com.apple.security.cs.disable-library-validation

Autenticar um aplicativo .NET


Se você quiser que seu aplicativo seja executado no macOS Catalina (versão 10.15) ou
superior, você desejará autenticar seu aplicativo. O appHost que você envia com seu
aplicativo para autenticação deve ser usado com pelo menos os mesmos direitos
padrão para o .NET Core.

Próximas etapas
Instale .NET no macOS.
Solucionar erros do .NET relacionados a
arquivos ausentes no Linux
Artigo • 17/03/2023

Ao tentar usar o .NET no Linux, comandos como dotnet new e dotnet run podem falhar
com uma mensagem relacionada a um arquivo não encontrado, como fxr, libhostfxr.so
ou FrameworkList.xml. Algumas das mensagens de erro podem ser semelhantes aos
seguintes itens:

System.IO.FileNotFoundException

System.IO.FileNotFoundException: não foi possível localizar o arquivo


'/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/5.0.0/data/FrameworkList
.xml'.

Ocorreu um erro fatal.

Ocorreu um erro fatal. Não foi possível encontrar a biblioteca necessária


libhostfxr.so.

ou

Ocorreu um erro fatal. A pasta [/usr/share/dotnethost/fxr] não existe.

ou

Ocorreu um erro fatal, a pasta [//usrshare/dotnethost/fxr] não contém pastas


filho com o número da versão.

Mensagens genéricas sobre dotnet não encontradas

Uma mensagem geral pode ser exibida indicando que o SDK não foi encontrado
ou que o pacote já foi instalado.

Um sintoma desses problemas é que as pastas /usr/lib64/dotnet e /usr/share/dotnet


estão no sistema.

 Dica
Use o comando dotnet --info para listar quais SDKs e runtimes estão instalados.
Para obter mais informações, confira Como verificar se o .NET já está instalado.

O que está acontecendo


Esses erros geralmente ocorrem quando dois repositórios de pacotes do Linux fornecem
pacotes do .NET. Embora a Microsoft forneça um repositório de pacotes Linux para
pacotes .NET de origem, algumas distribuições do Linux também fornecem pacotes
.NET, como:

Alpine Linux
Arch
CentOS
CentOS Stream
Fedora
RHEL
Ubuntu 22.04+

A combinação de pacotes .NET de duas fontes diferentes provavelmente causará


problemas, uma vez que os pacotes podem colocar os recursos em caminhos diferentes
e podem ser compilados de forma diferente.

Soluções
A solução para esses problemas é usar o .NET em um repositório de pacotes. Qual
repositório escolher e como fazer isso varia de acordo com o caso de uso e a
distribuição do Linux.

Minha distribuição do Linux fornece pacotes do .NET e desejo usá-los.


Preciso de uma versão do .NET que não é fornecida pela minha distribuição do
Linux.

Minha distribuição do Linux fornece pacotes do .NET e


desejo usá-los
Você usa o repositório da Microsoft para outros pacotes, como o PowerShell e o
MSSQL?

Sim
Configure o gerenciador de pacotes para ignorar os pacotes do .NET no
repositório da Microsoft. É possível que você tenha instalado o .NET por meio
dos dois repositórios, portanto, escolha um ou outro.

1. Remova os pacotes existentes do .NET de sua distribuição. Recomece e


não os instale por meio do repositório errado.

Bash

sudo dnf remove 'dotnet*' 'aspnet*' 'netstandard*'

2. Configure o repositório da Microsoft para ignorar os pacotes do .NET.

Bash

echo 'excludepkgs=dotnet*,aspnet*,netstandard*' | sudo tee -a


/etc/yum.repos.d/microsoft-prod.repo

3. Reinstale o .NET por meio do feed de pacotes da distribuição. Para saber


mais, confira Instalar o .NET no Linux.

Não

1. Remova os pacotes existentes do .NET de sua distribuição. Recomece e


não os instale por meio do repositório errado.

Bash

sudo dnf remove 'dotnet*' 'aspnet*' 'netstandard*'

2. Exclua o feed do repositório da Microsoft de sua distribuição.

Bash

sudo dnf remove packages-microsoft-prod

3. Reinstale o .NET por meio do feed de pacotes da distribuição. Para saber


mais, confira Instalar o .NET no Linux.

Preciso de uma versão do .NET que não é fornecida pela


minha distribuição Linux
Configure o gerenciador de pacotes para ignorar os pacotes do .NET no repositório da
distribuição. É possível que você tenha instalado o .NET por meio dos dois repositórios,
portanto, escolha um ou outro.

1. Remova os pacotes existentes do .NET de sua distribuição. Recomece e não os


instale por meio do repositório errado.

Bash

sudo dnf remove 'dotnet*' 'aspnet*' 'netstandard*'

2. Configure o repositório do Linux para ignorar os pacotes do .NET.

Bash

echo 'excludepkgs=dotnet*,aspnet*,netstandard*' | sudo tee -a


/etc/yum.repos.d/<your-package-source>.repo

Substitua <your-package-source> pela fonte do pacote da sua distribuição.

3. Reinstale o .NET por meio do feed de pacotes da distribuição. Para saber mais,
confira Instalar o .NET no Linux.

Referências online
Muitos outros usuários relataram esses problemas. A lista a seguir é desses problemas.
Você pode ler esses problemas para obter insights sobre o que pode estar acontecendo:

System.IO.FileNotFoundException e
'/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/5.0.0/data/FrameworkList.xml
'
SDK nº 15785: não é possível criar um novo projeto depois de atualizar para
5.0.3
SDK nº 15863: "a tarefa ResolveTargetingPackAssets do MSB4018 falhou
inesperadamente" depois de atualizar para 5.0.103
SDK nº 17411: a compilação dotnet sempre gera erro
SDK nº 12075: dotnet 3.1.301 no Fedora 32, não é possível localizar
FrameworkList.xml porque ele não existe

Erro fatal: não foi possível encontrar libhostfxr.so


SDK nº 17570: depois de atualizar o Fedora 33 para 34 e dotnet 5.0.5 para 5.0.6,
recebo um erro relacionado a libhostfxr.so :
Erro fatal: a pasta /host/fxr não existe
Core nº 5746: a pasta não existe ao instalar o 3.1 no CentOS 8 com o repositório
packages.microsoft.com habilitado
SDK nº 15476: ocorreu um erro fatal. A pasta [/usr/share/dotnet/host/fxr] não
existe :

Erro fatal: a pasta /host/fxr não contém as pastas filho com o número da versão
Instalador nº 9254: erro ao instalar dotnet/core/aspnet:3.1 no CentOS 8 – A
pasta não contém as pastas filho com o número da versão
StackOverflow: erro ao instalar dotnet/core/aspnet:3.1 no CentOS 8 – A pasta
não contém as pastas filho com o número da versão

Erros genéricos sem mensagens claras


Core nº 4605: não é possível executar o "novo console do dotnet"
Core nº 4644: não é possível instalar o SDK 2.1 do .NET Core no Fedora 32
Runtime nº 49375: depois de atualizar para 5.0.200-1 usando o gerenciador de
pacotes, parece que nenhum SDK foi instalado

Confira também
Como verificar se o .NET já está instalado
Como remover o SDK e o runtime do .NET
Instalar o .NET no Linux
Como verificar se o .NET já está
instalado
Artigo • 03/01/2024

Este artigo ensina a verificar quais versões do runtime e do SDK do .NET estão instaladas
no computador. Se você tiver um ambiente de desenvolvimento integrado, como Visual
Studio, o .NET pode já ter sido instalado.

Instalar um SDK instala o runtime correspondente.

Se algum comando neste artigo falhar, isso significa que você não tem o runtime ou o
SDK instalado. Para obter mais informações, confira os artigos de instalação para
Windows, macOS ou Linux.

Verificar versões do SDK


Você pode ver quais versões do SDK do .NET estão instaladas atualmente com um
terminal. Abra um terminal e execute o comando a seguir.

CLI do .NET

dotnet --list-sdks

Você terá um resultado semelhante ao mostrado a seguir.

Console

3.1.424 [C:\program files\dotnet\sdk]


5.0.100 [C:\program files\dotnet\sdk]
6.0.402 [C:\program files\dotnet\sdk]
7.0.404 [C:\program files\dotnet\sdk]
8.0.100 [C:\program files\dotnet\sdk]

Verificar versões do runtime


Você pode ver quais versões do runtime do .NET estão instaladas no momento com o
comando a seguir.

CLI do .NET
dotnet --list-runtimes

Você terá um resultado semelhante ao mostrado a seguir.

Console

Microsoft.AspNetCore.App 3.1.30 [C:\Program


Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 6.0.10 [C:\Program
Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.5 [C:\Program
Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.0 [C:\Program
Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 3.1.30 [C:\Program
Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 5.0.17 [C:\Program
Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 6.0.10 [C:\Program
Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.5 [C:\Program
Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.0 [C:\Program
Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.WindowsDesktop.App 3.1.30 [C:\Program
Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 6.0.10 [C:\Program
Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 7.0.5 [C:\Program
Files\dotnet\shared\Microsoft.WindowsDesktop.App]
Microsoft.WindowsDesktop.App 8.0.0 [C:\Program
Files\dotnet\shared\Microsoft.WindowsDesktop.App]

Verificar se há pastas de instalação


É possível que o .NET esteja instalado, mas não adicionado à variável PATH do seu
sistema operacional ou perfil de usuário. Nesse caso, os comandos das seções
anteriores podem não funcionar. Como alternativa, você pode verificar se as pastas de
instalação do .NET existem.

Quando você instala o .NET por meio de um instalador ou de um script, ele é instalado
em uma pasta padrão. Na maioria das vezes, o instalador ou o script usado para instalar
o .NET oferece a opção de instalar em outra pasta. Se você optar por instalar em outra
pasta, ajuste o início do caminho da pasta.

executável dotnet
C:\program files\dotnet\dotnet.exe
SDK .NET
C:\program files\dotnet\sdk\{version}\

Runtime do .NET
C:\program files\dotnet\shared\{runtime-type}\{version}\

Mais informações
Você pode ver as versões do SDK e as versões do runtime com o comando dotnet --
info . Você também obterá outras informações relacionadas ao ambiente, como a

versão do sistema operacional e o RID (identificador de runtime).

Próximas etapas
Instale o Runtime e o SDK do .NET para Windows.
Instale o Runtime e o SDK do .NET para macOS.
Instale o Runtime e o SDK do .NET para Linux.

Confira também
Determinar quais versões do .NET Framework estão instaladas

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como instalar os arquivos do
IntelliSense localizados para .NET
Artigo • 02/06/2023

O IntelliSense é um recurso de conclusão de código disponível em IDEs (ambientes de


desenvolvimento integrado) diferentes como o Visual Studio. Por padrão, quando você
está desenvolvendo projetos .NET, o SDK inclui apenas a versão em inglês dos arquivos
do IntelliSense. Este artigo explica:

Como instalar a versão localizada desses arquivos.


Como modificar a instalação do Visual Studio para usar uma linguagem diferente.

7 Observação

Os arquivos do IntelliSense localizados não estão mais disponíveis. A versão mais


recente para a qual eles estão disponíveis é o .NET 5. É recomendável usar os
arquivos intelliSense em inglês.

Pré-requisitos
SDK do .NET .
Visual Studio 2019 versão 16.3 ou uma versão posterior.

Baixar e instalar os arquivos do IntelliSense


localizado

) Importante

Este procedimento exige que você tenha permissão de administrador para copiar
os arquivos do IntelliSense para a pasta de instalação do .NET.

1. Acesse a página Baixar arquivos do IntelliSense .

2. Baixe o arquivo do IntelliSense para a linguagem e a versão que você gostaria de


usar.

3. Extraia o conteúdo do arquivo zip.


4. Navegue até a pasta do IntelliSense do .NET.

a. Navegue até a pasta de instalação do .NET. Por padrão, ela está em


%ProgramFiles%\dotnet\packs.

b. Escolha o SDK para o qual você deseja instalar o IntelliSense e navegue até o
caminho associado. Você tem as seguintes opções:

Tipo de SDK Caminho

.NET 6 e posteriores Microsoft.NETCore.App.Ref

Windows Desktop Microsoft.WindowsDesktop.App.Ref

.NET Standard NETStandard.Library.Ref

c. Navegue até a versão para a qual você deseja instalar o IntelliSense localizado.
Por exemplo, 5.0.0.

d. Abra a pasta ref.

e. Abra a pasta moniker. Por exemplo, net5.0.

Portanto, o caminho completo para o qual você navegaria seria semelhante a


C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\5.0.0\ref\net5.0.

5. Crie uma subpasta dentro da pasta moniker que você acabou de abrir. O nome da
pasta indica qual linguagem você deseja usar. A tabela a seguir especifica as
diferentes opções:

Linguagem Nome da pasta

Português (Brasil) pt-br

Chinês (simplificado) zh-hans

Chinês (tradicional) zh-hant

Francês fr

Alemão de

Italiano it

Japonês ja

Coreano ko

Russo ru
Linguagem Nome da pasta

Espanhol es

6. Copie os arquivos .xml que você extraiu na etapa 3 para essa nova pasta. Os
arquivos .xml são divididos por pastas do SDK, portanto, copie-os para o SDK
correspondente que você escolheu na etapa 4.

Modificar o idioma do Visual Studio


Para que o Visual Studio use um idioma diferente para o IntelliSense, instale o pacote de
idiomas apropriado. Isso pode ser feito durante a instalação ou em um momento
posterior, modificando a instalação do Visual Studio. Se você já tiver o Visual Studio
configurado para o idioma de sua escolha, a instalação do IntelliSense estará pronta.

Instalar o pacote de idiomas


Se você não instalou o pacote de idiomas desejado durante a instalação, atualize o
Visual Studio da seguinte maneira para instalar o pacote de idiomas:

) Importante

Para instalar, atualizar ou modificar o Visual Studio, será necessário fazer logon com
uma conta que tenha permissão de administrador. Para saber mais, confira
Permissões de usuário e Visual Studio.

1. Localize o Instalador do Visual Studio no computador.

Por exemplo, em um computador que executa o Windows 10, selecione Iniciar e,


em seguida, role até a letra I, onde ele está listado como Instalador do Visual
Studio.
7 Observação

Também é possível encontrar o Instalador do Visual Studio no seguinte local:

C:\Program Files (x86)\Microsoft Visual Studio\Installer\vs_installer.exe

Talvez você precise atualizar o instalador antes de continuar. Nesse caso, siga os
prompts.

2. No instalador, procure a edição do Visual Studio à qual você deseja adicionar o


pacote de idiomas e escolha Modificar.
) Importante

Se você não vir um botão Modificar, mas vir um Atualizar, será necessário
atualizar seu Visual Studio antes de poder modificar sua instalação. Depois
que a atualização for concluída, o botão Modificar deverá ser exibido.

3. Na guia Pacotes de idiomas, selecione ou cancele a seleção dos idiomas que você
deseja instalar ou desinstalar.

4. Escolha Modificar. A atualização será iniciada.

Modificar as configurações de idioma no Visual Studio


Depois de instalar os pacotes de idiomas desejados, modifique suas configurações do
Visual Studio para usar um idioma diferente:

1. Abra o Visual Studio.

2. Na janela de início, escolha Continuar sem código.

3. Na barra de menus, selecione Ferramentas>Opções. A caixa de diálogo Opções é


aberta.

4. No nó Ambiente, escolha Configurações Internacionais.

5. No menu suspenso Idioma, selecione o idioma desejado. Selecione OK.

6. Uma caixa de diálogo informa que você precisa reiniciar o Visual Studio para que
as alterações entrem em vigor. Selecione OK.

7. Reinicie o Visual Studio.

Depois disso, o IntelliSense deverá funcionar conforme o esperado ao abrir um projeto


.NET direcionado à versão dos arquivos do IntelliSense que você acabou de instalar.

Confira também
IntelliSense no Visual Studio
Introdução ao NET
Artigo • 05/02/2024

O .NET é uma plataforma de desenvolvimento gratuita multiplataforma e de código


aberto para criar muitos tipos de aplicativos. Ele pode executar programas escritos em
vários idiomas, com C# sendo os mais populares. Ele depende de um runtime de alto
desempenho usado em produção por muitos aplicativos de alta escala .

Para saber como baixar o .NET e começar a escrever seu primeiro aplicativo, consulte
Introdução.

A plataforma .NET foi projetada para fornecer produtividade, desempenho, segurança e


confiabilidade. Ele fornece gerenciamento automático de memória por um coletor de
lixo (GC). Ele é seguro de tipo e de memória, devido ao uso de um GC e compiladores
de idioma estritos. Oferece simultaneidade via async / await e Task primitivas. Ele inclui
um grande conjunto de bibliotecas que têm ampla funcionalidade e foram otimizadas
para desempenho em vários sistemas operacionais e arquiteturas de chip.

O .NET tem os seguintes pontos de design :

A Produtividade é de pilha completa com runtime, bibliotecas, idioma e


ferramentas, contribuindo para a experiência do usuário do desenvolvedor.
O Código seguro é o modelo de computação primário, enquanto o código não
seguro permite otimizações manuais adicionais.
O Código estático e dinâmico têm suporte, permitindo um amplo conjunto de
cenários distintos.
A Interoperabilidade de código nativo e intrínsecos de hardware são de baixo
custo e alta fidelidade (API bruta e acesso de instrução).
O Código é portátil entre plataformas (sistema operacional e arquitetura de chip),
enquanto a segmentação por plataforma permite especialização e otimização.
A Adaptabilidade entre domínios de programação (nuvem, cliente, jogos) está
habilitada com implementações especializadas do modelo de programação de uso
geral.
Os Padrões do setor, como OpenTelemetry e gRPC, são favorecidos em relação às
soluções sob medida.

O .NET é mantido pela Microsoft e pela comunidade. Ele é atualizado regularmente para
garantir que os usuários implantem aplicativos seguros e confiáveis na produção.

Componentes
O .NET inclui os seguintes componentes:

Runtime – executa o código do aplicativo.


Bibliotecas – fornece funcionalidade de utilitário como Análise JSON.
Compilador – compila o código-fonte C# (e outros idiomas) no código executável
(runtime).
SDK e outras ferramentas - habilita a criação e o monitoramento dos aplicativos
com fluxos de trabalho modernos.
Pilhas de aplicativos - como ASP.NET Core e Windows Forms, que permitem a
gravação dos aplicativos.

O runtime, as bibliotecas e os idiomas são os pilares da pilha do .NET. Componentes de


nível superior, como ferramentas .NET e pilhas de aplicativos, como ASP.NET Core,
baseiam-se nesses pilares. O C# é a principal linguagem de programação do .NET e
grande parte do .NET é escrito em C#.

O C# é orientado aos objetos e o tempo de execução oferece suporte à orientação aos


objetos. O C# requer coleta de lixo e o tempo de execução fornece um coletor de lixo
de rastreamento. As bibliotecas (e também as pilhas de aplicativos) moldam esses
recursos em conceitos e modelos de objeto que permitem que os desenvolvedores
escrevam algoritmos de forma produtiva nos fluxos de trabalho intuitivos.

As principais bibliotecas expõem milhares de tipos, muitos dos quais se integram e


alimentam o idioma C#. Por exemplo, a instrução foreach do C#’s permite que você
enumere coleções arbitrárias. As otimizações baseadas em padrões permitem que
coleções como List<T> sejam processadas de forma simples e eficiente. Você pode
deixar o gerenciamento de recursos para coleta de lixo, mas a limpeza do prompt é
possível pelo IDisposable e suporte direto à linguagem na instrução using .

O suporte para fazer várias coisas ao mesmo tempo é fundamental para praticamente
todas as cargas de trabalho. Isso pode ser feito por aplicativos de clientes que realizam
processamento em segundo plano e mantêm a interface do usuário responsiva, serviços
que lidam com milhares de solicitações simultâneas, dispositivos que respondem a uma
infinidade de estímulos simultâneos ou máquinas de alta potência que paralelizam o
processamento de operações de computação intensiva. O suporte à programação
assíncrona é um recurso de primeira classe do idioma de programação C#, que fornece
as palavras-chave async e await que facilitam a gravação e a composição das
operações assíncronas, aproveitando todos os benefícios de todos os constructos de
fluxo de controle que o idioma tem a oferecer.

O sistema de tipos oferece uma amplitude significativa, atendendo igualmente à


segurança, à descritividade, ao dinamismo e à interoperabilidade nativa. Em primeiro
lugar, o sistema de tipos permite um modelo de programação orientado a objetos. Ele
inclui tipos, herança (classe base única), interfaces (incluindo implementações de
métodos padrão) e envio de métodos virtuais para fornecer um comportamento
sensato para todas as camadas de tipos que a orientação a objetos permite. Os Tipos
genéricos são um recurso generalizado que permite que você especialize classes para
um ou mais tipos.

O runtime do .NET fornece gerenciamento automático de memória por um coletor de


lixo. Para qualquer linguagem, seu modelo de gerenciamento de memória é
provavelmente sua característica mais definidora. Isso é verdadeiro para os idiomas
.NET. O .NET tem um GC de autoajuste e rastreamento. Seu objetivo é proporcionar uma
operação "hands off" no caso geral e, ao mesmo tempo, oferecer opções de
configuração para cargas de trabalho mais extremas. O GC atual é o resultado de muitos
anos de investimentos e aprendizados de uma infinidade de cargas de trabalho.

Os tipos de valor e blocos de memória alocados em pilha oferecem controle mais direto
e de baixo nível sobre os dados e a interoperabilidade de plataforma nativa, ao
contrário dos tipos gerenciados pelo GC do NET. A maioria dos tipos primitivos no .NET,
como tipos inteiros, são tipos de valor e os usuários podem definir seus próprios tipos
com semântica semelhante. Os tipos de valor têm suporte total pelo sistema genérico
do NET, o que significa que tipos genéricos como List<T> podem fornecer
representações de memória simples e sem a sobrecarga das coleções dos tipos de valor.

A Reflexão é um paradigma de "programas como dados", permitindo que uma parte de


um programa consulte e invoque outra dinamicamente, em termos de assemblies, tipos
e membros. É particularmente útil para modelos e ferramentas de programação com
limite tardio.

As exceções são o principal modelo de tratamento de erros no .NET. As exceções têm o


benefício de que as informações de erro não precisam ser representadas nas assinaturas
de método ou tratadas por todos os métodos. O tratamento adequado de exceções é
essencial para a confiabilidade do aplicativo. Para evitar que seu aplicativo falhe, você
pode manipular intencionalmente as exceções esperadas no seu código. Um aplicativo
com falha é mais confiável e diagnosticável do que um aplicativo com comportamento
indefinido.

As pilhas dos aplicativos, como ASP.NET Core e Windows Forms, baseiam-se nas e
aproveitam as bibliotecas de baixo nível, idioma e runtime. As pilhas dos aplicativos
definem a maneira como os aplicativos são construídos e seu ciclo de vida de execução.

O SDK e outras ferramentas permitem uma experiência moderna para desenvolvedores,


tanto em uma área de trabalho do desenvolvedor quanto para a integração contínua
(CI). A experiência do desenvolvedor moderno inclui a capacidade de criar, analisar e
testar códigos. Geralmente, os projetos do .NET podem ser criados por um único
comando dotnet build , que orquestra a restauração dos pacotes NuGet e a criação de
dependências.

O NuGet é o gerenciador de pacotes para o .NET. Ele contém centenas de milhares de


pacotes que implementam a funcionalidade para muitos cenários. A maioria dos
aplicativos depende ds pacotes NuGet para algumas funcionalidades. A Galeria NuGet
é mantida pela Microsoft.

Gratuito e software livre


O .NET é gratuito, de código aberto e é um projeto da .NET Foundation . O .NET é
mantido pela Microsoft e pela comunidade no GitHub em vários repositórios .

A fonte e os binários do .NET são licenciados com a licença MIT . As licenças adicionais
se aplicam no Windows .

Suporte
O .NET é suportado por várias organizações que trabalham para garantir que o .NET
possa ser executado em vários sistemas operacionais e seja mantido atualizado. Ele
pode ser usado em arquiteturas Arm64, x64 e x86.

As novas versões do .NET são lançadas anualmente em novembro, de acordo com


nossas versões e políticas de suporte. Ele é atualizado mensalmente na Terça-feira do
Patch (segunda terça-feira), normalmente às 10h, horário do Pacífico.

Ecossistema do .NET
Há várias variantes do .NET, cada uma dando suporte a um tipo diferente de aplicativo.
O motivo de várias variantes é parte histórico e parte técnico.

Implementações do .NET

.NET Framework -- o .NET original. Ele fornece acesso aos amplos recursos do
Windows e do Windows Server. Ele tem suporte ativo, em manutenção.
Mono – a comunidade original e o .NET de software livre. Uma implementação
multiplataforma do .NET Framework. Suportado ativamente para Android, iOS e
WebAssembly.
.NET (Core) -- .NET moderno. Uma implementação multiplataforma e de código
aberto do .NET, repensada para a era da nuvem e que permanece
significativamente compatível com o .NET Framework. Suportado ativamente para
Linux, macOS e Windows.

Próximas etapas
Escolher um tutorial do .NET
Experimente o .NET em seu navegador
Fazer um tour pelo C#
Fazer um tour pelo F#

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Criar aplicativos com o .NET
Artigo • 01/02/2024

O .NET tem suporte para criar diversos tipos de aplicativos, incluindo cliente, nuvem e
jogos.

Aplicativos na nuvem
.NET Aspire
Funções sem servidor
Web e microsserviços

Aplicativos cliente
Móvel
Jogos
Aplicativos da área de trabalho

Outros tipos de aplicativo


Aplicativos de console
Internet das coisas (IoT)
Aprendizado de máquina
Serviços do Windows

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Estratégia de linguagem do Microsoft
.NET
Artigo • 05/06/2023

A Microsoft oferece três linguagens na plataforma .NET – C#, F# e Visual Basic. Neste
artigo, você aprenderá sobre nossa estratégia para cada linguagem. Procure links para
artigos adicionais sobre como essas estratégias nos orientam e maneiras de saber mais
sobre cada linguagem.

C#
O C# é uma linguagem de uso geral multiplataforma que torna os desenvolvedores
produtivos ao escrever um código de alto desempenho. Com milhões de
desenvolvedores, o C# é a linguagem .NET mais popular. O C# tem amplo suporte no
ecossistema e em todas as cargas de trabalho do .NET. Com base em princípios
orientados a objetos, ele incorpora muitos recursos de outros paradigmas,
especialmente a programação funcional. Recursos de baixo nível dão suporte a cenários
de alta eficiência sem escrever código não seguro. A maioria dos runtimes e bibliotecas
do .NET são escritos em C# e avanços no C# geralmente beneficiam todos os
desenvolvedores do .NET.

Nossa estratégia para o C#


Continuaremos desenvolvendo o C# para atender às necessidades em constante
mudança dos desenvolvedores e continuaremos sendo uma linguagem de programação
de última geração. Inovaremos de forma ávida e ampla em colaboração com as equipes
responsáveis pelas bibliotecas do .NET, pelas ferramentas de desenvolvedor e pelo
suporte à carga de trabalho, ao mesmo tempo em que temos o cuidado de permanecer
dentro manter o espírito da linguagem. Reconhecendo a diversidade de domínios em
que o C# está sendo usado, preferiremos aprimoramentos de linguagem e desempenho
que beneficiem todos ou a maioria dos desenvolvedores e mantenham um alto
compromisso com a compatibilidade com versões anteriores. Continuaremos
capacitando o ecossistema mais amplo do .NET e aumentando seu papel no futuro do
C#, mantendo a administração de decisões de design.

Você pode ler mais sobre como essa estratégia nos guia no Guia de C#.

F#
O F# é uma linguagem sucinta, robusta e de alto desempenho baseada em expressões e
imutável por padrão. Ele se concentra no poder expressivo, na simplicidade e na
elegância e é usado por milhares de desenvolvedores que apreciam sua abordagem
pragmática ao .NET. O F# oferece todo o poder do .NET e funciona bem com o C# para
soluções de linguagem mista. A comunidade faz contribuições significativas para o
compilador e o runtime, bem como uma ampla matriz de ferramentas e estruturas F#.

Nossa estratégia para o F#


Impulsionaremos a evolução do F# e daremos suporte ao ecossistema F# com liderança
e governança de linguagem. Incentivaremos os contribuições da comunidade para
melhorar a experiência do desenvolvedor e da linguagem F#. Continuaremos contando
com a comunidade para fornecer bibliotecas importantes, ferramentas de
desenvolvedor e suporte à carga de trabalho. À medida que a linguagem evoluir, o F#
dará suporte a melhorias na plataforma .NET e manterá a interoperabilidade com novos
recursos de C#. Trabalharemos na linguagem, em ferramentas e na documentação para
reduzir a barreira à entrada no F# para novos desenvolvedores e organizações, além de
ampliar seu alcance em novos domínios.

Você pode ler mais sobre como essa estratégia nos guia no Guia de F#.

Visual Basic
O VB (Visual Basic) tem um longo histórico como uma linguagem acessível, favorecendo
a clareza em vez da brevidade. Suas centenas de milhares de desenvolvedores estão
concentrados em torno das cargas de trabalho tradicionais de cliente baseadas no
Windows, em que o VB é pioneiro há muito tempo em termos de grandes ferramentas e
facilidade de uso. Os desenvolvedores do VB de hoje se beneficiam de uma linguagem
estável e madura orientada a objetos, emparelhada com um ecossistema .NET crescente
e melhorias contínuas de ferramentas. Algumas cargas de trabalho do .NET não têm
suporte no VB e é comum que os desenvolvedores do VB usem o C# para esses
cenários.

Nossa estratégia para o Visual Basic


Garantiremos que o Visual Basic continue sendo uma linguagem simples e acessível
com um design estável. As bibliotecas principais do .NET (como BCL) darão suporte ao
VB e muitas das melhorias no Runtime e nas bibliotecas do .NET beneficiarão
automaticamente o VB. Quando o runtime do C# ou do .NET introduzir novos recursos
que exigiriam suporte à linguagem, o VB geralmente adotará uma abordagem somente
de consumo e evitará uma nova sintaxe. Não planejamos estender o Visual Basic para
novas cargas de trabalho. Continuaremos investindo na experiência no Visual Studio e
interoperando com o C#, especialmente nos cenários principais do VB, como Windows
Forms e bibliotecas.

Você pode ler mais sobre como essa estratégia nos guia no Guia do Visual Basic.

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Implementações do .NET
Artigo • 22/11/2023

Um aplicativo .NET é desenvolvido para uma ou mais implementações do .NET. As


implementações do .NET incluem o .NET Framework, o .NET 5+ (e o .NET Core) e o
Mono.

Cada implementação do .NET inclui os seguintes componentes:

Um ou mais runtimes, por exemplo, o CLR do .NET Framework e do .NET 8.


Uma biblioteca de classes, por exemplo, as bibliotecas de classes base do .NET
Framework e do .NET 8.
Opcionalmente, uma ou mais estruturas de aplicativo, por exemplo, ASP.NET ,
Windows Forms e WPF (Windows Presentation Foundation), são incluídas no .NET
Framework e no .NET 5+.
Opcionalmente, ferramentas de desenvolvimento. Algumas ferramentas de
desenvolvimento são compartilhadas entre várias implementações.

Há quatro implementações do .NET compatíveis com a Microsoft:

.NET 6 e versões posteriores


.NET Framework
Mono
UWP

O .NET, anteriormente conhecido como .NET Core, é atualmente a implementação


primária. O .NET 8 é criado em uma única base de código que dá suporte a várias
plataformas e muitas cargas de trabalho, como aplicativos de desktop do Windows e
aplicativos de console multiplataforma, serviços de nuvem e sites. Algumas cargas de
trabalho, como ferramentas de build do .NET WebAssembly, estão disponíveis como
instalações opcionais.

.NET 5 e versões posteriores


O .NET, anteriormente conhecido como .NET Core, é uma implementação
multiplataforma do .NET projetada para lidar com cargas de trabalho de servidor e
nuvem em escala. Ele também dá suporte a outras cargas de trabalho, incluindo
aplicativos de desktop. Ele é executado no Windows, no Linux e no macOS. Ele
implementa o .NET Standard, portanto, o código direcionado para o .NET Standard
pode ser executado no .NET. O ASP.NET Core , o Windows Forms e o WPF (Windows
Presentation Foundation) são todos executados no .NET.
O .NET 8 é a versão mais recente dessa implementação do .NET.

Para saber mais, consulte os recursos a seguir:

Introdução ao .NET
.NET vs. .NET Framework para aplicativos de servidor
.NET 5+ e .NET Standard

.NET Framework
O.NET Framework é a implementação original do .NET que existe desde 2002. As
versões 4.5 e posteriores implementam o .NET Standard, assim, o código que se destina
ao .NET Standard pode ser executado nessas versões do .NET Framework. Ele contém
APIs adicionais específicas do Windows, como APIs para desenvolvimento de área de
trabalho do Windows com o Windows Forms e o WPF. O .NET Framework é otimizado
para a compilação de aplicativos da área de trabalho do Windows.

Para saber mais, confira Guia do .NET Framework.

Mono
O Mono é uma implementação do .NET que é usada principalmente quando um
pequeno runtime é necessário. É o runtime que executa os aplicativos Xamarin no
Android, no macOS, no iOS, no tvOS e no watchOS e ele se concentra principalmente
em um volume pequeno. O Mono também é plataforma para jogos criados com o
mecanismo Unity.

Ele dá suporte a todas as versões do .NET Standard publicadas atualmente.

Historicamente, o Mono implementava a maior API do .NET Framework e emulava


algumas das funcionalidades mais populares do Unix. Às vezes, ele é usado para
executar aplicativos .NET que dependem desses recursos no Unix.

O Mono normalmente é usado com um compilador Just-In-Time, mas ele também


apresenta um compilador estático completo (compilação Ahead Of Time) que é usado
em plataformas como iOS.

Para saber mais, confira a documentação do Mono .

Plataforma Universal do Windows (UWP)


A UWP é uma implementação do .NET que é usada para criar aplicativos do Windows
modernos e sensíveis ao toque, bem como software para a IoT (Internet das Coisas). Ela
foi projetada para unificar os diferentes tipos de dispositivos que você pode ter como
destinho, incluindo PCs, tablets, telefones e até mesmo o Xbox. A UWP fornece muitos
serviços, como um repositório centralizado de aplicativos, um ambiente de execução
(AppContainer) e um conjunto de APIs do Windows para usar em vez das APIS do
Win32 (WinRT). Os aplicativos podem ser escritos em C++, C#, Visual Basic e JavaScript.

Para saber mais, confira Introdução à Plataforma Universal do Windows.

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Bibliotecas de classes do .NET
Artigo • 10/05/2023

As bibliotecas de classe são o conceito de biblioteca compartilhada para .NET. Elas


permitem que você componentize funcionalidades úteis em módulos que podem ser
usados por vários aplicativos. Elas também podem ser usadas como um meio de
carregar a funcionalidade não necessária ou não conhecida na inicialização do
aplicativo. Bibliotecas de classe são descritas usando o formato de arquivo do Assembly
.NET.

Há três tipos de bibliotecas de classes que você pode usar:

Bibliotecas de classe específicas da plataforma têm acesso a todas as APIs em


uma determinada plataforma (por exemplo, .NET Framework no Windows, Xamarin
iOS), mas só podem ser usadas por aplicativos e bibliotecas que direcionam essa
plataforma.
Bibliotecas de classe portáteis têm acesso a um subconjunto de APIs e podem ser
usadas por aplicativos e bibliotecas voltados para várias plataformas.
Bibliotecas de classe .NET Standard são uma fusão do conceito de biblioteca
portátil e específica da plataforma em um único modelo que fornece o melhor dos
dois mundos.

Bibliotecas de classes específicas da plataforma


Bibliotecas específicas da plataforma estão associadas a uma única plataforma .NET (por
exemplo, .NET Framework no Windows) e, portanto, podem levar a dependências
significativas em um ambiente de execução conhecido. Esse ambiente expõe um
conjunto conhecido de APIs (.NET e APIs de SO) e mantêm e expõe o estado esperado
(por exemplo, o Registro do Windows).

Os desenvolvedores que criam bibliotecas específicas de plataforma podem aproveitar


ao máximo a plataforma subjacente. As bibliotecas só serão executadas na plataforma
específica, fazendo verificações de plataforma ou outras formas de código condicional
desnecessários (módulo de códigos de fornecimento únicos para várias plataformas).

Bibliotecas específicas da plataforma são o tipo de biblioteca de classes principal para o


.NET Framework. Mesmo que outras implementações do .NET tenham surgido, as
bibliotecas específicas da plataforma permaneceram o tipo de biblioteca dominante.

Bibliotecas de classes portáteis


Há suporte para bibliotecas portáteis em várias implementações do .NET. Elas ainda
podem assumir dependências em um ambiente de execução conhecido, no entanto, o
ambiente é sintético e é gerado pela intersecção de um conjunto de implementações de
.NET concretas. Suposições de plataforma e APIs expostas são um subconjunto que
estaria disponível em uma biblioteca específica da plataforma.

Você escolhe uma configuração de plataforma ao criar uma biblioteca portátil. A


configuração de plataforma são os conjuntos de plataformas aos quais você precisa dar
suporte (por exemplo, .NET Framework 4.5+, Windows Phone 8.0+). Quanto mais
plataformas você escolher dar suporte, menos suposições de plataforma terá que fazer
e menor será o denominador comum. Essa característica pode ser confusa a princípio, já
que as pessoas geralmente pensam que "mais é melhor", mas descobrem que mais
plataformas com suporte resultam em menos APIs disponíveis.

Muitos desenvolvedores de biblioteca mudam de produzir várias bibliotecas específicas


de plataforma de uma origem (usando as diretivas de compilação condicional) para
bibliotecas portáteis. Há várias abordagens para acessar a funcionalidade específica
da plataforma nas bibliotecas portáteis com a técnica bait-and-switch, a mais
amplamente aceita neste momento.

Bibliotecas de classe do .NET Standard


As bibliotecas do .NET Standard são uma substituição dos conceitos de bibliotecas
específicas da plataforma e portáteis. Elas são específicas da plataforma no sentido de
que expõem toda a funcionalidade da plataforma subjacente (sem plataformas
sintéticas ou interseções de plataforma). Elas são portáteis no sentido de que funcionam
em todas as plataformas de suporte.

O .NET Standard expõe um conjunto de contratos de biblioteca. As implementações do


.NET devem dar suporte a cada contrato, completamente ou não dar nenhum suporte.
Cada implementação, portanto, dá suporte a um conjunto de contratos do .NET
Standard. O resultado é que há suporte para cada biblioteca de classes do .NET
Standard nas plataformas compatíveis com suas dependências de contrato.

O .NET Standard não expõe toda a funcionalidade do .NET Framework (e isso tampouco
é uma meta), no entanto, as bibliotecas expõem muito mais APIs que as Bibliotecas de
classes portáteis.

As seguintes implementações dão suporte a bibliotecas .NET Standard:

.NET Core
.NET Framework
Mono
Plataforma Universal do Windows (UWP)

Para obter mais informações, confira .NET Standard.

Bibliotecas de classes do Mono


Há suporte para as bibliotecas de classe no Mono, incluindo os três tipos de bibliotecas
descritas anteriormente. Mono é frequentemente visto como uma implementação entre
plataformas do .NET Framework. Em parte, isso ocorre porque bibliotecas do .NET
Framework específicas da plataforma podem ser executadas no runtime Mono sem
modificação ou recompilação. Essa característica estava em vigor antes da criação de
bibliotecas de classes portáteis, portanto, era uma opção óbvia para habilitar a
portabilidade binária entre o .NET Framework e o Mono (embora ele funcionasse apenas
em uma direção).
.NET Standard
Artigo • 15/11/2023

O .NET Standard é uma especificação formal de APIs do .NET que estão disponíveis em
todas as implementações do .NET. A motivação por trás do .NET Standard era
estabelecer maior uniformidade no ecossistema do .NET. O .NET 5 e versões posteriores
adotam uma abordagem diferente para estabelecer a uniformidade que elimina a
necessidade do .NET Standard na maioria dos cenários. No entanto, se você quiser
compartilhar código entre .NET Framework e qualquer outra implementação do .NET,
como o .NET Core, sua biblioteca deverá ter como destino o .NET Standard 2.0.
Nenhuma nova versão do .NET Standard será lançada , mas o .NET 5 e todas as
versões posteriores continuarão dando suporte ao .NET Standard 2.1 e versões
anteriores.

Para obter informações sobre como escolher entre o .NET 5+ e o .NET Standard, confira
.NET 5+ e .NET Standard mais adiante neste artigo.

Versões do .NET Standard


O .NET Standard tem controle de versão. Cada nova versão adiciona mais APIs. Quando
uma biblioteca é criada em relação a uma determinada versão do .NET Standard, ela
pode ser executada em qualquer implementação do .NET que implemente essa versão
do .NET Standard (ou superior).

Ter como alvo uma versão mais alta do .NET Standard permite que uma biblioteca use
mais APIs, mas ela só poderá ser usada em versões mais recentes do .NET. Ter como
alvo uma versão inferior reduz as APIs disponíveis, mas a biblioteca poderá ser
executada em mais lugares.

Selecione a versão do .NET Standard

1.0

O .NET Standard 1.0 tem 7.949 das 37.118 APIs disponíveis.

Implementação do .NET Suporte à versão

.NET e .NET Core 1.0, 1.1, 2.0, 2.1, 2.2, 3.0, 3.1, 5.0, 6.0, 7.0, 8.0

.NET Framework 4.5, 4.5.1, 4.5.2, 4.6, 4.6.1, 4.6.2, 4.7, 4.7.1, 4.7.2, 4.8, 4.8.1
Implementação do .NET Suporte à versão

Mono 4.6, 5.4, 6.4

Xamarin.iOS 10.0, 10.14, 12.16

Xamarin.Mac 3.0, 3.8, 5.16

Xamarin.Android 7.0, 8.0, 10.0

Plataforma Universal do Windows 8.0, 8.1, 10.0, 10.0.16299, a ser determinado

Unity 2018.1

Para mais informações, confira .NET Standard 1.0 . Para obter uma tabela
interativa, consulte Versões do .NET Standard .

Para qual versão do .NET Standard direcionar


Recomendamos que você tenha como destino o .NET Standard 2.0, a menos que precise
dar suporte a uma versão anterior. A maioria das bibliotecas de uso não deve precisar
de APIs fora do .NET Standard 2.0. O .NET standard 2.0 é compatível com todas as
plataformas modernas e é a maneira recomendada de dar suporte a várias plataformas
com um destino.

Se você precisar dar suporte ao .NET Standard 1.x, recomendamos que você também
tenha como destino o .NET Standard 2.0. O .NET standard 1.x é distribuído como um
conjunto granular de pacotes do NuGet, que cria um grande grafo de dependência de
pacote e resulta em os desenvolvedores baixarem muitos pacotes durante o build. Para
mais informações, confira Direcionamento multiplataforma e .NET 5+ e .NET Standard
posteriormente neste artigo.

Regras de controle de versão do .NET Standard


Há duas regras principais de controle de versão:

Aditivo: as versões do .NET Standard são círculos logicamente concêntricos:


versões mais recentes incorporam todas as APIs das versões anteriores. Não há
alterações interruptivas entre as versões.
Imutável: após o envio, as versões do .NET Standard serão congeladas.

Não haverá novas versões do .NET Standard após a 2.1. Para mais informações, confira
.NET 5+ e .NET Standard mais adiante neste artigo.
Especificação
A especificação do .NET Standard é um conjunto padronizado de APIs. A especificação é
mantida por implementadores do .NET, especificamente Microsoft (inclui o .NET
Framework, .NET Core e Mono) e Unity.

Artefatos oficiais
A especificação oficial é um conjunto de arquivos .cs que definem as APIs que fazem
parte do padrão. O diretório ref no (agora arquivado) repositório dotnet/standard
define as APIs do .NET Standard.

O metapacote NETStandard.Library (de origem ) descreve o conjunto de bibliotecas


que define (parcialmente) uma ou mais versões do .NET Standard.

Um componente específico, como o System.Runtime , descreve:

Parte do .NET Standard (apenas seu escopo).


Várias versões do .NET Standard, para esse escopo.

Artefatos derivados são fornecidos para habilitar leitura mais conveniente e permitir
determinados cenários do desenvolvedor (por exemplo, usando um compilador).

Lista de APIs em markdown .


Assemblies de referência, distribuídos como pacotes NuGet e referenciados pelo
metapacote NETStandard.Library .

Representação de pacote
O meio de distribuição principal dos assemblies de referência do .NET Standard são os
pacotes NuGet. As implementações são entregues de várias formas, apropriadas para
cada implementação do .NET.

Pacotes NuGet são direcionados a uma ou mais estruturas. Os pacotes do .NET Standard
são direcionados à estrutura do ".NET Standard". É possível direcionar a estrutura do
.NET Standard usando o netstandard TFM compacto (por exemplo, netstandard1.4 ).
Bibliotecas destinadas à execução em várias implementações do .NET devem ter essa
estrutura como destino. Para obter o mais amplo conjunto de APIs, direcione
netstandard2.0 , pois o número de APIs disponíveis mais do que dobrou entre o .NET

Standard 1.6 e 2.0.

O metapacote NETStandard.Library faz referência ao conjunto completo de pacotes


NuGet que definem o .NET Standard. A maneira mais comum de direcionar para
netstandard é fazer referência a esse metapacote. Ele descreve e fornece acesso às ~40

bibliotecas .NET e APIs associadas, que definem a .NET Standard. Você pode referenciar
pacotes adicionais destinados a netstandard para obter acesso a APIs adicionais.

Controle de versão
A especificação não é única, mas um conjunto com controle de versão linear de APIs. A
primeira versão do padrão estabelece um conjunto de linhas de base de APIs. As
versões subsequentes adicionam APIs e herdam APIs definidas por versões anteriores.
Não há nenhuma provisão estabelecida para a remoção de APIs do Standard.

O .NET Standard não é específico a nenhuma implementação do .NET, nem corresponde


ao esquema de controle de versão de nenhuma dessas implementações.

Como observado anteriormente, não haverá novas versões do .NET Standard após a 2.1.

.NET Standard de destino


Você pode criar .NET Standard Libraries usando uma combinação de estrutura
netstandard e o metapacote NETStandard.Library .

Modo de compatibilidade do .NET framework


O modo de compatibilidade do .NET Framework foi introduzido a partir do .NET
Standard 2.0. Esse modo de compatibilidade permite que os projetos do .NET Standard
referenciem as bibliotecas do .NET Framework como se elas fossem compiladas para o
.NET Standard. Fazer referência a bibliotecas do .NET Framework não funciona para
todos os projetos, como as bibliotecas que usam APIs do WPF (Windows Presentation
Foundation).

Para obter mais informações, veja .NET Framework compatibility mode (Modo de
compatibilidade do .NET Framework).

Bibliotecas do .NET standard e Visual Studio


Para criar bibliotecas do .NET Standard no Visual Studio, verifique se tem o Visual Studio
2022, Visual Studio 2019 ou o Visual Studio 2017 versão 15.3 ou posterior instalado no
Windows, ou Visual Studio para Mac versão 7.1 ou posterior instalado no macOS.

Se você precisar apenas consumir as bibliotecas do .NET Standard 2.0 em seus projetos,
também será possível fazer isso no Visual Studio 2015. No entanto, é necessário ter o
NuGet cliente 3.6 ou posterior instalado. É possível baixar o cliente do NuGet para Visual
Studio 2015 na página downloads do NuGet .

.NET 5+ e .NET Standard


O .NET 5, o .NET 6, o .NET 7 e o .NET 8 são produtos individuais com um conjunto
uniforme de funcionalidades e APIs que podem ser usadas para aplicativos da área de
trabalho do Windows e aplicativos de console multiplataforma, serviços de nuvem e
sites. Os TFMs do .NET 8, por exemplo, refletem essa ampla gama de cenários:

net8.0

Esse TFM é para código que é executado em todos os lugares. Com algumas
exceções, ele inclui apenas tecnologias que funcionam entre plataformas. No caso
do código do .NET 8, net8.0 substitui os TFMs netcoreapp e netstandard .

net8.0-windows

Este é um exemplo de um TFM específico do SO que adiciona funcionalidade


específica do SO a tudo a que net8.0 se refere.

Comparação entre o .NET 8.0 e .NET Standard em


cenários de direcionamento
No caso do código direcionado ao netstandard , não é necessário alterar o TFM para
net8.0 ou para um TFM posterior. O .NET 8 implementa o .NET Standard 2.1 e versões

anteriores. O único motivo de redirecionamento do .NET Standard para o .NET 8 e


versões posteriores é obter acesso a mais recursos de runtime, recursos de linguagem
ou APIs. Por exemplo, para usar o C# 9, você precisa ter como alvo o .NET 5 ou uma
versão posterior. Você pode ter fazer o direcionamento múltiplo do .NET 8 e do .NET
Standard para obter acesso aos recursos mais recentes e ainda ter sua biblioteca
disponível para outras implementações do .NET.

Aqui estão algumas diretrizes para o novo código para .NET 5+:

Componentes do aplicativo

Se você está usando bibliotecas para dividir um aplicativo em vários componentes,


recomendamos o direcionamento para o net8.0 . Para simplificar, é melhor manter
todos os projetos que compõem seu aplicativo na mesma versão do .NET. Em
seguida, você pode presumir os mesmos recursos BCL em todos os lugares.
Bibliotecas reutilizáveis

Se você estiver criando bibliotecas reutilizáveis que planeja enviar no NuGet,


considere o equilíbrio entre alcance e conjunto de recursos disponíveis. O .NET
Standard 2.0 é a versão mais recente compatível com .NET Framework, portanto,
dá um bom alcance com um conjunto de recursos bastante grande. Não
recomendamos ter o .NET Standard 1.x como alvo, pois você limitaria o conjunto
de recursos disponível para um aumento mínimo de alcance.

Caso não precise dar suporte ao .NET Framework, use o .NET Standard 2.1 ou o
.NET 8. Recomendamos que você ignore o .NET Standard 2.1 e vá direto para o
.NET 8. As bibliotecas mais usadas terão vários alvos para .NET Standard 2.0 e .NET
5+. O suporte ao .NET Standard 2.0 oferece o maior alcance, enquanto o suporte
ao .NET 5+ garante que você possa aproveitar os recursos mais recentes da
plataforma para clientes que já estão no .NET 5+.

Problemas do .NET Standard


Aqui estão alguns problemas com o .NET Standard que ajudam a explicar por que o
.NET 5 e versões posteriores são a melhor maneira de compartilhar código entre
plataformas e cargas de trabalho:

Lentidão para adicionar novas APIs

O .NET Standard foi criado como um conjunto de API a que todas as


implementações do .NET teriam que dar suporte, portanto, houve um processo de
análise de propostas para adicionar novas APIs. A meta era padronizar apenas AS
APIs que poderiam ser implementadas em todas as plataformas .NET atuais e
futuras. O resultado foi que, se um recurso perdia uma versão específica, poderia
ser preciso aguardar alguns anos antes de ele ser adicionado a uma versão do
Standard. Então você aguardaria ainda mais para que a nova versão do .NET
Standard tivesse amplo suporte.

Solução no .NET 5+: quando um recurso é implementado, ele já está disponível


para cada aplicativo e biblioteca do .NET 5+ porque a base de código é
compartilhada. Como não há diferença entre a especificação da API e sua
implementação, você pode aproveitar os novos recursos com muito mais rapidez
do que com o .NET Standard.

Controle de versão complexo

A separação da especificação da API de suas implementações resulta em


mapeamento complexo entre versões de especificação de API e versões de
implementação. Essa complexidade é evidente na tabela mostrada anteriormente
neste artigo e nas instruções de como interpretá-la.

Solução no .NET 5+: não há separação entre uma especificação de API do .NET 5+
e sua implementação. O resultado é um esquema de TFM simplificado. Há um
prefixo de TFM para todas as cargas de trabalho: net8.0 é usado para bibliotecas,
aplicativos de console e aplicativos Web. A única variação é um sufixo que
especifica APIs específicas da plataforma para uma plataforma específica, como
net8.0-windows . Graças a essa convenção de nomenclatura de TFM, você pode

facilmente determinar se um dado aplicativo pode usar uma biblioteca específica.


Nenhuma tabela de equivalentes de número de versão, como a do .NET Standard,
é necessária.

Exceções sem suporte da plataforma em tempo de execução

O .NET Standard expõe APIs específicas da plataforma. Seu código pode ser
compilado sem erros e parecer ser portátil para qualquer plataforma, mesmo que
não seja portátil. Quando ele é executado em uma plataforma que não tem uma
implementação para uma determinada API, você recebe erros em tempo de
execução.

Solução no .NET 5+: os SDKs do .NET 5+ incluem analisadores de código


habilitados por padrão. O analisador de compatibilidade da plataforma detecta o
uso não intencional de APIs que não têm suporte nas plataformas em que você
pretende executar. Para obter mais informações, confira Analisador de
Compatibilidade da Plataforma.

.NET Standard não preterido


O .NET Standard ainda é necessário para bibliotecas que podem ser usadas por várias
implementações do .NET. Recomendamos que você tenha como alvo o .NET Standard
nos seguintes cenários:

Use netstandard2.0 para compartilhar código entre o .NET Framework e todas as


outras implementações do .NET.
Use netstandard2.1 para compartilhar código entre Mono, Xamarin e .NET Core
3.x.

Confira também
Versões do .NET Standard (origem)
Versões do .NET Standard (interface do usuário interativa)
Cria uma biblioteca .NET Standard
Direcionamento multiplataforma

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Versões e suporte para .NET
Artigo • 22/11/2023

A Microsoft envia versões principais, versões secundárias e atualizações de manutenção


(patches) para .NET. Este artigo explica os tipos de versão, as atualizações de
manutenção, as bandas de recursos do SDK, os períodos de suporte e as opções de
suporte.

7 Observação

Para obter informações sobre controle de versão e suporte para o .NET Framework,
confira Ciclo de vida do .NET Framework.

Tipos de versão
Informações sobre o tipo de cada versão são codificadas no número de versão seguindo
o padrão principal.secundária.patch.

Por exemplo:

O .NET 6 e o NET 7 são versões principais.


O .NET Core 3.1 é a primeira versão secundária após a versão principal .NET Core
3.0.
O .NET Core 5.0.15 é o décimo quinto patch do .NET 5.

Para obter uma lista das versões lançadas do .NET e informações sobre a frequência
com que o .NET é fornecido, confira a Política de suporte .

Versões principais
As versões principais incluem novos recursos, nova área de superfície de API pública e
correções de bugs. Exemplos incluem o .NET 6 e o .NET 7. Devido à natureza das
alterações, espera-se que essas versões tenham alterações interruptivas. As versões
principais são instaladas lado a lado com as versões principais anteriores.

Versões secundárias
As versões secundárias também incluem novos recursos, área de superfície de API
pública e correções de bugs, além de também poderem ter alterações interruptivas. Um
exemplo é o .NET Core 3.1. A diferença entre essas e as versões principais é que a
magnitude das alterações é inferior. Um aplicativo atualizado do .NET Core 3.0 para o
3.1 tem um salto menor para avançar. Versões secundárias são instaladas lado a lado
com versões secundárias anteriores.

Atualizações de manutenção
As atualizações de manutenção (patches) são enviadas quase todos os meses e
carregam correções de bugs relacionadas ou não à segurança. Por exemplo, o .NET 5.0.8
era a oitava atualização do .NET 5. Quando essas atualizações incluem correções de
segurança, elas são lançadas na "terça-feira do patch", que ocorrem sempre na segunda
terça-feira do mês. Espera-se que as atualizações de manutenção mantenham a
compatibilidade. Do .NET Core 3.1 em diante, as atualizações de manutenção passaram
a ser upgrades que removem a atualização anterior. Por exemplo, a atualização de
manutenção mais recente da versão 3.1 remove a atualização 3.1 anterior após a
instalação bem-sucedida.

Faixas de recursos (somente SDK)


O controle de versão do SDK do .NET funciona um pouco diferente do runtime do .NET.
Para se alinhar com as novas versões do Visual Studio, as atualizações do SDK do .NET
às vezes incluem novos recursos ou novas versões de componentes como MSBuild e
NuGet. Esses novos recursos ou componentes podem ser incompatíveis com as versões
que foram enviadas em atualizações anteriores do SDK para a mesma versão principal
ou secundária.

Para diferenciar essas atualizações, o SDK do .NET usa o conceito de faixas de recursos.
Por exemplo, o primeiro SDK do .NET 5 foi o 5.0.100. Esta versão corresponde à faixa de
recursos 5.0.1xx. As faixas de recursos são definidas no grupo das centenas da terceira
seção do número de versão. Por exemplo, 5.0.101 e 5.0.201 são versões em duas faixas
de recursos diferentes, enquanto 5.0.101 e 5.0.199 estão na mesma faixa de recursos.
Quando o SDK do .NET 5.0.101 é instalado, o SDK do .NET 5.1.100 é removido do
computador se ele existir. Quando o SDK do .NET 5.0.200 é instalado no mesmo
computador, o SDK do .NET 5.0.101 não é removido.

Para obter mais informações sobre a relação entre o SDK do .NET e as versões do Visual
Studio, confira SDK do .NET, o MSBuild e o controle de versão do Visual Studio.

Roll forward e compatibilidade do runtime


As atualizações principais e secundárias são instaladas lado a lado com versões
anteriores. Um aplicativo criado para direcionar uma versão principal.secundária
específica continua a usar esse runtime de destino mesmo se houver uma versão mais
recente instalada. O aplicativo não efetua automaticamente roll forward para usar uma
versão principal.secundária mais recente do runtime, a menos que você opte por esse
comportamento. Um aplicativo criado para ser direcionado ao .NET Core 3.0 não
começa a ser executado automaticamente no .NET Core 3.1. É recomendável recriar o
aplicativo e testar em uma versão de runtime principal ou secundária mais recente antes
de implantar em produção. Para obter mais informações, confira Efetuar roll forward de
aplicativos dependentes do Framework e Efetuar roll forward de runtime de implantação
autossuficiente.

As atualizações de manutenção são tratadas de modo diferente das versões principais e


secundárias. Um aplicativo criado para direcionar o .NET 7 é executado no runtime 7.0.0
por padrão. Ele avança automaticamente para usar um runtime 7.0.1 mais recente
quando essa atualização de manutenção é instalada. Esse comportamento é o padrão
porque queremos que as correções de segurança sejam usadas assim que elas forem
instaladas sem nenhuma outra ação necessária. Você pode recusar esse comportamento
de roll forward padrão.

Ciclos de vida de versão do .NET


As versões do .NET adotam o ciclo de vida moderno em vez do ciclo de vida fixo usado
para versões do .NET Framework. Os produtos que adotam um ciclo de vida moderno
têm um modelo de suporte semelhante a um serviço, com períodos de suporte mais
curtos e versões mais frequentes.

Faixas de versão
Há duas faixas de suporte para versões:

Versões do Suporte a Termos Padrão (STS)

Essas versões têm suporte até 6 meses após a próxima versão principal ou
secundária.

Exemplo:
O .NET 5 é uma versão STS e foi lançado em novembro de 2020. Ele teve
suporte por 18 meses, até maio de 2022.
O .NET 7 é uma versão STS e foi lançado em novembro de 2022. Ele tem
suporte por 18 meses, até maio de 2024.

Versões com LTS (suporte de longo prazo)


Essas versões têm suporte por no mínimo 3 anos, ou 1 ano após a próxima versão
LTS ser enviada, se essa data for inferior.

Exemplo:
O .NET Core 3.1 é uma versão LTS e foi lançado em dezembro de 2019. Ele teve
suporte por 3 anos, até dezembro de 2022.
O .NET 6 é uma versão LTS e foi lançado em novembro de 2021. Ele tem suporte
por 3 anos, até novembro de 2024.

As versões alternam entre LTS e STS, portanto, é possível que uma versão anterior tenha
suporte por mais tempo do que uma posterior. Por exemplo, o .NET Core 3.1 era uma
versão LTS com suporte até dezembro de 2022. A versão do .NET 5 foi enviada quase
um ano depois, mas deixou de ter suporte antes, em maio de 2022.

As atualizações de manutenção são enviadas mensalmente e incluem correções


relacionadas à segurança ou de outra natureza (confiabilidade, compatibilidade e
estabilidade). As atualizações de manutenção tem suporte até que a próxima
atualização de manutenção seja lançada. As atualizações de manutenção têm o
comportamento de roll forward do runtime. Isso significa que os aplicativos executam
por padrão sobre a atualização de manutenção do runtime instalada mais
recentemente.

Como escolher uma versão


Se você estiver criando um serviço e espera continuar atualizando-o regularmente, uma
versão STS como o runtime do .NET 7 pode ser sua melhor opção para se manter
atualizado com os recursos mais recentes que o .NET tem a oferecer.

Se você estiver criando um aplicativo cliente que será distribuído aos consumidores,
estabilidade poderá ser mais importante do que acesso aos recursos mais recentes.
Talvez o aplicativo precise ter suporte por um determinado período antes que o
consumidor possa atualizar para a próxima versão. Nesse caso, uma versão LTS como o
runtime do .NET 6 pode ser a opção certa.

7 Observação

É recomendável atualizar para a versão mais recente do SDK, mesmo que seja uma
versão STS, pois ela pode direcionar todos os runtimes disponíveis.

Suporte para fornecimento de atualizações


As atualizações de manutenção do .NET tem suporte até que a próxima atualização de
manutenção seja lançada. A cadência de lançamento é mensal.

Você precisa instalar regularmente as atualizações de manutenção para garantir que


seus aplicativos estejam em um estado seguro e com suporte. Por exemplo, se a
atualização de manutenção mais recente do .NET 7 for 7.0.8 e enviarmos 7.0.9, a 7.0.8
não será mais a mais recente. O nível de manutenção com suporte para .NET 7 é então
7.0.9.

Para obter informações sobre as atualizações de manutenção mais recentes de cada


versão principal e secundária, confira a página de downloads do .NET .

Fim do suporte
O fim do suporte refere-se à data após a qual a Microsoft não fornece mais correções,
atualizações ou assistência técnica para uma versão do produto. Antes dessa data
chegar, verifique se você passou a usar uma versão com suporte. As versões sem
suporte não recebem mais as atualizações de segurança que protegem seus aplicativos
e dados. Para obter os intervalos de datas com suporte para cada versão do .NET,
confira a Política de suporte .

Sistemas operacionais compatíveis


O .NET pode ser executado em uma variedade de sistemas operacionais. Cada um
desses sistemas operacionais tem um ciclo de vida definido pela organização
patrocinadora (por exemplo, Microsoft, Red Hat ou Apple). Levamos em conta essas
programações de ciclo de vida ao adicionar e remover suporte para as versões do
sistema operacional.

Quando uma versão do sistema operacional fica sem suporte, paramos de testar e
fornecer suporte para ela. Os usuários precisam migrar para uma versão do sistema
operacional com suporte para obter assistência.

Para obter mais informações, confira a Política de Ciclo de Vida de Sistema Operacional
do .NET .

Obter suporte
Você pode escolher entre o suporte assistido da Microsoft e o suporte da Comunidade.

Suporte da Microsoft
Para obter suporte assistido, entre em contato com um Profissional do Suporte da
Microsoft .

Você precisa estar em um nível de manutenção com suporte (a atualização de


manutenção mais recente disponível) a fim de ser qualificado para receber suporte. Se
um sistema estiver executando o .NET 7 e a atualização de manutenção 7.0.8 tiver sido
lançada, a 7.0.8 precisará ser instalada como uma primeira etapa.

Suporte da comunidade
Para obter suporte da comunidade, confira a página Comunidade .

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Padrões de Ecma
Artigo • 08/11/2023

A linguagem C# e as especificações da CLI (Common Language Infrastructure) são


padronizadas por meio do Ecma International® . As primeiras edições desses padrões
foram publicadas pela Ecma em dezembro de 2001.

Revisões subsequentes dos padrões foram desenvolvidas pelos grupos de tarefas TC49-
TG2 (C#) e TC49-TG3 (CLI) no TC49 (Comitê Técnico de Linguagens de Programação) e
adotadas pelo Assembly Geral do Ecma e, posteriormente, pelo ISO/IEC JTC 1 por meio
do processo de Fast-Track de ISO.

Padrões mais recentes


Os seguintes documentos oficiais do Ecma estão disponíveis para C# e a CLI (TR-
84 ):

O padrão de linguagem C# (versão 6.0): ECMA-334.pdf


A Common Language Infrastructure: ECMA-335.pdf .
Informações Derivadas do Arquivo XML da Partição IV: formato ECMA TR/84 .

Os documentos oficiais de ISO/IEC estão disponíveis na página Padrões publicamente


disponíveis de ISO/IEC. Estes links são diretos dessa página:

Tecnologia da informação – Linguagens de programação – C#: ISO/IEC


23270:2018
Tecnologia da informação – Partições I a VI da CLI (Common Language
Infrastructure): ISO/IEC 23271:2012
Tecnologia da informação — CLI (Common Language Infrastructure) — Relatório
técnico sobre informações derivadas do arquivo XML da partição IV: ISO/IEC TR
23272:2011

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Glossário do .NET
Artigo • 05/06/2023

A principal meta deste glossário é esclarecer os significados de termos e acrônimos


selecionados que aparecem com frequência na documentação do .NET.

AOT
Compilador Ahead-of-Time.

Semelhante ao JIT, esse compilador também converte IL em código de máquina.


Diferentemente da compilação JIT, a compilação AOT acontece antes que o aplicativo
seja executado e normalmente é executada em um computador diferente. Como as
cadeias da ferramenta AOT não compilam em tempo de execução, elas não têm que
minimizar o tempo gasto na compilação. Isso significa que elas podem gastar mais
tempo em otimização. Como o contexto da AOT é o aplicativo inteiro, o compilador AOT
também executa a vinculação de módulo cruzado e a análise de programa inteiro, o que
significa que todas as referências são seguidas e um único executável é produzido.

Confira CoreRT e .NET Native.

Modelo de aplicativo
Uma API específica de carga de trabalho. Estes são alguns exemplos:

ASP.NET
ASP.NET Web API
Entity Framework – EF
Windows Presentation Foundation (WPF)
Windows Communication Foundation (WCF)
Windows Workflow Foundation (WF)
Windows Forms (WinForms)

ASP.NET
A implementação do ASP.NET original que é fornecida com o .NET Framework, também
conhecida como ASP.NET 4.x.

Às vezes, o ASP.NET é um termo abrangente que se refere às implementações do


ASP.NET e do ASP.NET Core. O significado que o termo carrega em uma determinada
instância é determinado pelo contexto. Consulte o ASP.NET 4.x quando quiser deixar
claro que não está usando o ASP.NET para as duas implementações.

Confira Documentação do ASP.NET.

ASP.NET Core
Uma implementação multiplataforma de alto desempenho e código aberto do ASP.NET.

Confira Documentação do ASP.NET Core.

assembly
Um arquivo .dll ou .exe que contém uma coleção de APIs que podem ser chamadas por
aplicativos ou outros assemblies.

Um assembly pode incluir tipos como interfaces, classes, estruturas, enumerações e


delegados. Às vezes, os assemblies em uma pasta bin de projeto são chamados de
binários. Consulte também biblioteca.

BCL
Biblioteca de classes base.

Um conjunto de bibliotecas que compõem os namespaces System.* (e, até certo limite,
Microsoft.*). A BCL é uma estrutura de nível inferior e de uso geral, base para a criação
de estruturas de aplicativo de nível mais alto, como o ASP.NET Core.

O código-fonte da BCL para .NET 5 (e .NET Core) e versões posteriores está contido no
repositório de runtime do .NET . A maioria dessas APIs BCL também está disponível
em .NET Framework, portanto, você pode pensar nesse código-fonte como um fork do
código-fonte BCL do .NET Framework.

Os termos a seguir geralmente se referem à mesma coleção de APIs às quais a BCL se


refere:

Principais bibliotecas do .NET


Bibliotecas do Framework
Bibliotecas de runtime
estrutura compartilhada
CLR
Common Language Runtime.

O significado exato depende do contexto. O Common Language Runtime geralmente se


refere ao runtime de .NET Framework ou ao runtime do .NET 5 (e do .NET Core) e
versões posteriores.

O CLR manipula a alocação e o gerenciamento de memória. O CLR também é uma


máquina virtual que não só executa aplicativos, mas também gera e compila código
dinamicamente usando um compilador JIT.

A implementação do CLR para .NET Framework é somente do Windows.

A implementação do CLR para o .NET 5 e versões posteriores (também conhecidas


como CLR Principal) é criada a partir da mesma base de código que o CLR para .NET
Framework. Originalmente, o Core CLR era o runtime do Silverlight e foi projetado para
ser executado em várias plataformas, especificamente Windows e OS X. Ele ainda é um
runtime multiplataforma, agora incluindo suporte para muitas distribuições do Linux.

Consulte também runtime.

Core CLR
O Common Language Runtime para .NET 5 (e .NET Core) e versões posteriores.

Consulte CLR.

CoreRT
Ao contrário do CLR, o CoreRT não é uma máquina virtual, o que significa que ele não
inclui os recursos para gerar e executar código dinamicamente, já que não inclui um JIT.
No entanto, ele inclui a GC e a capacidade de RTTI (identificação de tipo de runtime) e
reflexão. Contudo, seu sistema de tipos é projetado para que os metadados para
reflexão não sejam necessários. Isso permite ter uma cadeia de ferramentas AOT que
possa desvincular metadados supérfluos e, mais importante, identificar código que o
aplicativo não usa. O CoreRT está em desenvolvimento.

Consulte Introdução ao CoreRT e o .NET Runtime Lab .

várias plataformas
A capacidade de desenvolver e executar um aplicativo que pode ser usado em vários
sistemas operacionais diferentes, como Linux, Windows e iOS, sem que seja preciso
reescrever especificamente para cada um deles. Isso permite a reutilização de código e a
consistência entre os aplicativos em diferentes plataformas.

Consulte plataforma.

ecossistema
Todos os softwares de runtime, as ferramentas de desenvolvimento e os recursos da
comunidade que são usados para compilar e executar aplicativos de uma determinada
tecnologia.

O termo "Ecossistema do .NET" difere de termos semelhantes, como "Pilha do .NET", em


relação à inclusão de bibliotecas e aplicativos de terceiros. Veja um exemplo em uma
frase:

"A motivação por trás do .NET Standard era estabelecer maior uniformidade no
ecossistema .NET."

estrutura
Em geral, uma coleção abrangente de APIs que facilita o desenvolvimento e a
implantação de aplicativos que são baseados em uma tecnologia específica. Nesse
sentido geral, o ASP.NET Core e o Windows Forms são exemplos de estruturas de
aplicativo. A estrutura de palavras e a biblioteca geralmente são usadas como
sinônimos.

A palavra "estrutura" tem um significado técnico mais específico nos seguintes termos:

Bibliotecas do Framework
.NET Framework
estrutura compartilhada
estrutura de destino
TFM (Moniker de Estrutura de Destino)
Aplicativo dependente da estrutura

Às vezes "estrutura" refere-se a uma implementação do .NET. Por exemplo, um artigo


pode chamar o .NET 5+ de uma estrutura.

Bibliotecas de estrutura
O significado depende do contexto. Pode se referir às bibliotecas de estrutura para .NET
5 (e .NET Core) e versões posteriores, nesse caso, refere-se às mesmas bibliotecas às
quais a BCL se refere. Também pode se referir às bibliotecas de estrutura ASP.NET Core,
que se baseiam na BCL e fornecem APIs adicionais para aplicativos Web.

GC
Coletor de lixo.

O coletor de lixo é uma implementação do gerenciamento automático de memória. O


GC libera a memória ocupada por objetos que não estejam mais em uso.

Consulte Coleta de lixo.

IL
Linguagem intermediária.

As linguagens .NET de nível mais alto , como C#, são compiladas em um conjunto de
instruções independente de hardware, que é chamado de IL (linguagem intermediária).
A IL às vezes é conhecida como MSIL (Microsoft Intermediate Language) ou CIL
(Common Intermediate Language).

JIT
Compilador Just-In-Time.

Semelhante ao AOT, esse compilador converte a IL em um código de máquina que o


processador entenda. Ao contrário da AOT, a compilação JIT acontece sob demanda e é
executada no mesmo computador em que o código precisa ser executado. Como a
compilação JIT ocorre durante a execução do aplicativo, o tempo de compilação faz
parte do tempo de execução. Assim, os compiladores JIT precisam equilibrar o tempo
gasto com a otimização do código em relação à economia que o código resultante
pode produzir. Mas um JIT reconhece o hardware e pode evitar que os desenvolvedores
tenham que fornecer implementações diferentes.

implementação do .NET
Uma implementação do .NET inclui o seguinte:

Um ou mais runtimes. Exemplos: CLR, CoreRT.


Uma biblioteca de classes que implemente uma versão do .NET Standard, podendo
incluir APIs adicionais. Exemplos: as BCLs para .NET Framework e .NET 5 (e .NET
Core) e versões posteriores.
Opcionalmente, uma ou mais estruturas de aplicativo. Exemplos: ASP.NET,
Windows Forms, e WPF estão incluídos no .NET Framework e .NET 5+.
Opcionalmente, ferramentas de desenvolvimento. Algumas ferramentas de
desenvolvimento são compartilhadas entre várias implementações.

Exemplos de implementações do .NET:

.NET Framework
.NET 5 (e .NET Core) e versões posteriores
UWP (Plataforma Universal do Windows)
Mono

Para obter mais informações, consulte Implementações do .NET.

biblioteca
Uma coleção de APIs que podem ser chamadas por aplicativos ou outras bibliotecas.
Uma biblioteca do .NET é composta de um ou mais assemblies.

A biblioteca de palavras e a estrutura geralmente são usadas como sinônimos.

Mono
Uma implementação do .NETmultiplataforma de software livre usada principalmente
quando é necessário um pequeno runtime. É o runtime que impulsiona aplicativos
Xamarin no Android, Mac, iOS, tvOS e watchOS, e se concentra, principalmente, em
aplicativos que exigem pequeno volume de memória.

Ele dá suporte a todas as versões do .NET Standard publicadas atualmente.

Historicamente, o Mono implementava a maior API do .NET Framework e emulava


alguns dos recursos mais populares do Unix. Às vezes, ele é usado para executar
aplicativos .NET que dependem desses recursos no Unix.

O Mono normalmente é usado com um compilador just-in-time, mas também


apresenta um compilador estático completo (compilação ahead-of-time) que é usado
em plataformas como a iOS.

Para saber mais, confira a documentação do Mono .


AOT nativo
Um modo de implantação em que o aplicativo é independente e foi compilado
antecipadamente para código nativo no momento da publicação. Aplicativos AOT
nativos não usam um compilador JIT em tempo de execução. Eles podem ser
executados em computadores que não têm o runtime do .NET instalado.

Para obter mais informações, consulte Implantação do AOT nativo.

.NET
Em geral, .NET é termo abrangente para.NET Standard e todas as implementações
de .NET, bem como as cargas de trabalho.
Mais especificamente, o .NET refere-se à implementação do .NET recomendada
para todo o novo desenvolvimento: .NET 5 (e .NET Core) e versões posteriores.

Por exemplo, o primeiro significado destina-se a frases como "implementações do .NET"


ou "a plataforma de desenvolvimento do .NET". O segundo significado destina-se a
nomes como SDK do .NET e CLI do .NET.

O .NET é sempre totalmente maiúscula, nunca ".Net".

Consulte documentação do .NET

.NET 5+
O sinal de adição após um número de versão significa "e versões posteriores". Consulte
.NET 5 e versões posteriores.

.NET 5 e versões posteriores


Uma implementação multiplataforma de alto desempenho e código aberto do .NET.
Também conhecido como .NET 5+. Inclui um CLR (Common Language Runtime), um
AOT Runtime (CoreRT, em desenvolvimento), uma BCL (Biblioteca de Classes Base) e o
SDK do .NET.

As versões anteriores dessa implementação do .NET são conhecidas como .NET Core. O
.NET 5 é a próxima versão após o .NET Core 3.1. A versão 4 foi ignorada para evitar
confundir essa implementação mais recente do .NET com a implementação mais antiga
conhecida como .NET Framework. A versão atual é o .NET Framework 4.8.

Consulte documentação do .NET.


CLI do .NET
Uma cadeia de ferramentas multiplataforma para desenvolver aplicativos e bibliotecas
para .NET 5 (e .NET Core) e versões posteriores. Também conhecida como CLI do .NET
Core.

O CLI do.NET.

.NET Core
Consulte .NET 5 e versões posteriores.

.NET Framework
Uma implementação do .NET que é executado somente no Windows. Inclui o CLR
(Common Language Runtime), aBCL (Biblioteca de classes base) e as bibliotecas de
estrutura do aplicativo, como ASP.NET, Windows Forms e WPF.

Consulte o Guia do .NET Framework.

.NET Nativo
Uma cadeia de ferramentas de compilador que gera código nativo AOT (Ahead Of
Time), em vez de JIT (Just-In-Time).

A compilação acontece no computador do desenvolvedor, semelhante à maneira como


um compilador e vinculador C++ funciona. Ela remove código não utilizado e gasta
mais tempo otimizando-o. Ele extrai o código de bibliotecas e os mescla no executável.
O resultado é um módulo único que representa o aplicativo inteiro.

A UWP foi a primeira estrutura de aplicativo com suporte pelo .NET Native.

Consulte documentação .NET Native.

SDK .NET
Um conjunto de bibliotecas e ferramentas que permitem aos desenvolvedores criar
aplicativos .NET e bibliotecas para .NET 5 (e .NET Core) e versões posteriores. Também
conhecido como SDK do .NET Core.
Inclui a CLI do .NET Core para a criação de aplicativos, bibliotecas e runtime do .NET
Core para criar e executar aplicativos e o executável do dotnet (dotnet.exe) que executa
comandos de CLI e executa aplicativos.

Consulte Visão geral do SDK do .NET.

.NET Standard
Uma especificação formal das APIs do .NET que estão disponíveis em cada
implementação do .NET.

Às vezes, a especificação do .NET Standard também é chamada de biblioteca. Como


uma biblioteca inclui implementações de API e não apenas especificações (interfaces), é
um equívoco chamar .NET Standard de "biblioteca".

Consulte .NET Standard.

NGen
Geração (de imagem) nativa.

Você pode pensar nessa tecnologia como um compilador JIT persistente. Ela geralmente
compila o código no computador em que o código é executado, mas a compilação
normalmente ocorre no momento da instalação.

pacote
Um pacote NuGet ou apenas um pacote é um arquivo .zip com um ou mais assemblies
de mesmo nome, junto com metadados adicionais, como o nome do autor.

O arquivo .zip tem uma extensão .nupkg e pode conter ativos, como arquivos .dll e
arquivos .xml, para uso com várias versões e estruturas de destino. Quando instalado
em um aplicativo ou uma biblioteca, os ativos apropriados são selecionados com base
na estrutura de destino especificada pelo aplicativo ou pela biblioteca. Os ativos que
definem a interface estão na pasta ref e os ativos que definem a implementação estão
na pasta lib.

plataforma
Um sistema operacional e o hardware em que ele é executado, como macOS, Windows,
Linux, iOS e Android.
Veja alguns exemplos de uso nessas frases:

"O .NET Core é uma implementação multiplataforma do .NET."


"Os perfis de PCL representam plataformas da Microsoft enquanto que o .NET
Standard é independente de plataforma."

A documentação do .NET herdada frequentemente usa "plataforma .NET" para significar


uma implementação do .NET ou a pilha do .NET, incluindo todas as implementações. Os
dois usos tendem a ser confundidos com o significado (SO/hardware) principal,
portanto planejamos eliminar esses usos da documentação.

"Platforma" tem um significado diferente na frase "plataforma de desenvolvedor", que


se refere ao software que fornece ferramentas e bibliotecas para criar e executar
aplicativos. O .NET é uma plataforma de desenvolvedor multiplataforma, de código
aberto, gratuita para criar muitos tipos diferentes de aplicativos.

POCO
Um POCO — ou um objeto CLR /classe antiga simples — é uma estrutura de dados do
.NET que contém apenas propriedades ou campos públicos. Um POCO não deve conter
nenhum outro membro, como:

methods
events
delegados

Esses objetos são usados principalmente como DTOs (objetos de transferência de


dados). Um POCO puro não herdará outro objeto nem implementará uma interface. É
comum que POCOs sejam usados com serialização.

runtime
Geralmente, o ambiente de execução de um programa gerenciado. O SO faz parte do
ambiente do runtime, mas não faz parte do runtime do .NET. Eis alguns exemplos de
runtimes do .NET nesse sentido da palavra:

CLR (Common Language Runtime)


.NET Native (para UWP)
runtime Mono

A palavra "runtime" tem um significado diferente em alguns contextos:

Runtime do .NET na página de download do .NET 5 .


Você pode baixar o runtime do .NET ou outros runtimes, como o runtime ASP.NET
Core. Um runtime nesse uso é o conjunto de componentes que deve ser instalado
em um computador para executar um aplicativo dependente de estrutura no
computador. O runtime do .NET inclui o CLR e a estrutura compartilhada do .NET,
que fornece a BCL.

Bibliotecas de runtime do .NET

Refere-se às mesmas bibliotecas às quais a BCL se refere. No entanto, outros


runtimes, como o runtime ASP.NET Core, têm estruturas compartilhadas diferentes,
com bibliotecas adicionais que se baseiam na BCL.

RID (Identificador de Runtime).

Runtime aqui significa a plataforma do sistema operacional e a arquitetura de CPU


em que um aplicativo .NET é executado, por exemplo: linux-x64 .

Às vezes, "runtime" é usado no sentido de uma implementação do .NET, como nos


seguintes exemplos:
"Os diversos runtimes do .NET implementam versões específicas do .NET
Standard. … Cada versão de runtime do .NET anuncia a última versão do .NET
Standard à qual ele dá suporte..."
"Bibliotecas destinadas à execução em vários runtimes devem ter essa estrutura
como destino." (em referência ao .NET Standard)

estrutura compartilhada
O significado depende do contexto. A estrutura compartilhada do .NET refere-se às
bibliotecas incluídas no runtime do .NET. Nesse caso, a estrutura compartilhada para
.NET 5 (e .NET Core) e versões posteriores refere-se às mesmas bibliotecas às quais a
BCL se refere.

Há outras estruturas compartilhadas. A estrutura compartilhada ASP.NET Core refere-se


às bibliotecas incluídas no runtime do ASP.NET Core, que inclui a BCL, além de APIs
adicionais para uso por aplicativos Web.

Para aplicativos dependentes de estrutura, a estrutura compartilhada consiste em


bibliotecas contidas em assemblies instalados em uma pasta no computador que
executa o aplicativo. Para aplicativos independentes, os assemblies de estrutura
compartilhada são incluídos no aplicativo.

Para obter mais informações, consulte Aprofundamento nos primitivos do .NET Core,
parte 2: a estrutura compartilhada .
stack
Um conjunto de tecnologias de programação que são usadas para compilar e executar
aplicativos.

"A pilha do .NET" refere-se ao .NET Standard e a todas as implementações do .NET. A


frase "uma pilha do .NET" pode se referir a uma implementação do .NET.

estrutura de destino
A coleção de APIs da qual um aplicativo ou biblioteca do .NET depende.

Um aplicativo ou uma biblioteca pode se destinar a uma versão do .NET Standard (por
exemplo, .NET Standard 2.0), que é a especificação de um conjunto padronizado de APIs
entre todas as implementações do .NET. Um aplicativo ou uma biblioteca também pode
se destinar a uma versão de uma implementação específica do .NET, obtendo acesso a
APIs específicas da implementação. Por exemplo, um aplicativo que tem como destino o
Xamarin.iOS obtém acesso aos wrappers da API fornecidos para Xamarin iOS.

Para algumas estruturas de destino (por exemplo, o .NET Framework) as APIs disponíveis
são definidas pelos assemblies que uma implementação do .NET instala em um sistema,
que podem incluir as APIs de estrutura do aplicativo (por exemplo, ASP.NET, WinForms).
Para estruturas de destino baseadas em pacote, as APIs de estrutura são definidas por
pacotes instalados no aplicativo ou na biblioteca.

Consulte Estruturas de destino.

TFM
Moniker da estrutura de destino.

Um formato de token padronizado para especificar a estrutura de destino de um


aplicativo ou uma biblioteca do .NET. As estruturas de destino são geralmente
referenciadas por um nome curto, como net462 . Os TFMs de formato longo (como.
.NETFramework,Version=4.6.2) existem, mas não são geralmente usados para especificar
uma estrutura de destino.

Consulte Estruturas de destino.

UWP
Plataforma Universal do Windows.
Uma implementação do .NET que é usada para criar aplicativos do Windows sensíveis
ao toque, bem como software para a IoT (Internet das Coisas). Ela foi projetada para
unificar os diferentes tipos de dispositivos que você pode ter como destinho, incluindo
PCs, tablets, telefones e até mesmo o Xbox. A UWP fornece muitos serviços, como um
repositório centralizado de aplicativos, um ambiente de execução (AppContainer) e um
conjunto de APIs do Windows para usar em vez das APIS do Win32 (WinRT). Os
aplicativos podem ser escritos em C++, C#, Visual Basic e JavaScript. Ao usar o C# e o
Visual Basic, as APIs do .NET são fornecidas por .NET 5 (e .NET Core) e versões
posteriores.

workload
Um tipo de aplicativo que alguém está criando. Mais genérico do que o modelo de
aplicativo. Por exemplo, na parte superior de cada página de documentação do .NET,
incluindo esta, está uma lista suspensa para Cargas de Trabalho, que permite mudar
para documentação para Dados da Web, Móvel, Nuvem, Área de Trabalho e Machine
Learning&.

Em alguns contextos, a carga de trabalho refere-se a uma coleção de recursos do Visual


Studio que você pode optar por instalar para dar suporte a um tipo específico de
aplicativo. Para obter um exemplo, consulte Selecionar uma carga de trabalho.

Confira também
Conceitos básicos do .NET
Guia do .NET Framework
Visão geral do ASP.NET
Visão geral do ASP.NET Core
O modelo de aplicativo de console do
C# gera instruções de nível superior
Artigo • 09/05/2023

Do .NET 6 em diante, o modelo de projeto para novos aplicativos de console do C# gera


o seguinte código no arquivo Program.cs:

C#

// See https://aka.ms/new-console-template for more information


Console.WriteLine("Hello, World!");

A nova saída usa recursos recentes do C# que simplificam o código que você precisa
escrever para um programa. Para o .NET 5 e versões anteriores, o modelo de aplicativo
de console gera o seguinte código:

C#

using System;

namespace MyApp // Note: actual namespace depends on the project name.


{
internal class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

Essas duas formas representam o mesmo programa. Ambos são válidos com C# 10.0.
Ao usar a versão mais recente, você só precisa escrever o corpo do método Main . O
compilador sintetiza uma classe Program com um método Main e coloca todas as
instruções de nível superior nesse método Main . Você não precisa incluir os outros
elementos do programa, o compilador os gera para você. Você pode saber mais sobre o
código gerado pelo compilador ao usar instruções de nível superior no artigo sobre
Instruções de nível superior, na seção de conceitos básicos do Guia do C#.

Você tem duas opções para trabalhar com tutoriais que não foram atualizados para usar
modelos do .NET 6+:
Use o novo estilo de programa, adicionando novas instruções de nível superior à
medida que você adiciona recursos.
Converta o novo estilo de programa no estilo mais antigo, com uma classe
Program e um método Main .

Se você quiser usar os modelos antigos, confira Usar o antigo estilo de programa mais
adiante neste artigo.

Usar o novo estilo de programa


Os recursos que tornam o novo programa mais simples são as instruções de nível
superior, as diretivas using globais e as diretivas using implícitas.

O termo instruções de nível superior significa que o compilador gera os elementos de


classe e método para o programa principal. A classe e o método Main gerados pelo
compilador são declarados no namespace global. Você pode examinar o código do
novo aplicativo e imaginar que ele contém as instruções dentro do método Main
gerado por modelos anteriores, mas no namespace global.

Você pode adicionar mais instruções ao programa, assim como pode adicionar mais
instruções ao seu método Main no estilo tradicional. Você pode acessar args
(argumentos de linha de comando), usar await e definir o código de saída. Você pode
até adicionar funções. Elas são criadas como funções locais aninhadas dentro do
método Main gerado. As funções locais não podem incluir modificadores de acesso (por
exemplo, public ou protected ).

As instruções de nível superior e as diretivas using implícitas simplificam o código que


compõe seu aplicativo. Para seguir um tutorial existente, adicione as novas instruções ao
arquivo Program.cs gerado pelo modelo. Você pode imaginar que as instruções que
você escreve estão entre as chaves de abertura e fechamento no método Main nas
instruções do tutorial.

Se preferir usar o formato mais antigo, copie o código do segundo exemplo deste
artigo e prossiga com o tutorial como antes.

Você pode saber mais sobre as instruções de nível superior explorando o tutorial sobre
esse assunto.

Diretivas using implícitas


O termo diretivas using implícitas significa que o compilador adiciona automaticamente
um conjunto de diretivas using com base no tipo de projeto. Para aplicativos de console,
as seguintes diretivas são incluídas implicitamente no aplicativo:

using System;
using System.IO;

using System.Collections.Generic;

using System.Linq;
using System.Net.Http;

using System.Threading;
using System.Threading.Tasks;

Outros tipos de aplicativo incluem mais namespaces, que são comuns para esses tipos
de aplicativo.

Se precisar de diretivas using não incluídas implicitamente, você poderá adicioná-las ao


arquivo .cs que contém instruções de nível superior ou a outros arquivos .cs. Para
diretivas using , necessárias em todos os arquivos .cs de um aplicativo, use as diretivas
using globais.

Desabilitar diretivas using implícitas


Se você quiser remover esse comportamento e controlar manualmente todos os
namespaces em seu projeto, adicione <ImplicitUsings>disable</ImplicitUsings> ao
arquivo de projeto no elemento <PropertyGroup> , conforme mostrado no seguinte
exemplo:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
...
<ImplicitUsings>disable</ImplicitUsings>
</PropertyGroup>

</Project>

Diretivas using globais


Uma diretiva using global importa um namespace para todo o aplicativo, e não para um
só arquivo. Essas diretivas globais podem ser adicionadas incluindo um item <Using>
ao arquivo de projeto ou adicionando a diretiva global using a um arquivo de código.

Você também pode adicionar um item <Using> com um atributo Remove ao arquivo de
projeto para remover uma diretiva using implícita específica. Por exemplo, se o recurso
de diretivas using implícitas estiver ativado com
<ImplicitUsings>enable</ImplicitUsings>, adicionar o seguinte item <Using> removerá
o namespace System.Net.Http daqueles que tiverem sido importados implicitamente:

XML

<ItemGroup>
<Using Remove="System.Net.Http" />
</ItemGroup>

Usar o antigo estilo de programa


A partir do SDK do .NET 6.0.300, o modelo console tem uma opção --use-program-
main . Use-o para criar um projeto de console que não usa instruções de nível superior e
tem um método Main .

CLI do .NET

dotnet new console --use-program-main

O Program.cs gerado é o seguinte:

C#

namespace MyProject;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}

Usar o antigo estilo de programa no Visual Studio


1. Ao criar um projeto, as etapas de configuração levam você à página Informações
adicionais. Nesta página, marque a caixa de seleção Não usar instruções de nível
superior.
2. Depois que o projeto é criado, o conteúdo do Program.cs é o seguinte:

C#

namespace MyProject;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}

7 Observação

O Visual Studio preservará o valor das opções da próxima vez que você criar o
projeto com base no mesmo modelo. Portanto, por padrão, ao criar o projeto de
Aplicativo de Console da próxima vez, a caixa de seleção "Não usar instruções de
nível superior" será marcada. O conteúdo do arquivo Program.cs pode ser
diferente para corresponder ao estilo de código definido nas configurações globais
do editor de texto do Visual Studio ou no arquivo EditorConfig .

Para obter mais informações, confira Criar configurações do editor portáteis e


personalizadas com EditorConfig e Opções, Editor de Texto, C#, Avançado.
Tutorial: Criar um aplicativo de console
.NET usando o Visual Studio
Artigo • 28/08/2023

Este tutorial mostra como criar e executar um aplicativo de console .NET no Visual
Studio 2022.

Pré-requisitos
Versão prévia do Visual Studio 2022 com a carga de trabalho de desenvolvimento
de desktop .NET instalada. O SDK do .NET 8 é instalado automaticamente quando
você seleciona essa carga de trabalho.

Para obter mais informações, confira Instalar o SDK do .NET com o Visual Studio.

Criar o aplicativo
Crie um projeto de aplicativo de console do .NET chamado "Olá, Mundo".

1. Inicie o Visual Studio 2022.

2. Na página Iniciar, escolha Criar um projeto.


3. Na página Criar um projeto, insira console na caixa de pesquisa. Em seguida,
escolha C# ou Visual Basic na lista de linguagens de computador e depois Todas
as plataformas na lista de plataformas. Escolha o modelo Aplicativo de Console e,
em seguida, Avançar.

 Dica

Se você não vir os modelos do .NET, provavelmente está faltando a carga de


trabalho necessária. Na mensagem Não encontrou o que precisa?, escolha o
link Instalar mais ferramentas e recursos. O Instalador do Visual Studio abre.
Verifique se você tem a carga de trabalho instalada para desenvolvimento de
desktop .NET.

4. Na caixa de diálogo Configurar seu novo projeto, digite ou insira Olá, Mundo na
caixa Nome do projeto. Em seguida, escolha Avançar.
5. Na caixa de diálogo Informações adicionais:

Selecione .NET 8 (Versão prévia).


Selecione Não usar declarações de nível superior.
Selecione Criar.

O modelo cria um aplicativo simples que exibe "Olá, Mundo!" na janela do


console. O código está no arquivo Program.cs ou Program.vb:

C#

namespace HelloWorld;

internal class Program


{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}

Se a linguagem que você quer usar não aparecer, altere o seletor de linguagem na
parte superior da página.

O código define uma classe, Program , com um único método, Main , que usa uma
matriz String como um argumento. Main é o ponto de entrada do aplicativo, o
método que é chamado automaticamente pelo runtime quando ele inicia o
aplicativo. Quaisquer argumentos de linha de comando fornecidos quando o
aplicativo for iniciado estão disponíveis na matriz args.

O C# tem um recurso chamado instruções de nível superior que permite omitir a


classe Program e o método Main . Este tutorial não usa esse recurso. É uma questão
de preferência de estilo usá-lo em seus programas.

Executar o aplicativo
1. Pressione Ctrl + F5 para executar o programa sem depuração.

Uma janela do console é aberta com o texto "Olá, Mundo!" impresso na tela. (Ou
"Olá Mundo!", sem vírgula, no modelo de projeto do Visual Basic.)

2. Pressione qualquer tecla para fechar a janela do console.

Aprimorar o aplicativo
Aprimore seu aplicativo para solicitar ao usuário seu nome e exibi-lo junto com a data e
hora.

1. Em Program.cs ou Program.vb, substitua o conteúdo do método Main , que é a


linha que chama Console.WriteLine , pelo seguinte código:

C#

Console.WriteLine("What is your name?");


var name = Console.ReadLine();
var currentDate = DateTime.Now;
Console.WriteLine($"{Environment.NewLine}Hello, {name}, on
{currentDate:d} at {currentDate:t}!");
Console.Write($"{Environment.NewLine}Press any key to exit...");
Console.ReadKey(true);

Esse código mostra um prompt na janela do console e aguarda até que o usuário
insira uma cadeia de caracteres seguida da tecla Enter . Ele armazena essa cadeia
de caracteres em uma variável chamada name . Ele também recupera o valor da
propriedade DateTime.Now, que contém a hora local atual e o atribui a uma
variável chamada currentDate . E exibe esses valores na janela do console. Por fim,
ele exibe um prompt na janela do console e chama o método
Console.ReadKey(Boolean) para aguardar a entrada do usuário.

Environment.NewLine é uma maneira independente de plataforma e de linguagem


para representar uma quebra de linha. As alternativas são \n em C# e vbCrLf no
Visual Basic.

O sinal de dólar ( $ ) na frente de uma cadeia de caracteres permite colocar


expressões como nomes de variáveis em chaves na cadeia de caracteres. O valor
da expressão é inserido na cadeia de caracteres no lugar da expressão. Essa sintaxe
é conhecida como cadeia de caracteres interpolada.

2. Pressione Ctrl + F5 para executar o programa sem depuração.

3. Responda à solicitação inserindo um nome e pressionando a tecla Enter .

4. Pressione qualquer tecla para fechar a janela do console.

Recursos adicionais
Versões de suporte de prazo padrão (STS) e versões de suporte de longo prazo
(LTS).

Próximas etapas
Neste tutorial, você criou um aplicativo de console .NET. No próximo tutorial, você
depura o aplicativo.

Depurar um aplicativo de console do .NET usando Visual Studio Code


Tutorial: depurar um aplicativo de
console .NET usando o Visual Studio
Artigo • 28/08/2023

Este tutorial apresenta as ferramentas de depuração disponíveis no Visual Studio.

) Importante

Todos os atalhos de teclado são baseados nos padrões do Visual Studio. Os atalhos
de teclado podem variar; para obter mais informações, confira Atalhos de teclado
no Visual Studio.

Pré-requisitos
Este tutorial funciona com o aplicativo de console que você cria em Criar um
aplicativo de console .NET usando o Visual Studio.

Usar a configuração de build de depuração


Depuração e Lançamento são as configurações de build internas do Visual Studio. Você
usa a configuração de build de depuração para depuração e a configuração de
lançamento para a distribuição da versão final.

Na configuração de depuração, um programa é compilado com informações de


depuração simbólicas e sem otimização. A otimização complica a depuração, porque a
relação entre o código fonte e as instruções geradas é mais complexa. A configuração
de lançamento de um programa não tem informações de depuração simbólica e é
totalmente otimizada.

Por padrão, o Visual Studio usa a configuração de build de depuração, portanto, não é
necessário alterá-la antes da depuração.

1. Inicie o Visual Studio.

2. Abra o projeto que você criou em Criar um aplicativo de console .NET usando o
Visual Studio.

A configuração de build atual é mostrada na barra de ferramentas. A seguinte


imagem da barra de ferramentas mostra que o Visual Studio está configurado para
compilar na versão de Depuração do aplicativo:

Definir um ponto de interrupção


Um ponto de interrupção interrompe temporariamente a execução do aplicativo antes
de a linha com o ponto de interrupção ser executada.

1. Defina um ponto de interrupção na linha que exibe o nome, a data e a hora


clicando na margem esquerda da janela de código nessa linha. A margem
esquerda fica à esquerda dos números de linha. Outras maneiras de definir um
ponto de interrupção são colocando o cursor na linha de código e pressionando
F9 ou escolhendo Depurar>Alternar Ponto de Interrupção na barra de menus.

Como mostra a figura a seguir, o Visual Studio indica a linha na qual o ponto de
interrupção é definido, realçando-o e exibindo um círculo vermelho na margem
esquerda.

2. Pressione F5 para executar o aplicativo no modo de depuração. Outra maneira de


iniciar a depuração é escolhendo Depurar>Iniciar Depuração no menu.

3. Insira uma cadeia de caracteres na janela do console quando o programa solicitar


um nome e pressione Enter .

4. A execução do programa para quando ele atinge o ponto de interrupção e antes


de o método Console.WriteLine ser executado. A janela Locais exibe os valores
das variáveis que são definidas no método que está sendo executado no
momento.

Usar a janela Imediata


A janela Imediata permite interagir com o aplicativo que você está depurando. Você
pode alterar o valor das variáveis de maneira interativa para ver como isso afeta o
programa.

1. Se a janela Imediata não estiver visível, exiba-a escolhendo


Depurar>Janelas>Imediata.

2. Insira name = "Gracie" na janela Imediata e pressione a tecla Enter .

3. Insira currentDate = DateTime.Parse("2019-11-16T17:25:00Z").ToUniversalTime()


na janela Imediata e pressione a tecla Enter .

A janela Imediata exibe o valor da variável de cadeia de caracteres e as


propriedades do valor DateTime. Além disso, os valores das variáveis são
atualizados na janela Locais.
4. Pressione F5 para continuar a execução do programa. Outra maneira de continuar
é escolher Depurar>Continuar no menu.

Os valores exibidos na janela do console correspondem às alterações feitas na


janela Imediata.

5. Pressione qualquer tecla para sair do aplicativo e parar a depuração.

Definir um ponto de interrupção condicional


O programa exibe a cadeia de caracteres que o usuário insere. O que acontecerá se o
usuário não inserir nada? Isso pode ser testado com um recurso de depuração útil
chamado ponto de interrupção condicional.

1. Clique com o botão direito do mouse no ponto vermelho que representa o ponto
de interrupção. No menu de contexto, selecione Condições para abrir a caixa de
diálogo Configurações de Ponto de Interrupção. Selecione a caixa de Condições
se ela ainda não estiver selecionada.
2. Na Expressão Condicional, insira o código a seguir no campo que mostra o código
de exemplo que é testado quando x é 5.

C#

string.IsNullOrEmpty(name)

Em todas as ocorrências do ponto de interrupção, o depurador chama o método


String.IsNullOrEmpty(name) e ele é interrompido nessa linha somente quando a

chamada do método retorna true .

Em vez de uma expressão condicional, você pode especificar uma contagem de


ocorrências, que interrompe a execução do programa antes que uma instrução seja
executada um determinado número de vezes. Outra opção é especificar uma
condição de filtro, que interrompe a execução do programa com base em atributos
como identificador de thread, nome do processo ou nome do thread.

3. Selecione Fechar para fechar a caixa de diálogo.

4. Inicie o programa com a depuração pressionando F5 .

5. Na janela do console, pressione a tecla Enter quando seu nome for solicitado.

6. Como a condição especificada ( name é null ou String.Empty) foi atendida, a


execução do programa é interrompida quando ele chega no ponto de interrupção
e antes que o método Console.WriteLine seja executado.
7. Selecione a janela Locais, que mostra os valores das variáveis que são locais para o
método em execução no momento. Nesse caso, Main é o método em execução no
momento. Observe que o valor da variável name é "" ou String.Empty.

8. Confirme se o valor é uma cadeia de caracteres vazia inserindo a seguinte


instrução na janela Imediata e pressionando Enter . O resultado é true .

C#

? name == String.Empty

O ponto de interrogação direciona a janela imediata para avaliar uma expressão.

9. Pressione F5 para continuar a execução do programa.

10. Pressione qualquer tecla para fechar a janela do console e sair do modo de
depuração.

11. Limpe o ponto de interrupção clicando no ponto na margem esquerda da janela


de código. Outras maneiras de limpar um ponto de interrupção são pressionando
F9 ou escolhendo Depurar > Alternar Ponto de Interrupção enquanto a linha de
código está selecionada.

Percorrer um programa
O Visual Studio também permite percorrer linha por linha de um programa e monitorar
sua execução. Normalmente, você define um ponto de interrupção e segue o fluxo do
programa em uma pequena parte do código do programa. Como esse programa é
pequeno, você pode percorrer todo o programa.

1. Escolha Depurar>Intervir. Outra maneira de depurar uma instrução por vez é


pressionando F11 .
O Visual Studio realça e exibe uma seta ao lado da próxima linha de execução.

C#

Visual Basic

Neste ponto, a janela Locais mostra que a matriz args está vazia e name e
currentDate têm valores padrão. Além disso, o Visual Studio abriu uma janela de

console em branco.

2. Pressione F11 . O Visual Studio agora destaca a próxima linha de execução. A


janela Locais permanece inalterada e a janela do console permanece em branco.

C#
Visual Basic

3. Pressione F11 . O Visual Studio destaca a instrução que inclui a atribuição de


variável name . A janela Locais mostra que name é null e a janela do console exibe
a cadeia de caracteres "Qual é seu nome?".

4. Responda à solicitação inserindo uma cadeia de caracteres na janela do console e


pressionando Enter . O console não responde e a cadeia de caracteres inserida não
é exibida na janela do console, mas o método Console.ReadLine vai capturar a
entrada.

5. Pressione F11 . O Visual Studio destaca a instrução que inclui a atribuição de


variável currentDate . A janela Locais mostra o valor retornado pela chamada ao
método Console.ReadLine. A janela do console também exibe a cadeia de
caracteres que você inseriu no prompt.

6. Pressione F11 . A janela Locais mostra o valor da variável currentDate após a


atribuição da propriedade DateTime.Now. A janela do console não é alterada.
7. Pressione F11 . O Visual Studio chama o método Console.WriteLine(String, Object,
Object). A janela do console exibe a cadeia de caracteres formatada.

8. Escolha Depurar>Etapa de Saída. Outra maneira de interromper a execução passo


a passo é pressionar Shift + F11 .

A janela do console exibe uma mensagem e aguarda até que uma tecla seja
pressionada.

9. Pressione qualquer tecla para fechar a janela do console e sair do modo de


depuração.

Usar a configuração de build de lançamento


Depois de testar a versão de depuração do aplicativo, você deverá compilar e testar a
versão de lançamento. A versão de lançamento incorpora otimizações do compilador
que, às vezes, podem afetar negativamente o comportamento de um aplicativo. Por
exemplo, as otimizações de compilador que são projetadas para aprimorar o
desempenho podem criar condições de corrida em aplicativos multi-threaded.

Para compilar e testar a versão de lançamento do seu aplicativo de console, altere a


configuração de build na barra de ferramentas de Depuração para Lançamento.

Quando você pressiona F5 ou escolhe Compilar Solução no menu Build, o Visual


Studio compila a versão de lançamento do aplicativo. Você pode testá-la como fez com
a versão de depuração.

Próximas etapas
Neste tutorial, você usou as ferramentas de depuração do Visual Studio. No próximo
tutorial, você publicará uma versão implantável do aplicativo.

Publicar um aplicativo de console .NET usando o Visual Studio


Tutorial: Publicar um aplicativo de
console do .NET usando Visual Studio
Artigo • 28/08/2023

Este tutorial mostra como publicar um aplicativo de console para que outros usuários
possam executá-lo. A publicação cria o conjunto de arquivos necessários para executar
seu aplicativo. Para implantar os arquivos, copie-os para o computador de destino.

Pré-requisitos
Este tutorial funciona com o aplicativo de console que você cria em Criar um
aplicativo de console .NET usando o Visual Studio.

Publicar o aplicativo
1. Inicie o Visual Studio.

2. Abra o projeto HelloWorld que você criou em Criar um aplicativo de console .NET
usando o Visual Studio.

3. Verifique se o Visual Studio está usando a configuração de build de versão. Se


necessário, altere a configuração de build na barra de ferramentas de Depuração
para Lançamento.

4. Clique com o botão direito do mouse no projeto HelloWorld (e não na solução


HelloWorld) e selecione Publicar no menu.
5. Na guia Destino da página Publicar, selecione Pasta e selecione Avançar.

6. Na guia Especificar destino da página Publicar, selecione Pasta e selecione


Avançar.
7. Na guia Local da página Publicar, selecione Concluir.

8. Na página Publicar progresso de criação do perfil, selecione Fechar.

9. Na guia Publicar da janela Publicar, selecione Publicar.


Inspecionar os arquivos
Por padrão, o processo de publicação cria uma implantação dependente de estrutura,
que é o um tipo de implantação em que o aplicativo publicado é executado em um
computador que tenha o runtime do .NET instalado. Os usuários podem executar o
aplicativo publicado clicando duas vezes no executável ou emitindo o comando dotnet
HelloWorld.dll a partir de um prompt de comando.

Nas etapas a seguir, você examinará os arquivos criados pelo processo de publicação.

1. Em Gerenciador de Soluções, clique Mostrar todos os arquivos.

2. Na pasta do projeto, expanda bin/Release/net7.0/publish.


Como mostra a imagem anterior, a saída publicada inclui os seguintes arquivos:

HelloWorld.deps.json

Esse é o arquivo de dependências de runtime do aplicativo. Ele define os


componentes e as bibliotecas do .NET (incluindo a biblioteca de vínculo
dinâmico que contém o aplicativo) necessários para executar o aplicativo.
Para obter mais informações, confira Arquivos de configuração de runtime .

HelloWorld.dll

Essa é a versão de implantação dependente de estrutura do aplicativo. Para


executar essa biblioteca de links dinâmicos, insira dotnet HelloWorld.dll em
um prompt de comando. Esse método de execução do aplicativo funciona
em qualquer plataforma que tenha o runtime do .NET instalado.

HelloWorld.exe

Essa é a versão do executável dependente da estrutura do aplicativo. Para


executá-lo, insira HelloWorld.exe em um prompt de comando. O arquivo é
específico do sistema operacional.
HelloWorld.pdb (opcional para implantação)

Esse é o arquivo de símbolos de depuração. Não é necessário implantar esse


arquivo juntamente com seu aplicativo, embora você deva salvá-lo caso
precise depurar a versão publicada do seu aplicativo.

HelloWorld.runtimeconfig.json

Esse é o arquivo de configuração de runtime do aplicativo. Ele identifica a


versão do .NET com base na qual o aplicativo foi criado para ser executado.
Você também pode adicionar opções de configuração a ele. Para obter mais
informações, confira Definições de configuração de runtime do .NET.

Executar o aplicativo publicado


1. Em Gerenciador de Soluções, clique com o botão direito do mouse na pasta
publish e selecione Copiar caminho completo.

2. Abra um prompt de comando e navegue até a pasta publish. Para fazer isso, insira
cd e cole o caminho completo. Por exemplo:

Console

cd C:\Projects\HelloWorld\bin\Release\net8.0\publish\

3. Execute o aplicativo usando o comando :

a. Insira HelloWorld.exe e pressione ENTER .

b. Insira um nome em resposta à solicitação e pressione qualquer tecla para sair.

4. Execute o aplicativo usando o comando dotnet :

a. Insira dotnet HelloWorld.dll e pressione ENTER .

b. Insira um nome em resposta à solicitação e pressione qualquer tecla para sair.

Recursos adicionais
Implantação de aplicativos .NET
Publicar aplicativos .NET com a CLI do .NET
dotnet publish
Tutorial: publicar um aplicativo de console do .NET usando Visual Studio Code
Usar o SDK do .NET em ambientes de CI (integração contínua)

Próximas etapas
Neste tutorial, você publicou um aplicativo de console. No próximo tutorial, você criará
uma biblioteca de classes.

Criar uma biblioteca de classes .NET usando o Visual Studio


Tutorial: criar uma biblioteca de classes
.NET usando o Visual Studio
Artigo • 28/08/2023

Neste tutorial, você vai criar uma biblioteca de classes simples que contém um só
método de manipulação de cadeia de caracteres.

Uma biblioteca de classes define tipos e métodos que são chamados por um aplicativo.
Se a biblioteca for direcionada ao .NET Standard 2.0, ela poderá ser chamada por
qualquer implementação do .NET (incluindo o .NET Framework) que dê suporte ao .NET
Standard 2.0. Se a biblioteca tiver como destino o .NET 8, ela poderá ser chamada por
qualquer aplicativo que tenha como destino o .NET 8. Este tutorial mostra como fazer
para o destino do .NET 8.

Ao criar uma biblioteca de classes, você pode distribuí-la como um pacote NuGet ou
como um componente no pacote com o aplicativo que a usa.

Pré-requisitos
Prévia do Visual Studio 2022 com a carga de trabalho de desenvolvimento de
desktop .NET instalada. O SDK .NET 8 é instalado automaticamente quando você
seleciona essa carga de trabalho.

Para obter mais informações, confira Instalar o SDK do .NET com o Visual Studio.

Criar uma solução


Comece criando uma solução em branco para colocar o projeto de biblioteca de classes.
Uma solução do Visual Studio serve como um contêiner para um ou mais projetos. Você
adicionará projetos adicionais relacionados à mesma solução.

Para criar a solução em branco:

1. Inicie o Visual Studio.

2. Na tela Iniciar, selecione Criar um novo projeto.

3. Na página Criar um projeto, insira solução na caixa de pesquisa. Selecione o


modelo Solução em branco e clique em Avançar.
4. Na página Configurar o novo projeto, insira ClassLibraryProjects na caixa Nome
da solução. Em seguida, escolha Criar.

Criar um projeto de biblioteca de classes


1. Adicione um novo projeto de biblioteca de classes .NET chamado "StringLibrary"
para a solução.

a. Clique com o botão direito do mouse na solução em Gerenciador de Soluções


e selecione Adicionar>Novo Projeto.

b. Na página Adicionar um novo projeto, insira biblioteca na caixa de pesquisa.


Escolha C# ou Visual Basic na lista Linguagem e depois Todas as plataformas
na lista Plataforma. Escolha o modelo Biblioteca de Classes e escolha Avançar.

c. Na página Configurar o novo projeto, insira StringLibrary na caixa Nome do


projeto e clique em Avançar.

d. Na página Informações adicionais, selecione .NET 8 (Prévia) e, em seguida,


Criar.

2. Verifique se a biblioteca é direcionada à versão correta do .NET. Clique com o


botão direito do mouse no projeto de biblioteca no Gerenciador de Soluções e
selecione Propriedades. A caixa de texto Estrutura de Destino mostra que o
projeto tem como destino o .NET 7.0.
3. Se você estiver usando o Visual Basic, limpe o texto na caixa de texto do
Namespace raiz.

Para cada projeto, o Visual Basic cria automaticamente um namespace que


corresponde ao nome do projeto. Neste tutorial, você define um namespace de
nível superior usando a palavra-chave namespace no arquivo de código.

4. Substitua o código na janela de código de Class1.cs or Class1.vb pelo mostrado no


código a seguir e salve o arquivo. Se a linguagem que você quer usar não
aparecer, altere o seletor de linguagem na parte superior da página.

C#

namespace UtilityLibraries;

public static class StringLibrary


{
public static bool StartsWithUpper(this string? str)
{
if (string.IsNullOrWhiteSpace(str))
return false;

char ch = str[0];
return char.IsUpper(ch);
}
}

A biblioteca de classes UtilityLibraries.StringLibrary contém um método


chamado StartsWithUpper . Esse método retorna um valor Boolean que indica se a
instância atual da cadeia de caracteres começa com um caractere maiúsculo. O
padrão Unicode distingue caracteres maiúsculos de caracteres minúsculos. O
método Char.IsUpper(Char) retornará true se um caractere for maiúsculo.
StartsWithUpper é implementado como um método de extensão para que você

possa chamá-lo como se ele fosse um membro da classe String. O ponto de


interrogação ( ? ) depois de string no código C# indica que a cadeia de caracteres
pode ser nula.

5. Na barra de menus, selecione Criar>Criar Solução ou pressione Ctrl + Shift + B

para verificar se o projeto é compilado sem erro.

Adicionar um aplicativo de console à solução


Adicione um aplicativo de console que usa a biblioteca de classes. O aplicativo solicitará
que o usuário insira uma cadeia de caracteres e informe se a cadeia de caracteres
começa com um caractere maiúsculo.

1. Adicione um novo aplicativo de console .NET chamado "ShowCase" à solução.

a. Clique com o botão direito do mouse na solução em Gerenciador de Soluções


e selecione Adicionar>Novo projeto.

b. Na página Adicionar um projeto, insira console na caixa de pesquisa. Escolha


C# ou Visual Basic na lista Linguagem e depois Todas as plataformas na lista
Plataforma.

c. Escolha o modelo Aplicativo de Console e selecione Avançar.

d. Na página Configurar o novo projeto, insira ShowCase na caixa Nome do


projeto. Em seguida, escolha Avançar.

e. Na página Informações Adicionais, selecione .NET 8 (Prévia) na caixa


Framework. Em seguida, escolha Criar.

2. Na janela de código do arquivo Program.cs ou Program.vb, substitua todo o


código pelo código a seguir.

C#

using UtilityLibraries;

class Program
{
static void Main(string[] args)
{
int row = 0;

do
{
if (row == 0 || row >= 25)
ResetConsole();

string? input = Console.ReadLine();


if (string.IsNullOrEmpty(input)) break;
Console.WriteLine($"Input: {input}");
Console.WriteLine("Begins with uppercase? " +
$"{(input.StartsWithUpper() ? "Yes" : "No")}");
Console.WriteLine();
row += 4;
} while (true);
return;

// Declare a ResetConsole local method


void ResetConsole()
{
if (row > 0)
{
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
Console.Clear();
Console.WriteLine($"{Environment.NewLine}Press <Enter> only
to exit; otherwise, enter a string and press <Enter>:
{Environment.NewLine}");
row = 3;
}
}
}

O código usa a variável row ​para manter uma contagem do número de linhas de
dados gravadas na janela do console. Sempre que ela for igual ou superior a 25, o
código limpa a janela do console e exibe uma mensagem para o usuário.

O programa solicita que o usuário insira uma cadeia de caracteres. Ele indica se a
cadeia de caracteres começa com um caractere maiúsculo. Se o usuário pressionar
a tecla Enter sem inserir uma cadeia de caracteres, o aplicativo será encerrado e a
janela do console será fechada.

Adicionar uma referência ao projeto


Inicialmente, o novo projeto de aplicativo de console não tem acesso à biblioteca de
classes. Para permitir que ele chame métodos na biblioteca de classes, crie uma
referência de projeto para o projeto da biblioteca de classes.

1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó


Dependências do projeto ShowCase e selecione Adicionar Referência de Projeto.
2. Na caixa de diálogo Gerenciador de Referências, selecione o projeto StringLibrary
e clique no botão OK.

Executar o aplicativo
1. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto
ShowCase e selecione Definir como Projeto de Inicialização no menu de contexto.
2. Pressione Ctrl + F5 para compilar e executar o programa sem depuração.

3. Experimente o programa inserindo cadeias de caracteres e pressionando Enter .


Depois pressione Enter para sair.

Recursos adicionais
Desenvolver bibliotecas com a CLI do .NET
Versões do .NET Standard e plataformas com suporte.

Próximas etapas
Neste tutorial, você criou uma biblioteca de classes. No próximo tutorial, você
aprenderá a fazer um teste de unidade na biblioteca de classes.

Fazer um teste de unidade em uma biblioteca de classes .NET usando o Visual


Studio

Ou você pode ignorar o teste de unidade automatizado e aprender a compartilhar a


biblioteca criando um pacote NuGet:

Criar e publicar um pacote usando o Visual Studio

Ou aprenda a publicar um aplicativo de console. Se você publicar o aplicativo de


console por meio da solução criada neste tutorial, a biblioteca de classes o usará como
um arquivo .dll.

Publicar um aplicativo de console .NET usando o Visual Studio


Tutorial: Testar uma biblioteca de classes
.NET com o .NET usando o Visual Studio
Artigo • 28/08/2023

Este tutorial mostra como automatizar o teste de unidade adicionando um projeto de


teste a uma solução.

Pré-requisitos
Este tutorial funciona com a solução que você cria em Criar uma biblioteca de
classes do .NET usando o Visual Studio.

Criar um projeto de teste de unidade


As unidade de teste fornecem testes de software automatizados durante o
desenvolvimento e a publicação. MSTest é uma das três estruturas de teste que você
pode escolher. As outras são xUnit e nUnit .

1. Inicie o Visual Studio.

2. Abra a solução ClassLibraryProjects criada em Criar uma biblioteca de classes do


.NET usando o Visual Studio.

3. Adicione um novo projeto de teste de unidade chamado "StringLibraryTest" à


solução.

a. Clique com o botão direito do mouse na solução em Gerenciador de Soluções


e escolha Adicionar>Novo projeto.

b. Na página Adicionar um novo projeto, insira mstest na caixa de pesquisa.


Escolha C# ou Visual Basic na lista Linguagem e depois Todas as plataformas
na lista Plataforma.

c. Escolha o modelo do Projeto de Teste MSTest e, em seguida, Avançar.

d. Na página Configurar seu novo projeto, insira StringLibraryTest na caixa Nome


do projeto. Em seguida, escolha Avançar.

e. Na página Informações Adicionais, selecione .NET 8 (Prévia) na caixa


Framework. Em seguida, escolha Criar.
4. O Visual Studio cria o projeto e abre o arquivo de classe na janela do código, com
o código a seguir. Se a linguagem que você quer usar não aparecer, altere o
seletor de linguagem na parte superior da página.

C#

namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}

O código-fonte criado pelo modelo de teste de unidade faz o seguinte:

Ele importa o namespace Microsoft.VisualStudio.TestTools.UnitTesting, que


contém os tipos usados para o teste de unidade. Em C#, o namespace é
importado por meio de uma diretiva global using em GlobalUsings.cs.
Ele aplica o atributoTestClassAttribute à classe UnitTest1 .
Ele aplica o atributo TestMethodAttribute para definir TestMethod1 em C# ou
TestSub no Visual Basic.

Cada método marcado com [TestMethod] em uma classe de teste marcada com
[TestClass] é executado automaticamente quando o teste de unidade é executado.

Adicionar uma referência ao projeto


Para que o projeto de teste funcione com a classe StringLibrary , adicione uma
referência no projeto StringLibraryTest ao projeto StringLibrary .

1. No Gerenciador de Soluções, clique com o botão direito do mouse no nó


Dependências do projeto StringLibraryTest e selecione Adicionar Referência do
Projeto no menu de contexto.

2. Na caixa de diálogo Gerenciador de Referências, expanda o nó Projetos e


selecione a caixa ao lado de StringLibrary. A adição de uma referência ao
assembly StringLibrary permite que o compilador localize os métodos
StringLibrary durante a compilação do projeto StringLibraryTest.
3. Selecione OK.

Adicionar e executar métodos de teste de


unidade
Quando o Visual Studio executa um teste de unidade, ele executa cada método
marcado com o atributo TestMethodAttribute em uma classe marcada com o atributo
TestClassAttribute. Um método de teste termina quando a primeira falha é encontrada
ou quando todos os testes contidos no método têm êxito.

Os testes mais comuns chamam membros da classe Assert. Muitos métodos assert
incluem pelo menos dois parâmetros, um deles é o resultado esperado do teste, e o
outro é o resultado real do teste. Alguns dos métodos chamados com mais frequência
da classe Assert são mostrados na tabela a seguir:

Métodos assert Função

Assert.AreEqual Verifica se os dois valores ou objetos são iguais. O assert falha se os valores
ou objetos não forem iguais.

Assert.AreSame Verifica se duas variáveis de objeto se referem ao mesmo objeto. A assert


falhará se as variáveis se referirem a objetos diferentes.

Assert.IsFalse Verifica se uma condição é false . O assert falhará se a condição for true .

Assert.IsNotNull Verifica se um objeto não é null . A assert falhará se o objeto for null .

Você também pode usar o método Assert.ThrowsException em um método de teste


para indicar o tipo de exceção que ele deve gerar. O teste falhará se a exceção
especificada não for lançada.

Ao testar o método StringLibrary.StartsWithUpper , você quer fornecer um número de


cadeias de caracteres que comecem com um caractere maiúsculo. Você espera que o
método retorne true nesses casos, para que possa chamar o método Assert.IsTrue. Da
mesma forma, você deseja fornecer um número de cadeias de caracteres que comecem
com algo diferente de um caractere maiúsculo. Você espera que o método retorne
false nesses casos, para que possa chamar o método Assert.IsFalse.

Como seu método de biblioteca lida com cadeias de caracteres, convém ter certeza de
que ele manipulará com êxito uma cadeia de caracteres vazia (String.Empty), uma cadeia
de caracteres válida sem caracteres e cujo Length é 0 e uma cadeia de caracteres null
que não foi inicializada. Você pode chamar StartsWithUpper diretamente como um
método estático e passar um único argumento String. Ou você pode chamar
StartsWithUpper como um método de extensão em uma variável string atribuída a
null .

Você definirá três métodos, cada um deles chamará um método Assert para cada
elemento em uma matriz de cadeia de caracteres. Você chamará uma sobrecarga de
método que permite especificar uma mensagem de erro a ser exibida em caso de falha
no teste. A mensagem identifica a cadeia de caracteres que causou a falha.

Para criar os métodos de teste:

1. Na janela de código UnitTest1.cs ou UnitTest1.vb, substitua o código pelo seguinte:

C#

using UtilityLibraries;

namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestStartsWithUpper()
{
// Tests that we expect to return true.
string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα",
"Москва" };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsTrue(result,
string.Format("Expected for '{0}': true; Actual:
{1}",
word, result));
}
}

[TestMethod]
public void TestDoesNotStartWithUpper()
{
// Tests that we expect to return false.
string[] words = { "alphabet", "zebra", "abc",
"αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsFalse(result,
string.Format("Expected for '{0}': false;
Actual: {1}",
word, result));
}
}

[TestMethod]
public void DirectCallWithNullOrEmpty()
{
// Tests that we expect to return false.
string?[] words = { string.Empty, null };
foreach (var word in words)
{
bool result = StringLibrary.StartsWithUpper(word);
Assert.IsFalse(result,
string.Format("Expected for '{0}': false;
Actual: {1}",
word == null ? "<null>" : word,
result));
}
}
}
}

O teste de caracteres maiúsculos no método TestStartsWithUpper inclui a letra


maiúscula grega alfa (U+0391) e a letra maiúscula cirílica EM (U+041C). O teste de
caracteres minúsculos no método TestDoesNotStartWithUpper inclui a letra
minúscula grega alfa (U+03B1) e a letra minúscula cirílica Ghe (U+0433).

2. Na barra de menus, selecione Arquivo>Salvar UnitTest1.cs Como ou


Arquivo>Salvar UnitTest1.vb Como. Na caixa de diálogo Salvar Arquivo Como,
selecione a seta ao lado do botão Salvar e selecione Salvar com Codificação.
3. Na caixa de diálogo Confirmar Salvar Como, selecione o botão Sim para salvar o
arquivo.

4. Na caixa de diálogo Opções Avançadas de Salvamento, selecione Unicode (UTF-8


com assinatura) – página de código 65001 na lista suspensa Codificação e
selecione OK.

Se você não salvar seu código-fonte como um arquivo codificado em UTF8, o


Visual Studio poderá salvá-lo como um arquivo ASCII. Quando isso acontecer, o
runtime não decodificará de forma precisa os caracteres UTF8 fora do intervalo
ASCII e os resultados de teste não serão corretos.

5. Na barra de menus, selecione Testar>Executar Todos os Testes. Se a janela do


Gerenciador de Testes não abrir, abra-a escolhendo Teste>Gerenciador de Testes.
Os três testes estão listados na seção Testes Aprovados, e a seção Resumo relata o
resultado da execução de teste.

Identificar falhas de teste


Se você estiver fazendo o TDD (Desenvolvimento Orientado por Testes), você grava os
testes primeiro e eles falharão na execução. Em seguida, você adiciona o código ao
aplicativo que faz com que o teste seja bem-sucedido. Neste tutorial, você criou o teste
depois de gravar o código do aplicativo que ele valida, portanto, você não viu o teste
falhar. Para validar se um teste falha conforme o esperado, adicione um valor inválido à
entrada de teste.

1. Modifique a matriz words no método TestDoesNotStartWithUpper para incluir a


cadeia de caracteres “Error”. Não é necessário salvar o arquivo porque o Visual
Studio salva automaticamente os arquivos abertos quando uma solução é criada
para executar testes.

C#

string[] words = { "alphabet", "Error", "zebra", "abc",


"αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };

2. Execute o teste selecionando Testar>Executar Todos os Testes na barra de menus.


A Janela Gerenciador de Testes indica que dois testes tiveram êxito e um falhou.
3. Selecione o teste com falha, TestDoesNotStartWith .

A janela Gerenciador de Testes mostra a mensagem produzida pelo assert:


"Assert.IsFalse falhou. Esperado para 'Error': false, real: True". Devido à falha,
nenhuma cadeia de caracteres na matriz após "Error" foi testada.

4. Remova a cadeia de caracteres "Error" que você adicionou na etapa 1. Execute


novamente o teste, e ele será aprovado.

Testar a versão de lançamento da biblioteca


Agora que todos os nossos testes foram aprovados ao executar a compilação de
Depuração da biblioteca, execute os testes mais uma vez na compilação de Lançamento
da biblioteca. Vários fatores, incluindo as otimizações do compilador, podem produzir
um comportamento diferente entre as compilações de Depuração e Lançamento.

Para testar a compilação de Lançamento:

1. Na barra de ferramentas do Visual Studio, altere a configuração de compilação de


Depurar para Lançamento.
2. No Gerenciador de Soluções, clique com o botão direito do mouse no projeto
StringLibrary e selecione Compilar no menu de contexto para recompilar a
biblioteca.

3. Execute os testes de unidade escolhendo Testar>Executar todos os testes da barra


de menu. Os testes são aprovados.

Depurar testes
Se você estiver usando o Visual Studio como seu IDE, será possível usar o mesmo
processo mostrado no Tutorial: Depurar um aplicativo de console do .NET usando o
Visual Studio para depurar o código usando seu projeto de teste de unidade. Em vez de
iniciar o projeto do aplicativo ShowCase, clique com o botão direito do mouse no
projeto StringLibraryTests e selecione Testes de Depuração no menu de contexto.

O Visual Studio inicia o projeto de teste com o depurador anexado. A execução será
interrompida em qualquer ponto de interrupção adicionado ao projeto de teste ou ao
código da biblioteca subjacente.

Recursos adicionais
Noções básicas sobre testes de unidade – Visual Studio
Teste de unidade no .NET

Próximas etapas
Neste tutorial, você testou uma biblioteca de classes. Você pode disponibilizar a
biblioteca para outras pessoas publicando-a no NuGet como um pacote. Para saber
como, siga um tutorial do NuGet:

Criar e publicar um pacote NuGet usando o Visual Studio


Se você publicar uma biblioteca como um pacote NuGet, outras pessoas poderão
instalá-la e usá-la. Para saber como, siga um tutorial do NuGet:

Instalar e usar um pacote no Visual Studio

Uma biblioteca não precisa ser distribuída como um pacote. Ela pode ser empacotada
com um aplicativo de console que a usa. Para saber como publicar um aplicativo de
console, consulte o tutorial anterior nesta série:

Publicar um aplicativo de console .NET usando o Visual Studio


Início Rápido: Instalar e usar um pacote
NuGet no Visual Studio (somente
Windows)
Artigo • 22/12/2023

Um pacote NuGet contém código reutilizável que outros desenvolvedores


disponibilizam para uso em seus projetos. Você pode instalar um pacote NuGet em um
projeto do Microsoft Visual Studio usando o Gerenciador de Pacotes NuGet, o Console
do Gerenciador de Pacotes ou a CLI do .NET. Este artigo demonstra como criar um
projeto do Windows Presentation Foundation (WPF) com o popular pacote
Newtonsoft.Json . O mesmo processo se aplica a qualquer outro projeto .NET ou .NET

Core.

Depois de instalar um pacote NuGet, você poderá fazer uma referência a ele em seu
código com a instrução using <namespace> , onde <namespace> é o nome do pacote
que você está usando. Depois que a referência for feita, você poderá chamar o pacote
por meio de sua API.

O artigo destina-se apenas a usuários do Windows. Se você estiver usando o Visual


Studio para Mac, consulte Instalar e usar um pacote no Visual Studio para Mac.

 Dica

Para encontrar um pacote NuGet, comece com nuget.org: navegar em nuget.org é


como os desenvolvedores em .NET costumam encontrar os componentes que
podem reutilizar em seus próprios aplicativos. Você pode pesquisar em nuget.org
diretamente ou localizar e instalar pacotes de dentro do Visual Studio, conforme
mostrado neste artigo. Para obter mais informações, consulte Procurar e avaliar
pacotes NuGet.

Pré-requisitos
Instale o Visual Studio 2022 para Windows com a carga de trabalho de
desenvolvimento para desktop do .NET

É possível instalar a edição Community 2022 gratuitamente em


visualstudio.microsoft.com ou usar a edição Professional ou Enterprise.
Criar um projeto
É possível instalar um pacote do NuGet em qualquer projeto .NET desde que o pacote
ofereça suporte à mesma estrutura de destino que o projeto. No entanto, para este
início rápido, você criará um aplicativo do WPF (Windows Presentation Foundation).

Siga estas etapas:

1. No Visual Studio, selecione Arquivo>Novo>Projeto.

2. Na janela Criar um novo projeto, digite WPF na caixa de pesquisa e selecione C# e


Windows nas listas suspensas. Na lista resultante de modelos de projeto, selecione
Aplicativo WPF e, em seguida, selecione Avançar.

3. Na janela Configurar seu novo projeto, opcionalmente, atualize o Nome do


projeto e o Nome da solução e selecione Avançar.

4. Na janela Informações adicionais, selecione .NET 6.0 (ou a versão mais recente)
como Estrutura e, em seguida, selecione Criar.

O Visual Studio cria e abre o projeto no Gerenciador de Soluções.

Adicionar o pacote do NuGet Newtonsoft.Json


Para instalar um pacote do NuGet, neste início rápido, você pode usar o Gerenciador de
Pacotes NuGet ou o Console do Gerenciador de Pacotes. Dependendo do formato do
projeto, a instalação de um pacote NuGet registra a dependência no arquivo de projeto
ou em um arquivo packages.config. Para obter mais informações, consulte Fluxo de
trabalho do consumo de pacotes.

Gerenciador de Pacotes NuGet


Para usar o Gerenciador de Pacotes NuGet para instalar o pacote Newtonsoft.Json no
Visual Studio, siga estas etapas:

1. Selecione Projeto>Gerenciar Pacotes do NuGet.

2. Na página Gerenciador de Pacotes do NuGet, selecione nuget.org como a


Origem do pacote.

3. Na guia Procurar, procure Newtonsoft.Json, selecione Newtonsoft.Json na lista e


selecione Instalar.
4. Se for solicitado que você verifique a instalação, selecione OK.

Console do Gerenciador de Pacotes


Para usar o Console do Gerenciador de Pacotes no Visual Studio para instalar o pacote
Newtonsoft.Json , siga estas etapas:

1. No Visual Studio, selecione Ferramentas>Gerenciador de Pacotes


NuGet>Console do Gerenciador de Pacotes

2. Após o painel Console do Package Manager abrir, verifique se a lista suspensa


Projeto padrão mostra o projeto no qual você deseja instalar o pacote. Se você
tiver um único projeto na solução, ele estará pré-selecionado.

3. No prompt do console, insira o comando Install-Package Newtonsoft.Json . Para


obter mais informações sobre esse comando, consulte Instalar pacote.

A janela do console mostra a saída do comando. Os erros indicam que o pacote


não é compatível com a estrutura de destino do projeto.
Use a API Newtonsoft.Json no aplicativo
Com o pacote Newtonsoft.Json no projeto, chame seu método
JsonConvert.SerializeObject para converter um objeto em uma sequência legível por

pessoas:

1. No Gerenciador de Soluções, abra MainWindow.xaml e substitua o elemento


<Grid> existente pelo seguinte código:

XAML

<Grid Background="White">
<StackPanel VerticalAlignment="Center">
<Button Click="Button_Click" Width="100px"
HorizontalAlignment="Center" Content="Click Me" Margin="10"/>
<TextBlock Name="TextBlock" HorizontalAlignment="Center"
Text="TextBlock" Margin="10"/>
</StackPanel>
</Grid>

2. Abra o arquivo MainWindow.xaml.cs no nó MainWindow.xaml e insira o seguinte


código dentro da classe MainWindow após o construtor:

C#

public class Account


{
public string Name { get; set; }
public string Email { get; set; }
public DateTime DOB { get; set; }
}

private void Button_Click(object sender, RoutedEventArgs e)


{
Account account = new Account
{
Name = "John Doe",
Email = "john@microsoft.com",
DOB = new DateTime(1980, 2, 20, 0, 0, 0, DateTimeKind.Utc),
};
string json = JsonConvert.SerializeObject(account,
Newtonsoft.Json.Formatting.Indented);
TextBlock.Text = json;
}

3. Para evitar um erro para o objeto JsonConvert no código (uma linha de texto
sublinhado vermelha aparecerá), adicione a seguinte instrução no início do arquivo
de código:
C#

using Newtonsoft.Json;

4. Para compilar e executar o aplicativo, pressione F5 ou selecione Depurar>Iniciar


depuração.

A seguinte janela é exibida:

5. Selecione o botão Clique em mim para ver o conteúdo do objeto TextBlock


substituído com algum texto JSON:

Vídeo relacionado
Instalar e usar um pacote NuGet no Visual Studio
Encontre mais vídeos sobre o NuGet no Channel 9 e no YouTube .

Confira também
Para obter mais informações sobre o NuGet, consulte os seguintes artigos:

O que é o NuGet?
Fluxo de trabalho de consumo do pacote
Localizar e escolher pacotes
Referências de pacotes em arquivos de projeto
Instalar e usar um pacote usando a CLI do .NET
Pacote Newtonsoft.Json

Próximas etapas
Parabéns por instalar e usar seu primeiro pacote NuGet. Avance para o próximo artigo
para saber mais sobre como instalar e gerenciar pacotes NuGet.

Instalar e gerenciar pacotes usando o Gerenciador de Pacotes NuGet

Instalar e gerenciar pacotes usando o Console do Gerenciador de Pacotes

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Início Rápido: Criar e publicar um
pacote NuGet usando o Visual Studio
(somente no Windows)
Artigo • 22/12/2023

Com o Microsoft Visual Studio, você pode criar um pacote NuGet desde uma biblioteca
de classes .NET e publicá-lo em nuget.org usando uma ferramenta de CLI.

O guia de início rápido destina-se apenas a usuários do Windows. Se você estiver


usando o Visual Studio para Mac, consulte Criar um pacote NuGet a partir de projetos
de biblioteca existentes ou use a CLI do .NET.

Pré-requisitos
Instale o Visual Studio 2022 para Windows com qualquer carga de trabalho
relacionada ao .NET Core.

É possível instalar a edição Community 2022 gratuitamente em


visualstudio.microsoft.com ou usar a edição Professional ou Enterprise.

O Visual Studio 2017 em diante inclui automaticamente os recursos do NuGet


quando uma carga de trabalho relacionada ao .NET é instalada.

Instale a CLI do .NET se ela ainda não estiver instalada.

No Visual Studio 2017 em diante, a CLI do .NET é instalada automaticamente com


qualquer carga de trabalho relacionada ao .NET Core. Caso contrário, instale o SDK
do .NET Core para ter a CLI do .NET. A CLI do .NET é necessária para projetos do
.NET que usam o formato de estilo SDK (atributo do SDK). O modelo de biblioteca
de classes .NET padrão no Visual Studio 2017 em diante usa o atributo SDK.

) Importante

Se você estiver trabalhando com um projeto estilo não SDK, siga os


procedimentos em Criar e publicar um pacote de .NET Framework (Visual
Studio) para criar e publicar o pacote. Para este artigo, a CLI do .NET é
recomendada. Embora você possa publicar qualquer pacote NuGet usando a
CLI do NuGet, algumas das etapas neste artigo são específicas para projetos
no estilo SDK e a CLI do .NET. A CLI do NuGet é usada para projetos estilo
não SDK (normalmente, .NET Framework).

Registre-se em uma conta gratuita em nuget.org, se ainda não tiver uma. Você
deverá se registrar confirmar a conta antes de poder carregar um pacote do
NuGet.

Instale a CLI do NuGet baixando-a de nuget.org . Adicione o arquivo nuget.exe


em uma pasta adequada e adicione essa pasta à variável de ambiente PATH.

Criar um projeto de biblioteca de classes


Você pode usar um projeto existente da Biblioteca de Classes .NET para o código que
você deseja empacotar, ou criar um da seguinte maneira:

1. No Visual Studio, selecione Arquivo>Novo>Projeto.

2. Na janela Criar um novo projeto, selecione C#, Windows e Biblioteca nas listas
suspensas.

3. Na lista resultante de modelos de projeto, selecione Biblioteca de Classes (com a


descrição Um projeto para criar uma biblioteca de classes destinada ao .NET ou
.NET Standard) e selecione Avançar.

4. Na caixa de diálogo Configurar novo projeto, insira AppLogger como Nome do


projeto e selecione Avançar.

5. Na janela Informações adicionais, selecione uma Estrutura apropriada e selecione


Criar.

Se você não tiver certeza de qual estrutura selecionar, a mais recente é uma boa
escolha e pode ser facilmente alterada mais tarde. Para obter informações sobre
qual estrutura usar, consulte Quando o objetivo é o .NET 5.0 ou .NET 6.0 vs. .NET
Standard.

6. Para garantir que o projeto tenha sido criado corretamente, selecione


Compilar>Compilar solução. A DLL é encontrada dentro da pasta de Depuração
(ou Versão, se você compilar essa configuração).

7. (Opcional) Para este guia de início rápido, você não precisa escrever nenhum
código adicional para o pacote NuGet porque a biblioteca de classes de modelo é
suficiente para criar um pacote. No entanto, se você desejar algum código
funcional para o pacote, inclua o seguinte código:
C#

namespace AppLogger
{
public class Logger
{
public void Log(string text)
{
Console.WriteLine(text);
}
}
}

Configurar propriedades do pacote


Depois de criar seu projeto, você poderá configurar as propriedades do pacote NuGet
seguindo estas etapas:

1. Selecione seu projeto no Gerenciador de Soluções e, em seguida, selecione


Projeto>Propriedades de <nome do projeto>, onde <nome do projeto> é o
nome do seu projeto.

2. Expanda o nó Pacote e selecione Geral.

O nó Pacote aparece somente para projetos estilo SDK no Visual Studio. Se seu
objetivo for um projeto estilo não SDK (normalmente .NET Framework), migre o
projeto ou consulte Criar e publicar um pacote do .NET Framework para obter
instruções passo a passo.
3. Para pacotes compilados para consumo público, preste atenção especial à
propriedade Tags, à medida que as marcas ajudam outras pessoas a localizar o
pacote e entender o que ele faz.

4. Dê ao seu pacote um ID de pacote exclusivo e preencha as outras propriedades


desejadas. Para ver uma tabela que mostre como as propriedades do MSBuild
(projeto estilo SDK) são mapeadas em um arquivo .nuspec, consulte destinos do
pacote. Para obter uma descrição das propriedades do arquivo .nuspec, consulte a
referência do arquivo .nuspec. Todas essas propriedades vão para o manifesto
.nuspec criado pelo Visual Studio para o projeto.

) Importante

Você precisa dar ao pacote um identificador exclusivo em nuget.org ou


qualquer host que você esteja usando. Caso contrário, ocorrerá um erro. Para
este início rápido, recomendamos incluir Exemplo ou Teste no nome, pois a
etapa de publicação torna o pacote visível publicamente.

5. (Opcional) Para ver as propriedades diretamente no arquivo de projeto


AppLogger.csproj, selecione Projeto>Editar arquivo de projeto.

A guia AppLogger.csproj é carregada.


Essa opção está disponível a partir do Visual Studio 2017 para projetos que usam o
atributo estilo SDK. Para versões anteriores do Visual Studio, você deve selecionar
Projeto>Descarregar projeto antes de editar o arquivo de projeto.

Executar o comando pack


Para criar um pacote NuGet a partir do seu projeto, siga estas etapas:

1. Selecione Compilar>Gerenciador de Configuração e defina a Configuração da


solução ativa como Release.

2. Selecione o projeto AppLogger no Gerenciador de Soluções e selecione Pacote.

O Visual Studio compila o projeto e cria o arquivo .nupkg.

3. Examine a janela Saída para obter detalhes, os quais contêm o caminho até o
arquivo do pacote. Neste exemplo, o assembly compilado está em
bin\Release\net6.0 como convém a um destino do .NET 6.0:

Saída

1>------ Build started: Project: AppLogger, Configuration: Release Any


CPU ------
1>AppLogger ->
d:\proj\AppLogger\AppLogger\bin\Release\net6.0\AppLogger.dll
1>Successfully created package
'd:\proj\AppLogger\AppLogger\bin\Release\AppLogger.1.0.0.nupkg'.
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped
==========

4. Se você não vir o comando Pack no menu, seu projeto provavelmente não será um
projeto estilo SDK e você precisará usar a CLI do NuGet. Ou migre o projeto e use
a CLI do .NET ou consulte Criar e publicar um pacote do .NET Framework para
obter instruções passo a passo.

(Opcional) Gerar pacote ao compilar


Você pode configurar o Visual Studio para gerar automaticamente o pacote NuGet ao
compilar o projeto:

1. Selecione seu projeto no Gerenciador de Soluções e, em seguida, selecione


Projeto>Propriedades de <nome do projeto>, onde <nome do projeto> é o
nome do seu projeto (nese caso, AppLogger).
2. Expanda o nó Pacote, selecione Geral e, em seguida, selecione Gerar pacote
NuGet na compilação.

7 Observação

Ao gerar automaticamente o pacote, o tempo adicional para empacotamento


aumenta o tempo de compilação do seu projeto.

(Opcional) Empacotar com MSBuild


Como uma alternativa ao uso do comando de menu Pack, o NuGet 4.x+ e o MSBuild
15.1+ são compatíveis com um destino pack quando o projeto contém os dados do
pacote necessários:

1. Com o projeto aberto no Gerenciador de Soluções, abra um prompt de comando


selecionando Ferramentas>Linha de Comando>Prompt de Comando do
Desenvolvedor.

O prompt de comando é aberto no diretório do projeto.

2. Execute o seguinte comando: msbuild -t:pack .

Para saber mais, confira Criar um pacote usando MSBuild.


Publicar o pacote
Após criar um arquivo .nupkg, publique-o em nuget.org usando a CLI do .NET ou a CLI
do NuGet juntamente com uma chave de API adquirida em nuget.org.

7 Observação

Nuget.org verifica todos os pacotes carregados em busca de vírus e rejeita os


pacotes quando encontra um vírus. Nuget.org também verifica
periodicamente todos os pacotes listados.

Os pacotes que você publicar em nuget.org também são publicamente


visíveis para outros desenvolvedores, a menos que você os remova da lista.
Para hospedar pacotes de forma privada, consulte Hospedar seus próprios
feeds NuGet.

Adquirir a chave de sua API


Antes de publicar seu pacote NuGet, crie uma chave de API:

1. Entre em sua conta de nuget.org ou crie uma conta caso ainda não tenha uma.

2. Selecione seu nome de usuário no canto superior direito e selecione Chaves de


API.

3. Selecione Criar e forneça um nome para sua chave.

4. Em Selecionar Escopos, selecione Push.

5. Em Selecionar Pacotes>Padrão Glob, insira *.

6. Selecione Criar.

7. Selecione Copiar para copiar a nova chave.


) Importante

Sempre mantenha sua chave de API em segredo. A chave de API é como uma
senha que permite que qualquer pessoa gerencie pacotes em seu nome.
Exclua ou gere novamente sua chave de API se ela for revelada
acidentalmente.
Salve sua chave em um local seguro, pois não será possível copiá-la
novamente no futuro. Se você retornar à página da chave de API, será
necessário gerar novamente a chave para copiá-la. Também é possível
remover a chave de API, se você não quiser mais fazer o push de pacotes.

Os Escopos permitem criar chaves de API separadas para finalidades diferentes. Cada
chave tem um período de expiração e pode ter o escopo definido para pacotes ou
padrões glob específicos. Você também define o escopo de cada chave para operações
específicas: enviar novos pacotes e versões de pacotes, enviar por push apenas novas
versões de pacotes ou remover da lista.

Por meio de escopo, é possível criar chaves de API para diferentes pessoas que
gerenciam os pacotes para a sua organização, de modo que elas tenham somente as
permissões necessárias.

Para saber mais, confira Chaves de API com escopo.

Publicar com a CLI do .NET ou a CLI do NuGet


Cada uma das ferramentas de CLI a seguir permite enviar um pacote por push para o
servidor e publicá-lo. Selecione a guia para sua ferramenta de CLI, CLI do .NET ou CLI
do NuGet.
CLI do .NET

Usar a CLI do .NET (dotnet.exe) é a alternativa recomendada ao uso da CLI do


NuGet.

Na pasta que contém o arquivo .nupkg, execute o comando a seguir. Especifique o


nome do arquivo .nupkg e substitua o valor da chave pela chave da API.

CLI do .NET

dotnet nuget push Contoso.08.28.22.001.Test.1.0.0.nupkg --api-key


qz2jga8pl3dvn2akksyquwcs9ygggg4exypy3bhxy6w6x6 --source
https://api.nuget.org/v3/index.json

A janela de saída mostra os resultados do processo de publicação.

Saída

Pushing Contoso.08.28.22.001.Test.1.0.0.nupkg to
'https://www.nuget.org/api/v2/package'...
PUT https://www.nuget.org/api/v2/package/
warn : All published packages should have license information specified.
Learn more: https://aka.ms/nuget/authoring-best-practices#licensing.
Created https://www.nuget.org/api/v2/package/ 1221ms
Your package was pushed.

Para obter mais informações, confira dotnet nuget push.

7 Observação

Para evitar que seu pacote de teste fique permaneça em nuget.org, você pode
enviá-lo por push para o site de teste de nuget.org em
https://int.nugettest.org . Observe que os pacotes carregados para
int.nugettest.org podem não ser preservados.

Erros de publicação
Os erros do comando push geralmente indicam o problema. Por exemplo, talvez você
tenha esquecido de atualizar o número de versão em seu projeto e, portanto, está
tentando publicar um pacote que já existe.
Você também verá erros se sua chave de API for inválida ou tiver expirado, ou se você
tentar publicar um pacote usando um identificador que já existe no host. Suponha, por
exemplo, que o identificador AppLogger-test já exista em nuget.org. Se você tentar
publicar um pacote com esse identificador, o comando push resultará no seguinte erro:

Saída

Response status code does not indicate success: 403 (The specified API key
is invalid,
has expired, or does not have permission to access the specified package.).

Se você receber esse erro, verifique se está usando uma chave de API válida que ainda
não expirou. Se estiver, o erro indicará que o identificador do pacote já existe no host.
Para corrigir o erro, altere o identificador do pacote para torná-lo único, recompile o
projeto, recrie o arquivo .nupkg e tente novamente o comando push .

Gerenciar o pacote publicado


Quando seu pacote for publicado com êxito, você receberá um e-mail de confirmação.
Para ver o pacote que você acabou de publicar, em nuget.org , selecione seu nome de
usuário no canto superior direito e selecione Gerenciar pacotes.

7 Observação

Poderá demorar algum tempo para o pacote ser indexado e aparecer nos
resultados da pesquisa, onde outras pessoas podem encontrá-lo. Durante esse
tempo, o pacote aparece em Pacotes não listados e a página do pacote mostra a
seguinte mensagem:

Você acabou de publicar um pacote NuGet em nuget.org, o qual pode ser usado por
outros desenvolvedores podem usar em seus próprios projetos.

Se você criou um pacote que não é útil (como este pacote de exemplo que foi criado
com uma biblioteca de classes vazia) ou decidiu que não deseja que o pacote fique
visível, você poderá remover o pacote da lista para ocultá-lo dos resultados da pesquisa:

1. Depois que o pacote aparecer em Pacotes Publicados na página Gerenciar


Pacotes, selecione o ícone de lápis ao lado da listagem de pacotes.
2. Na próxima página, selecione Listagem, desmarque a caixa de seleção Listar nos
resultados da pesquisa e selecione Salvar.

O pacote agora aparece em Pacotes não listados em Gerenciar pacotes e não aparece
mais nos resultados da pesquisa.

7 Observação

Para evitar que seu pacote de teste fique ativo em nuget.org, você pode enviá-lo
por push para o site de teste de nuget.org em https://int.nugettest.org . Observe
que os pacotes carregados para int.nugettest.org podem não ser preservados.

Adicione um Leiame ou outro arquivo


Para especificar diretamente os arquivos a serem incluídos no pacote, edite o arquivo de
projeto e adicione a propriedade content :
XML

<ItemGroup>
<Content Include="readme.txt">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</Content>
</ItemGroup>

Neste exemplo, a propriedade especifica um arquivo chamado readme.txt na raiz do


projeto. O Visual Studio exibe o conteúdo do arquivo como texto sem formatação
imediatamente após instalar o pacote. Arquivos Leiame não são exibidos para pacotes
instalados como dependências. Por exemplo, este é o arquivo Leiame para o pacote
HtmlAgilityPack:

Saída

1 ----------------------------------------------------
2 ---------- Html Agility Pack Nuget Readme ----------
3 ----------------------------------------------------
4
5 ----Silverlight 4 and Windows Phone 7.1+ projects-----
6 To use XPATH features: System.Xml.Xpath.dll from the 3 Silverlight 4 SDK
must be referenced.
7 This is normally found at
8 %ProgramFiles(x86)%\Microsoft SDKs\Microsoft
SDKs\Silverlight\v4.0\Libraries\Client
9 or
10 %ProgramFiles%\Microsoft SDKs\Microsoft
SDKs\Silverlight\v4.0\Libraries\Client
11
12 ----Silverlight 5 projects-----
13 To use XPATH features: System.Xml.Xpath.dll from the Silverlight 5 SDK
must be referenced.
14 This is normally found at
15 %ProgramFiles(x86)%\Microsoft SDKs\Microsoft
SDKs\Silverlight\v5.0\Libraries\Client
16 or
17 %ProgramFiles%\Microsoft SDKs\Microsoft
SDKs\Silverlight\v5.0\Libraries\Client

7 Observação

Se você adicionar apenas readme.txt à raiz do projeto sem incluí-lo na propriedade


content do arquivo de projeto, ele não será incluído no pacote.
Vídeo relacionado
https://learn.microsoft.com/shows/NuGet-101/Create-and-Publish-a-NuGet-Package-
with-Visual-Studio-4-of-5/player

Encontre mais vídeos sobre o NuGet no Channel 9 e no YouTube .

Parabéns por criar um pacote NuGet usando uma biblioteca de classes do Visual Studio
.NET. Avance para o próximo artigo para saber como criar um pacote NuGet com o
Visual Studio .NET Framework.

Criar um pacote usando a CLI do NuGet

Para explorar mais o que o NuGet tem a oferecer, consulte os seguintes artigos:

Criar um pacote NuGet


Publicar um pacote
Compilar um pacote de pré-lançamento
Suporte a várias versões do .NET Framework
Controle de versão do pacote
Criar pacotes NuGet localizados
Portabilidade para o .NET Core do .NET Framework

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Tutorial: criar um aplicativo de console
do .NET usando o Visual Studio Code
Artigo • 08/09/2023

Este tutorial mostra como criar e executar um aplicativo de console do .NET usando o
Visual Studio Code e a CLI do .NET. As tarefas do projeto, como criar, compilar e
executar um projeto, são feitas usando a CLI do .NET. Você também pode seguir este
tutorial com um editor de código diferente e os executar comandos em um terminal.

Pré-requisitos
O Visual Studio Code com a extensão C# instalada. Para saber mais sobre
como instalar extensões no Visual Studio Code, confira Marketplace de extensão
do VS Code .
O SDK do .NET 7 .

Criar o aplicativo
Crie um projeto de aplicativo de console do .NET chamado "HelloWorld".

1. Inicie o Visual Studio Code.

2. Selecione Arquivo>Abrir Pasta (Arquivo>Abrir... no macOS) no menu principal.

3. Na caixa de diálogo Abrir Pasta, crie uma pasta HelloWorld e selecione-a. Depois,
clique em Selecionar Pasta (Abrir no macOS).

O nome da pasta se torna o nome do projeto e o nome do namespace por padrão.


Você adicionará o código mais tarde no tutorial, pressupondo que o namespace
do projeto seja HelloWorld .

4. Na caixa de diálogo Você confia nos autores dos arquivos nesta pasta?, selecione
Sim, confio nos autores.

5. Abra o Terminal no Visual Studio Code selecionando Exibir>Terminal no menu


principal.

O Terminal é aberto com o prompt de comando na pasta HelloWorld.

6. No Terminal, digite o seguinte comando:


CLI do .NET

dotnet new console --framework net7.0

O modelo de projeto cria um aplicativo simples que exibe "Olá, Mundo" na janela
do console chamando o método Console.WriteLine(String) em Program.cs.

C#

Console.WriteLine("Hello, World!");

7. Substitua o conteúdo do Program.cs pelo seguinte código:

C#

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
}
}
}

Na primeira vez que você editar um arquivo .cs, o Visual Studio Code solicitará que
você adicione os ativos ausentes para compilar e depurar o aplicativo. Selecione
Sim e o Visual Studio Code criará uma pasta .vscode com os arquivos launch.json e
tasks.json.

7 Observação

Se o prompt não for exibido ou se você o ignorar sem selecionar Sim, execute
as seguintes etapas para criar launch.json e tasks.json:

Selecione Executar>Adicionar Configuração no menu.


Selecione .NET 5 e posteriores e .NET Core no prompt Selecionar
ambiente.

O código define uma classe, Program , com um só método, Main , que usa uma
matriz de String como um argumento. Main é o ponto de entrada do aplicativo, o
método que é chamado automaticamente pelo runtime quando ele inicia o
aplicativo. Quaisquer argumentos de linha de comando fornecidos quando o
aplicativo for iniciado estão disponíveis na matriz args.

Na versão mais recente do C#, um novo recurso chamado instruções de nível


superior permite omitir a classe Program e o método Main . A maioria dos
programas C# existentes não usa instruções de nível superior, portanto, este
tutorial não usa esse novo recurso. Mas ele está disponível no C# 10 e se você usá-
lo em seus programas é uma questão de preferência de estilo.

Executar o aplicativo
Execute o seguinte comando no Terminal:

CLI do .NET

dotnet run

O programa exibe "Olá, Mundo!" e é encerrado.

Aprimorar o aplicativo
Aprimore o aplicativo para solicitar o nome do usuário e exibi-lo junto com a data e
hora.
1. Abra Program.cs.

2. Substitua o conteúdo do método Main em Program.cs, que é a linha que chama


Console.WriteLine , pelo seguinte código:

C#

Console.WriteLine("What is your name?");


var name = Console.ReadLine();
var currentDate = DateTime.Now;
Console.WriteLine($"{Environment.NewLine}Hello, {name}, on
{currentDate:d} at {currentDate:t}!");
Console.Write($"{Environment.NewLine}Press any key to exit...");
Console.ReadKey(true);

Esse código mostra um prompt na janela do console e aguarda até que o usuário
insira uma cadeia de caracteres seguida da tecla Enter . Ele armazena essa cadeia
de caracteres em uma variável chamada name . Ele também recupera o valor da
propriedade DateTime.Now, que contém a hora local atual e o atribui a uma
variável chamada currentDate . E exibe esses valores na janela do console. Por fim,
ele exibe um prompt na janela do console e chama o método
Console.ReadKey(Boolean) para aguardar a entrada do usuário.

NewLine é uma maneira independente de plataforma e de linguagem para


representar uma quebra de linha. As alternativas são \n em C# e vbCrLf no Visual
Basic.

O sinal de dólar ( $ ) na frente de uma cadeia de caracteres permite colocar


expressões como nomes de variáveis em chaves na cadeia de caracteres. O valor
da expressão é inserido na cadeia de caracteres no lugar da expressão. Essa sintaxe
é conhecida como cadeia de caracteres interpolada.

3. Salve suas alterações.

) Importante

No Visual Studio Code, você precisa salvar as alterações explicitamente. Ao


contrário do Visual Studio, as alterações de arquivo não são salvas
automaticamente quando você compila e executa um aplicativo.

4. Execute o programa novamente:

CLI do .NET
dotnet run

5. Responda ao prompt inserindo um nome e pressionando a tecla Enter .

6. Pressione qualquer tecla para encerrar o programa.

Recursos adicionais
Configurar o Visual Studio Code

Próximas etapas
Neste tutorial, você criou um aplicativo de console .NET. No próximo tutorial, você vai
depurar o aplicativo.

Depurar um aplicativo de console do .NET usando o Visual Studio Code

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review  Open a documentation issue
issues and pull requests. For
more information, see our  Provide product feedback
contributor guide.
Tutorial: Depurar um aplicativo de
console do .NET usando Visual Studio
Code
Artigo • 07/09/2023

Este tutorial apresenta as ferramentas de depuração disponíveis no Visual Studio Code


para trabalhar com aplicativos .NET.

Pré-requisitos
Este tutorial funciona com o aplicativo de console que você cria em Criar um
aplicativo de console .NET usando o Visual Studio Code.

Usar a configuração do build de depuração


Depuração e Lançamento são as configurações de build internas do .NET. Você usa a
configuração do build de depuração para depuração e a configuração da versão para a
distribuição da versão final.

Na configuração de depuração, um programa é compilado com informações de


depuração simbólicas e sem otimização. A otimização complica a depuração, porque a
relação entre o código fonte e as instruções geradas é mais complexa. A configuração
de versão de um programa não possui informações de depuração simbólica e é
totalmente otimizada.

Por padrão, as configurações de inicialização do Visual Studio Code usam a


configuração de compilação de depuração, portanto, não é necessário alterá-la antes da
depuração.

1. Inicie o Visual Studio Code.

2. Abra a pasta do projeto que você criou em Criar um aplicativo de console .NET
usando o Visual Studio Code.

Definir um ponto de interrupção


Um ponto de interrupção interrompe temporariamente a execução do aplicativo antes
de a linha com o ponto de interrupção ser executada.
1. Abra o arquivo Program.cs.

2. Defina um ponto de interrupção na linha que exibe o nome, a data e a hora


clicando na margem esquerda da janela de código. A margem esquerda fica à
esquerda dos números de linha. Um ponto de interrupção também pode ser
definido pressionando F9 ou escolhendo Executar>Ativar/desativar pontos de
interrupção no menu enquanto a linha de código está selecionada.

O Visual Studio Code indica a linha na qual o ponto de interrupção é definido


exibindo um ponto vermelho na margem esquerda.

Configurar para entrada de terminal


O ponto de interrupção está localizado após uma chamada de método
Console.ReadLine . O Console de Depuração não aceita a entrada de terminal para um

programa em execução. Para lidar com a entrada de terminal durante a depuração, você
pode usar o terminal integrado (uma das janelas do Visual Studio Code) ou um terminal
externo. Neste tutorial, você usa o terminal integrado.

1. A pasta do projeto contém uma pasta .vscode. Abra o arquivo launch.json que está
na pasta .vscode.

2. Em launch.json, altere a configuração console de internalConsole para


integratedTerminal :
JSON

"console": "integratedTerminal",

3. Salve suas alterações.

Iniciar a depuração
1. Abra a exibição de Depuração selecionando o ícone de depuração no menu do
lado esquerdo.

2. Selecione a seta verde na parte superior do painel, ao lado de Inicialização do


.NET Core (console). O programa também pode ser iniciado no modo de
depuração pressionando F5 ou escolhendo Executar>Iniciar Depuração no menu.

3. Selecione a guia Terminal para ver o prompt "Qual é o seu nome?" que o
programa exibe antes de aguardar uma resposta.
4. Digite uma cadeia de caracteres na janela Terminal em resposta ao prompt de
nome e, em seguida, pressione Enter .

A execução do programa para quando ele atinge o ponto de interrupção e antes


de o método Console.WriteLine ser executado. A seção Locais da janela Variáveis
exibe os valores das variáveis que são definidas no método que está sendo
executado no momento.
Usar o console de depuração
A janela Console de Depuração permite interagir com o aplicativo que você está
depurando. Você pode alterar o valor das variáveis para ver como isso afeta o programa.

1. Selecione a guia Console de depuração.

2. Insira name = "Gracie" no prompt na parte inferior da janela Console de


Depuração e pressione a tecla Enter .

3. Insira currentDate = DateTime.Parse("2019-11-16T17:25:00Z").ToUniversalTime()


na parte inferior da janela Console de Depuração e pressione a tecla Enter .

A janela Variáveis exibe os novos valores das variáveis name e currentDate .

4. Continue a execução do programa selecionando o botão Continuar na barra de


ferramentas. Outra maneira de continuar é pressionando F5 .

5. Selecione a guia Terminal novamente.

Os valores exibidos na janela do console também correspondem às alterações


feitas no Console de Depuração.
6. Pressione qualquer tecla para sair do aplicativo e parar a depuração.

Definir um ponto de interrupção condicional


O programa exibe a cadeia de caracteres que o usuário insere. O que acontecerá se o
usuário não inserir nada? Isso pode ser testado com um recurso de depuração útil
chamado ponto de interrupção condicional.

1. Clique com o botão direito ( Ctrl + clique no macOS) do mouse no ponto


vermelho que representa o ponto de interrupção. No menu de contexto, selecione
Editar Ponto de Interrupção para abrir uma caixa de diálogo que permite inserir
uma expressão condicional.

2. Selecione Expression na lista suspensa, insira a expressão condicional a seguir e


pressione Enter .

C#

String.IsNullOrEmpty(name)
Em todas as ocorrências do ponto de interrupção, o depurador chama o método
String.IsNullOrEmpty(name) e ele é interrompido nessa linha somente quando a

chamada do método retorna true .

Em vez de uma expressão condicional, você pode especificar uma contagem de


ocorrências, que interrompe a execução do programa antes que uma instrução seja
executada um determinado número de vezes. Outra opção é especificar uma
condição de filtro, que interrompe a execução do programa com base em atributos
como um identificador de thread, nome do processo ou nome do thread.

3. Inicie o programa com a depuração pressionando F5 .

4. Na guia Terminal, pressione a tecla Enter quando solicitado a inserir seu nome.

Como a condição especificada ( name é null ou String.Empty) foi atendida, a


execução do programa para quando ele chega no ponto de interrupção e antes de
o método Console.WriteLine ser executado.

A janela Variáveis mostra que o valor da variável name é "" ou String.Empty.

5. Confirme se o valor é uma cadeia de caracteres vazia inserindo a instrução a seguir


no prompt do Console de Depuração e pressionando Enter . O resultado é true .

C#

name == String.Empty

6. Selecione o botão Continuar na barra de ferramentas para continuar a execução


do programa.

7. Selecione a guia Terminal e pressione qualquer tecla para sair do programa e


interromper a depuração.

8. Desmarque o ponto de interrupção clicando no ponto na margem esquerda da


janela de código. Um ponto de interrupção também pode ser desmarcado
pressionando F9 ou escolhendo Executar > Ativar/desativar pontos de
interrupção no menu enquanto a linha de código está selecionada.

9. Se você receber um aviso de que a condição do ponto de interrupção será


perdida, selecione Remover ponto de interrupção.

Percorrer um programa
O Visual Studio Code também permite percorrer linha por linha de um programa e
monitorar sua execução. Normalmente, você define um ponto de interrupção e segue o
fluxo do programa por uma pequena parte do código do programa. Como esse
programa é pequeno, você pode percorrer todo o programa.

1. Defina um ponto de interrupção na chave de abertura do método Main .

2. Pressione F5 para iniciar a depuração.

O Visual Studio Code realça a linha do ponto de interrupção.

Neste ponto, a janela Variáveis mostra que a matriz args está vazia e name e
currentDate têm valores padrão.

3. Selecione Executar>Intervir pressione F11 .

O Visual Studio Code realça a próxima linha.

4. Selecione Executar>Intervir pressione F11 .

O Visual Studio Code executa o Console.WriteLine para o prompt de nome e


realça a próxima linha de execução. A próxima linha é o Console.ReadLine do
name . A janela Variáveis não é alterada e a guia Terminal mostra o prompt "Qual é

o seu nome?".

5. Selecione Executar>Intervir pressione F11 .

O Visual Studio destaca a atribuição de variável name . A janela Variáveis mostra


que name ainda é null .

6. Responda à solicitação inserindo uma cadeia de caracteres na guia Terminal e


pressionando Enter .
A guia Terminal pode não exibir a cadeia de caracteres inserida enquanto você a
insere, mas o método Console.ReadLine capturará sua entrada.

7. Selecione Executar>Intervir pressione F11 .

O Visual Studio Code destaca a atribuição de variável currentDate . A janela


Variáveis mostra o valor retornado pela chamada para o método
Console.ReadLine. A guia Terminal exibe a cadeia de caracteres inserida no
prompt.

8. Selecione Executar>Intervir pressione F11 .

A janela Variáveis mostra o valor da variável currentDate após a atribuição da


propriedade DateTime.Now.

9. Selecione Executar>Intervir pressione F11 .

O Visual Studio Code chama o método Console.WriteLine(String, Object, Object). A


janela do console exibe a cadeia de caracteres formatada.

10. Selecione Executar>Depuração Circular ou pressione Shift + F11 .

11. Selecione a guia Terminal.

O terminal exibe "Pressione qualquer tecla para sair..."

12. Pressione qualquer tecla para encerrar o programa.

Usar a configuração de build da Versão


Quando tiver testado a versão da depuração do seu aplicativo, você deverá compilar e
testar a versão de lançamento. A versão de Lançamento incorpora otimizações do
compilador que podem afetar o comportamento de um aplicativo. Por exemplo, as
otimizações de compilador que são projetadas para melhorar o desempenho podem
criar condições de corrida em aplicativos multi-threaded.

Para criar e testar a versão de Lançamento do seu aplicativo de console, abra o Terminal
e execute o seguinte comando:

CLI do .NET
dotnet run --configuration Release

Recursos adicionais
Depurar no Visual Studio Code

Próximas etapas
Neste tutorial, você usou as ferramentas de depuração do Visual Studio Code. No
próximo tutorial, você publicará uma versão implantável do aplicativo.

Publicar um aplicativo de console do .NET usando o Visual Studio Code

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Tutorial: publicar um aplicativo de
console do .NET usando Visual Studio
Code
Artigo • 08/09/2023

Este tutorial mostra como publicar um aplicativo de console para que outros usuários
possam executá-lo. A publicação cria o conjunto de arquivos necessários para executar
um aplicativo. Para implantar os arquivos, copie-os para o computador de destino.

A CLI do .NET é usada para publicar o aplicativo, portanto, você pode seguir este tutorial
com um editor de código diferente do Visual Studio Code, se preferir.

Pré-requisitos
Este tutorial funciona com o aplicativo de console que você cria em Criar um
aplicativo de console .NET usando o Visual Studio Code.

Publicar o aplicativo
1. Inicie o Visual Studio Code.

2. Abra a pasta de projeto HelloWorld que você criou em Criar um aplicativo de


console .NET usando o Visual Studio Code.

3. Escolha Exibir>Terminal no menu principal.

O terminal é aberto na pasta HelloWorld.

4. Execute o comando a seguir:

CLI do .NET

dotnet publish --configuration Release

A configuração de compilação padrão é Debug, portanto, esse comando especifica


a configuração de compilação Release. A saída da configuração de compilação
Release tem o mínimo de informações simbólicas de depuração e é totalmente
otimizada.

A saída do comando é semelhante ao exemplo a seguir:


Saída

Microsoft (R) Build Engine version 16.7.4+b89cb5fde for .NET


Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
All projects are up-to-date for restore.
HelloWorld ->
C:\Projects\HelloWorld\bin\Release\net7.0\HelloWorld.dll
HelloWorld -> C:\Projects\HelloWorld\bin\Release\net7.0\publish\

Inspecionar os arquivos
Por padrão, o processo de publicação cria uma implantação dependente de estrutura,
que é um tipo de implantação em que o aplicativo publicado é executado em um
computador que tenha o runtime do .NET instalado. Para executar o aplicativo
publicado, você pode usar o arquivo executável ou executar o comando dotnet
HelloWorld.dll em um prompt de comando.

Nas etapas a seguir, você examinará os arquivos criados pelo processo de publicação.

1. Selecione o Explorer, na barra de navegação à esquerda.

2. Expanda bin/Release/net7.0/publish.
Como mostra a imagem anterior, a saída publicada inclui os seguintes arquivos:

HelloWorld.deps.json

Esse é o arquivo de dependências de runtime do aplicativo. Ele define os


componentes e as bibliotecas do .NET (incluindo a biblioteca de vínculo
dinâmico que contém o aplicativo) necessários para executar o aplicativo.
Para obter mais informações, confira Arquivos de configuração de runtime .
HelloWorld.dll

Essa é a versão de implantação dependente de estrutura do aplicativo. Para


executar essa biblioteca de links dinâmicos, insira dotnet HelloWorld.dll em
um prompt de comando. Esse método de execução do aplicativo funciona
em qualquer plataforma que tenha o runtime do .NET instalado.

HelloWorld.exe (HelloWorld no Linux, não criado no macOS).

Essa é a versão do executável dependente de estrutura do aplicativo. O


arquivo é específico do sistema operacional.

HelloWorld.pdb (opcional para implantação)

Esse é o arquivo de símbolos de depuração. Não é necessário implantar esse


arquivo juntamente com seu aplicativo, embora você deva salvá-lo caso
precise depurar a versão publicada do seu aplicativo.

HelloWorld.runtimeconfig.json

Esse é o arquivo de configuração de runtime do aplicativo. Ele identifica a


versão do .NET com base na qual o aplicativo foi criado para ser executado.
Você também pode adicionar opções de configuração a ele. Para obter mais
informações, confira Definições de configuração de runtime do .NET.

Executar o aplicativo publicado


1. No Explorer, clique com o botão direito do mouse na pasta publish ( Ctrl +clique
no macOS) e selecione Abrir no Terminal.
2. No Windows ou no Linux, execute o aplicativo usando o executável.

a. No Windows, insira .\HelloWorld.exe e pressione Enter .

b. No Linux, insira ./HelloWorld e pressione Enter .

c. Insira um nome em resposta à solicitação e pressione qualquer tecla para sair.

3. Em qualquer plataforma, execute o aplicativo usando o comando dotnet:

a. Insira dotnet HelloWorld.dll e pressione ENTER .

b. Insira um nome em resposta à solicitação e pressione qualquer tecla para sair.

Recursos adicionais
Implantação de aplicativos .NET
Publicar aplicativos .NET com a CLI do .NET
dotnet publish
Usar o SDK do .NET em ambientes de CI (integração contínua)
Próximas etapas
Neste tutorial, você publicou um aplicativo de console. No próximo tutorial, você criará
uma biblioteca de classes.

Criar uma biblioteca de classes .NET usando o Visual Studio Code

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Tutorial: criar uma biblioteca de classes
.NET usando o Visual Studio Code
Artigo • 08/09/2023

Neste tutorial, você vai criar uma biblioteca de utilitário simples que contém um só
método de manipulação de cadeia de caracteres.

Uma biblioteca de classes define tipos e métodos que são chamados por um aplicativo.
Se a biblioteca for direcionada ao .NET Standard 2.0, ela poderá ser chamada por
qualquer implementação do .NET (incluindo o .NET Framework) que dê suporte ao .NET
Standard 2.0. Se a biblioteca for direcionada ao .NET 7, pode ser chamada por qualquer
aplicativo direcionado ao .NET 7. Este tutorial mostra como fazer o direcionamento para
o .NET 7.

Ao criar uma biblioteca de classes, você pode distribui-la como um componente de


terceiros ou como um componente agrupado com um ou mais aplicativos.

Pré-requisitos
O Visual Studio Code com a extensão C# instalada. Para saber mais sobre
como instalar extensões no Visual Studio Code, confira Marketplace de extensão
do VS Code .
O SDK do .NET 7 .

Criar uma solução


Comece criando uma solução em branco para colocar o projeto de biblioteca de classes.
Uma solução serve como um contêiner de um ou mais projetos. Você adicionará
projetos adicionais relacionados à mesma solução.

1. Inicie o Visual Studio Code.

2. Selecione Arquivo>Abrir Pasta (Abrir... no macOS) no menu principal.

3. Na caixa de diálogo Abrir Pasta, crie uma pasta ClassLibraryProjects e clique em


Selecionar Pasta (Abrir no macOS).

4. Abra o Terminal no Visual Studio Code selecionando Exibir>Terminal no menu


principal.

O Terminal é aberto com o prompt de comando na pasta ClassLibraryProjects.


5. No Terminal, digite o seguinte comando:

CLI do .NET

dotnet new sln

A saída do terminal é como no seguinte exemplo:

Saída

The template "Solution File" was created successfully.

Criar um projeto de biblioteca de classes


Adicione um novo projeto de biblioteca de classes .NET chamado "StringLibrary" para a
solução.

1. No terminal, execute o seguinte comando para criar o projeto de biblioteca:

CLI do .NET

dotnet new classlib -o StringLibrary

O comando -o ou --output especifica o local para colocar a saída gerada.

A saída do terminal é como no seguinte exemplo:

Saída

The template "Class library" was created successfully.


Processing post-creation actions...
Running 'dotnet restore' on StringLibrary\StringLibrary.csproj...
Determining projects to restore...
Restored
C:\Projects\ClassLibraryProjects\StringLibrary\StringLibrary.csproj (in
328 ms).
Restore succeeded.

2. Execute o seguinte comando para adicionar o projeto de biblioteca de utilitários à


solução:

CLI do .NET

dotnet sln add StringLibrary/StringLibrary.csproj


A saída do terminal é como no seguinte exemplo:

Saída

Project `StringLibrary\StringLibrary.csproj` added to the solution.

3. Verifique se a biblioteca tem como destino o .NET 7. No Explorer, abra


StringLibrary/StringLibrary.csproj.

O elemento TargetFramework mostra que o projeto tem como destino o .NET 7.0.

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>

</Project>

4. Abra Class1.cs e substitua o código pelo exibido a seguir.

C#

namespace UtilityLibraries;

public static class StringLibrary


{
public static bool StartsWithUpper(this string? str)
{
if (string.IsNullOrWhiteSpace(str))
return false;

char ch = str[0];
return char.IsUpper(ch);
}
}

A biblioteca de classes UtilityLibraries.StringLibrary contém um método


chamado StartsWithUpper . Esse método retorna um valor Boolean que indica se a
instância atual da cadeia de caracteres começa com um caractere maiúsculo. O
padrão Unicode distingue caracteres maiúsculos de caracteres minúsculos. O
método Char.IsUpper(Char) retornará true se um caractere for maiúsculo.

StartsWithUpper é implementado como um método de extensão para que você

possa chamá-lo como se ele fosse um membro da classe String.


5. Salve o arquivo.

6. Execute o comando a seguir para criar a solução e verificar se o projeto é


compilado sem erro.

CLI do .NET

dotnet build

A saída do terminal é como no seguinte exemplo:

Saída

Microsoft (R) Build Engine version 16.7.4+b89cb5fde for .NET


Copyright (C) Microsoft Corporation. All rights reserved.
Determining projects to restore...
All projects are up-to-date for restore.
StringLibrary ->
C:\Projects\ClassLibraryProjects\StringLibrary\bin\Debug\net7.0\StringL
ibrary.dll
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:02.78

Adicionar um aplicativo de console à solução


Adicione um aplicativo de console que usa a biblioteca de classes. O aplicativo solicitará
que o usuário insira uma cadeia de caracteres e informe se a cadeia de caracteres
começa com um caractere maiúsculo.

1. No terminal, execute o seguinte comando para criar o projeto de aplicativo de


console:

CLI do .NET

dotnet new console -o ShowCase

A saída do terminal é como no seguinte exemplo:

Saída

The template "Console Application" was created successfully.


Processing post-creation actions...
Running 'dotnet restore' on ShowCase\ShowCase.csproj...
Determining projects to restore...
Restored C:\Projects\ClassLibraryProjects\ShowCase\ShowCase.csproj
(in 210 ms).
Restore succeeded.

2. Execute o seguinte comando para adicionar o projeto de aplicativo de console à


solução:

CLI do .NET

dotnet sln add ShowCase/ShowCase.csproj

A saída do terminal é como no seguinte exemplo:

Saída

Project `ShowCase\ShowCase.csproj` added to the solution.

3. Abra ShowCase/Program.cs e substitua todo o código pelo código a seguir.

C#

using UtilityLibraries;

class Program
{
static void Main(string[] args)
{
int row = 0;

do
{
if (row == 0 || row >= 25)
ResetConsole();

string? input = Console.ReadLine();


if (string.IsNullOrEmpty(input)) break;
Console.WriteLine($"Input: {input}");
Console.WriteLine("Begins with uppercase? " +
$"{(input.StartsWithUpper() ? "Yes" : "No")}");
Console.WriteLine();
row += 4;
} while (true);
return;

// Declare a ResetConsole local method


void ResetConsole()
{
if (row > 0)
{
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
Console.Clear();
Console.WriteLine($"{Environment.NewLine}Press <Enter> only
to exit; otherwise, enter a string and press <Enter>:
{Environment.NewLine}");
row = 3;
}
}
}

O código usa a variável row ​para manter uma contagem do número de linhas de
dados gravadas na janela do console. Sempre que ela for igual ou superior a 25, o
código limpa a janela do console e exibe uma mensagem para o usuário.

O programa solicita que o usuário insira uma cadeia de caracteres. Ele indica se a
cadeia de caracteres começa com um caractere maiúsculo. Se o usuário pressionar
a tecla Enter sem inserir uma cadeia de caracteres, o aplicativo será encerrado e a
janela do console será fechada.

4. Salve suas alterações.

Adicionar uma referência ao projeto


Inicialmente, o novo projeto de aplicativo de console não tem acesso à biblioteca de
classes. Para permitir que ele chame métodos na biblioteca de classes, crie uma
referência de projeto para o projeto da biblioteca de classes.

1. Execute o comando a seguir:

CLI do .NET

dotnet add ShowCase/ShowCase.csproj reference


StringLibrary/StringLibrary.csproj

A saída do terminal é como no seguinte exemplo:

Saída

Reference `..\StringLibrary\StringLibrary.csproj` added to the project.

Executar o aplicativo
1. Execute o seguinte comando no terminal:
CLI do .NET

dotnet run --project ShowCase/ShowCase.csproj

2. Experimente o programa inserindo cadeias de caracteres e pressionando Enter .


Depois pressione Enter para sair.

A saída do terminal é como no seguinte exemplo:

Saída

Press <Enter> only to exit; otherwise, enter a string and press


<Enter>:

A string that starts with an uppercase letter


Input: A string that starts with an uppercase letter
Begins with uppercase? : Yes

a string that starts with a lowercase letter


Input: a string that starts with a lowercase letter
Begins with uppercase? : No

Recursos adicionais
Desenvolver bibliotecas com a CLI do .NET
Versões do .NET Standard e plataformas com suporte.

Próximas etapas
Neste tutorial, você criou uma solução, adicionou um projeto de biblioteca e adicionou
um projeto de aplicativo de console que usa a biblioteca. No próximo tutorial, você
adicionará um projeto de teste de unidade à solução.

Testar uma biblioteca de classes .NET com o .NET usando o Visual Studio Code

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our  Provide product feedback
contributor guide.
Tutorial: testar uma biblioteca de classes
.NET usando o Visual Studio Code
Artigo • 08/09/2023

Este tutorial mostra como automatizar o teste de unidade adicionando um projeto de


teste a uma solução.

Pré-requisitos
Este tutorial funciona com a solução que você cria em Criar uma biblioteca de
classes .NET usando o Visual Studio Code.

Criar um projeto de teste de unidade


As unidade de teste fornecem testes de software automatizados durante o
desenvolvimento e a publicação. A estrutura de teste usada neste tutorial é o MSTest.
MSTest é uma das três estruturas de teste que você pode escolher. As outras são
xUnit e nUnit .

1. Inicie o Visual Studio Code.

2. Abra a solução ClassLibraryProjects criada em Criar uma biblioteca de classes


.NET usando o Visual Studio Code.

3. Crie um projeto de teste de unidade chamado "StringLibraryTest".

CLI do .NET

dotnet new mstest -o StringLibraryTest

O modelo de projeto cria um arquivo UnitTest1.cs com o código a seguir:

C#

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}

O código-fonte criado pelo modelo de teste de unidade faz o seguinte:

Ele importa o namespace Microsoft.VisualStudio.TestTools.UnitTesting, que


contém os tipos usados para o teste de unidade.
Ele aplica o atributoTestClassAttribute à classe UnitTest1 .
Ele aplica o atributo TestMethodAttribute para definir TestMethod1 .

Cada método marcado com [TestMethod] em uma classe de teste marcada com
[TestClass] é executado automaticamente quando o teste de unidade é invocado.

4. Adicione o projeto de teste à solução.

CLI do .NET

dotnet sln add StringLibraryTest/StringLibraryTest.csproj

Adicionar uma referência ao projeto


Para que o projeto de teste funcione com a classe StringLibrary , adicione uma
referência no projeto StringLibraryTest ao projeto StringLibrary .

1. Execute o comando a seguir:

CLI do .NET

dotnet add StringLibraryTest/StringLibraryTest.csproj reference


StringLibrary/StringLibrary.csproj

Adicionar e executar métodos de teste de


unidade
Quando o Visual Studio invoca um teste de unidade, ele executa cada método marcado
com o atributo TestMethodAttribute em uma classe marcada com o atributo
TestClassAttribute. Um método de teste termina quando a primeira falha é encontrada
ou quando todos os testes contidos no método têm êxito.
Os testes mais comuns chamam membros da classe Assert. Muitos métodos assert
incluem pelo menos dois parâmetros, um deles é o resultado esperado do teste, e o
outro é o resultado real do teste. Alguns dos métodos chamados com mais frequência
da classe Assert são mostrados na tabela a seguir:

Métodos assert Função

Assert.AreEqual Verifica se os dois valores ou objetos são iguais. O assert falha se os valores
ou objetos não forem iguais.

Assert.AreSame Verifica se duas variáveis de objeto se referem ao mesmo objeto. A assert


falhará se as variáveis se referirem a objetos diferentes.

Assert.IsFalse Verifica se uma condição é false . O assert falhará se a condição for true .

Assert.IsNotNull Verifica se um objeto não é null . A assert falhará se o objeto for null .

Você também pode usar o método Assert.ThrowsException em um método de teste


para indicar o tipo de exceção que ele deve gerar. O teste falhará se a exceção
especificada não for lançada.

Ao testar o método StringLibrary.StartsWithUpper , você quer fornecer um número de


cadeias de caracteres que comecem com um caractere maiúsculo. Você espera que o
método retorne true nesses casos, para que possa chamar o método Assert.IsTrue. Da
mesma forma, você deseja fornecer um número de cadeias de caracteres que comecem
com algo diferente de um caractere maiúsculo. Você espera que o método retorne
false nesses casos, para que possa chamar o método Assert.IsFalse.

Como o método de biblioteca processa cadeias de caracteres, também convém verificar


se ele processa com êxito uma cadeia de caracteres vazia (String.Empty) e uma cadeia
de caracteres null . Uma cadeia de caracteres vazia é aquela que não tem caracteres e
cujo Length é 0. Uma cadeia de caracteres null é aquela que não foi inicializada. Você
pode chamar StartsWithUpper diretamente como um método estático e passar um só
argumento String. Ou você pode chamar StartsWithUpper como um método de
extensão em uma variável string atribuída a null .

Você definirá três métodos, cada um deles chamará um método Assert para cada
elemento em uma matriz de cadeia de caracteres. Você chamará uma sobrecarga de
método que permite especificar uma mensagem de erro a ser exibida em caso de falha
no teste. A mensagem identifica a cadeia de caracteres que causou a falha.

Para criar os métodos de teste:

1. Abra StringLibraryTest/UnitTest1.cs e substitua todo o código pelo código a seguir.


C#

using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;

namespace StringLibraryTest;

[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestStartsWithUpper()
{
// Tests that we expect to return true.
string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα",
"Москва" };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsTrue(result,
string.Format("Expected for '{0}': true; Actual:
{1}",
word, result));
}
}

[TestMethod]
public void TestDoesNotStartWithUpper()
{
// Tests that we expect to return false.
string[] words = { "alphabet", "zebra", "abc",
"αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsFalse(result,
string.Format("Expected for '{0}': false; Actual:
{1}",
word, result));
}
}

[TestMethod]
public void DirectCallWithNullOrEmpty()
{
// Tests that we expect to return false.
string?[] words = { string.Empty, null };
foreach (var word in words)
{
bool result = StringLibrary.StartsWithUpper(word);
Assert.IsFalse(result,
string.Format("Expected for '{0}': false; Actual:
{1}",
word == null ? "<null>" : word,
result));
}
}
}

O teste de caracteres maiúsculos no método TestStartsWithUpper inclui a letra


maiúscula grega alfa (U+0391) e a letra maiúscula cirílica EM (U+041C). O teste de
caracteres minúsculos no método TestDoesNotStartWithUpper inclui a letra
minúscula grega alfa (U+03B1) e a letra minúscula cirílica Ghe (U+0433).

2. Salve suas alterações.

3. Execute os testes:

CLI do .NET

dotnet test StringLibraryTest/StringLibraryTest.csproj

A saída do terminal mostra que todos os testes foram aprovados.

Saída

Starting test execution, please wait...


A total of 1 test files matched the specified pattern.

Passed! - Failed: 0, Passed: 3, Skipped: 0, Total: 3,


Duration: 3 ms - StringLibraryTest.dll (net7.0)

Tratar falhas de teste


Se você estiver fazendo o TDD (Desenvolvimento Orientado por Testes), você grava os
testes primeiro e eles falharão na execução. Em seguida, você adiciona o código ao
aplicativo que faz com que o teste seja bem-sucedido. Neste tutorial, você criou o teste
depois de gravar o código do aplicativo que ele valida, portanto, você não viu o teste
falhar. Para validar se um teste falha conforme o esperado, adicione um valor inválido à
entrada de teste.

1. Modifique a matriz words no método TestDoesNotStartWithUpper para incluir a


cadeia de caracteres “Error”.

C#
string[] words = { "alphabet", "Error", "zebra", "abc",
"αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };

2. Execute os testes:

CLI do .NET

dotnet test StringLibraryTest/StringLibraryTest.csproj

A saída do terminal mostra que um teste falha e fornece uma mensagem de erro
para o teste com falha: "Falha em Assert.IsFalse. Esperado para 'Error': false, real:
True". Devido à falha, nenhuma cadeia de caracteres na matriz após "Error" foi
testada.

Saída

Starting test execution, please wait...


A total of 1 test files matched the specified pattern.
Failed TestDoesNotStartWithUpper [28 ms]
Error Message:
Assert.IsFalse failed. Expected for 'Error': false; Actual: True
Stack Trace:
at StringLibraryTest.UnitTest1.TestDoesNotStartWithUpper() in
C:\ClassLibraryProjects\StringLibraryTest\UnitTest1.cs:line 33

Failed! - Failed: 1, Passed: 2, Skipped: 0, Total: 3,


Duration: 31 ms - StringLibraryTest.dll (net5.0)

3. Remova a cadeia de caracteres "Error" que você adicionou na etapa 1. Execute


novamente o teste, e ele será aprovado.

Testar a versão de lançamento da biblioteca


Agora que todos os nossos testes foram aprovados ao executar a compilação de
Depuração da biblioteca, execute os testes mais uma vez na compilação de Lançamento
da biblioteca. Vários fatores, incluindo as otimizações do compilador, podem produzir
um comportamento diferente entre as compilações de Depuração e Lançamento.

1. Execute os testes com a configuração de build de lançamento:

CLI do .NET

dotnet test StringLibraryTest/StringLibraryTest.csproj --configuration


Release

Os testes são aprovados.

Depurar testes
Se você estiver usando o Visual Studio Code como IDE, use o mesmo processo
mostrado em Depurar um aplicativo de console .NET usando o Visual Studio Code para
depurar o código usando seu projeto de teste de unidade. Em vez de iniciar o projeto
de aplicativo ShowCase, abra StringLibraryTest/UnitTest1.cs e selecione Depurar Todos os
Testes entre as linhas 7 e 8. Se não for possível encontrá-lo, pressione Ctrl + Shift + P

para abrir a paleta de comandos e insira Recarregar Janela.

O Visual Studio Code inicia o projeto de teste com o depurador anexado. A execução
será interrompida em algum ponto de interrupção adicionado ao projeto de teste ou ao
código da biblioteca subjacente.

Recursos adicionais
Teste de unidade no .NET

Próximas etapas
Neste tutorial, você testou uma biblioteca de classes. Você pode disponibilizar a
biblioteca para outras pessoas publicando-a no NuGet como um pacote. Para saber
como, siga um tutorial do NuGet:

Criar e publicar um pacote usando a CLI do dotnet

Se você publicar uma biblioteca como um pacote NuGet, outras pessoas poderão
instalá-la e usá-la. Para saber como, siga um tutorial do NuGet:

Instalar e usar um pacote usando a CLI do dotnet

Uma biblioteca não precisa ser distribuída como um pacote. Ela pode ser empacotada
com um aplicativo de console que a usa. Para saber como publicar um aplicativo de
console, confira o tutorial anterior nesta série:

Publicar um aplicativo de console .NET usando o Visual Studio Code


6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Início Rápido: Instalar e usar um pacote
com a CLI do dotnet
Artigo • 22/12/2023

Os pacotes NuGet contém código binário compilado que outros desenvolvedores


disponibilizam para uso por outros desenvolvedores em seus respectivos projetos. Para
obter mais informações, confira O que é o NuGet. Este guia de início rápido descreve
como instalar o popular pacote NuGety Newtonsoft.Json NuGet em um projeto .NET
usando o comando dotnet add package.

Você se refere a pacotes instalados no código com uma diretiva using <namespace> ,
onde <namespace> geralmente é o nome do pacote. Em seguida, você pode usar a API
do pacote em seu projeto.

 Dica

Navegue para nuget.org/packages para encontrar pacotes que você pode


reutilizar em seus próprios aplicativos. Você pode pesquisar diretamente em
https://nuget.org ou localizar e instalar pacotes de dentro do Visual Studio. Para
obter mais informações, consulte Localizar e avaliar pacotes NuGet para seu
projeto.

Pré-requisitos
O . NET SDK , que fornece a ferramenta de linha de comando dotnet . No Visual
Studio 2017 em diante, a CLI do dotnet é instalada automaticamente com
qualquer carga de trabalho relacionada ao .NET ou .NET Core.

Criar um projeto
Os pacotes NuGet podem ser instalados em um projeto do .NET. Para este passo a
passo, crie um projeto de console do .NET simples usando a CLI do dotnet da seguinte
maneira:

1. Crie uma pasta chamada Nuget.Quickstart para o projeto.

2. Abra um prompt de comando e alterne para a nova pasta.

3. Crie o projeto usando o seguinte comando:


CLI do .NET

dotnet new console

4. Use dotnet run para testar o aplicativo. Você deverá ver a saída Hello, World! .

Adicionar o pacote do NuGet Newtonsoft.Json


1. Use o seguinte comando para instalar o pacote Newtonsoft.json :

CLI do .NET

dotnet add package Newtonsoft.Json

2. Após a conclusão do comando, abra o arquivo Nuget.Quickstart.csproj no Visual


Studio para ver a referência do pacote NuGet adicionada:

XML

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>

Use a API Newtonsoft.Json no aplicativo


1. No Visual Studio, abra o arquivo Program.cs e adicione as seguintes instruções à
parte superior do arquivo:

cs

using Newtonsoft.Json;

2. Adicione o seguinte código para substituir a instrução Console.WriteLine("Hello,


World!"); :

cs

namespace Nuget.Quickstart
{
public class Account
{
public string? Name { get; set; }
public string? Email { get; set; }
public DateTime DOB { get; set; }
}
internal class Program
{
static void Main(string[] args)
{
Account account = new Account
{
Name = "John Doe",
Email = "john@nuget.org",
DOB = new DateTime(1980, 2, 20, 0, 0, 0,
DateTimeKind.Utc),
};

string json = JsonConvert.SerializeObject(account,


Formatting.Indented);
Console.WriteLine(json);
}
}
}

3. Salve o arquivo e compile e execute o aplicativo usando o comando dotnet run . A


saída é a representação JSON do objeto Account no código:

Saída

{
"Name": "John Doe",
"Email": "john@nuget.org",
"DOB": "1980-02-20T00:00:00Z"
}

Parabéns por instalar e usar seu primeiro pacote do NuGet!

Vídeo relacionado
https://learn.microsoft.com/shows/NuGet-101/Install-and-Use-a-NuGet-Package-with-
the-NET-CLI-3-of-5/player

Encontre mais vídeos sobre o NuGet no Channel 9 e no YouTube .

Próximas etapas
Saiba mais sobre como instalar e usar pacotes NuGet com a CLI do dotnet:

Instalar e usar pacotes usando a CLI do dotnet


Visão geral e fluxo de trabalho do consumo de pacote
Localizar e escolher pacotes
Referências de pacotes em arquivos de projeto

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Início Rápido: Criar e publicar um
pacote com a CLI do dotnet
Artigo • 22/12/2023

Este início rápido mostra como criar rapidamente um pacote NuGet a partir de uma
biblioteca de classes .NET e publicá-lo em nuget.org usando a interface de linha de
comando .NET ou CLI do dotnet.

Pré-requisitos
O SDK do .NET , que fornece a ferramenta de linha de comando do dotnet. No
Visual Studio 2017 em diante, a CLI do dotnet é instalada automaticamente com
qualquer carga de trabalho relacionada ao .NET ou .NET Core.

Uma conta gratuita em nuget.org. Siga as instruções em Adicionar uma nova conta
individual.

Criar um projeto de biblioteca de classes


Você pode usar um projeto existente da Biblioteca de Classes .NET para o código que
deseja empacotar, ou criar um projeto simples da seguinte maneira:

1. Crie uma pasta chamada AppLogger.


2. Abra um prompt de comando e vá para a pasta AppLogger. Todos os comandos da
CLI do dotnet neste início rápido são executados na pasta atual por padrão.
3. Insira dotnet new classlib , o que cria um projeto com o nome da pasta atual.

Para obter mais informações, confira dotnet new.

Adicionar metadados de pacote ao arquivo de


projeto
Todos os pacotes NuGet têm um manifesto que descreve seu conteúdo e suas
dependências. No pacote final, o manifesto é um arquivo .nuspec que usa as
propriedades dos metadados de NuGet que você incluiu no arquivo de projeto.

Abra o arquivo de projeto .csproj, .fproj ou .vbproj e adicione as propriedades a seguir


dentro da marca <PropertyGroup> existente. Use seus próprios valores para nome e
empresa e substitua o identificador de pacote por um valor exclusivo.
XML

<PackageId>Contoso.08.28.22.001.Test</PackageId>
<Version>1.0.0</Version>
<Authors>your_name</Authors>
<Company>your_company</Company>

) Importante

O identificador de pacote deve ser exclusivo em nuget.org e em outras origens de


pacotes. A publicação torna o pacote publicamente visível. Portanto, se você usar a
biblioteca AppLogger de exemplo ou outra biblioteca de teste, use um nome
exclusivo que inclua Sample ou Test .

É possível adicionar propriedades opcionais descritas em Propriedades de metadados


do NuGet.

7 Observação

Para pacotes que você cria para consumo público, preste atenção especial à
propriedade PackageTags . As tags ajudam outras pessoas a encontrar seu pacote e
entender o que ele faz.

Executar o comando pack


Para compilar um pacote do NuGet ou arquivo .nupkg do projeto, execute o comando
dotnet pack, que também compila o projeto automaticamente.

CLI do .NET

dotnet pack

A saída mostra o caminho até o arquivo .nupkg.

Saída

MSBuild version 17.3.0+92e077650 for .NET


Determining projects to restore...
Restored C:\Users\myname\source\repos\AppLogger\AppLogger.csproj (in 64
ms).
AppLogger ->
C:\Users\myname\source\repos\AppLogger\bin\Debug\net6.0\AppLogger.dll
Successfully created package
'C:\Users\myname\source\repos\AppLogger\bin\Debug\Contoso.08.28.22.001.Test.
1.0.0.nupkg'.

Gerar pacote automaticamente no build


Para executar dotnet pack automaticamente sempre que você executar dotnet build ,
adicione a seguinte linha ao arquivo de projeto em <PropertyGroup> :

XML

<GeneratePackageOnBuild>true</GeneratePackageOnBuild>

Publicar o pacote
Publique seu arquivo .nupkg em nuget.org usando o comando dotnet nuget push com
uma chave de API obtida por você em nuget.org.

7 Observação

Nuget.org verifica todos os pacotes carregados em busca de vírus e rejeita os


pacotes quando encontra um vírus. Nuget.org também verifica
periodicamente todos os pacotes listados.

Os pacotes que você publicar em nuget.org também são publicamente


visíveis para outros desenvolvedores, a menos que você os remova da lista.
Para hospedar pacotes de forma privada, consulte Hospedar seus próprios
feeds NuGet.

Obter sua chave de API


1. Entre em sua conta de nuget.org ou crie uma conta caso ainda não tenha uma.

2. Selecione seu nome de usuário no canto superior direito e selecione Chaves de


API.

3. Selecione Criar e forneça um nome para sua chave.

4. Em Selecionar Escopos, selecione Push.


5. Em Selecionar Pacotes>Padrão Glob, insira *.

6. Selecione Criar.

7. Selecione Copiar para copiar a nova chave.

) Importante

Sempre mantenha sua chave de API em segredo. A chave de API é como uma
senha que permite que qualquer pessoa gerencie pacotes em seu nome.
Exclua ou gere novamente sua chave de API se ela for revelada
acidentalmente.
Salve sua chave em um local seguro, pois não será possível copiá-la
novamente no futuro. Se você retornar à página da chave de API, será
necessário gerar novamente a chave para copiá-la. Também é possível
remover a chave de API, se você não quiser mais fazer o push de pacotes.

Os Escopos permitem criar chaves de API separadas para finalidades diferentes. Cada
chave tem um período de expiração e pode ter o escopo definido para pacotes ou
padrões glob específicos. Você também define o escopo de cada chave para operações
específicas: enviar novos pacotes e versões de pacotes, enviar por push apenas novas
versões de pacotes ou remover da lista.

Por meio de escopo, é possível criar chaves de API para diferentes pessoas que
gerenciam os pacotes para a sua organização, de modo que elas tenham somente as
permissões necessárias.

Para saber mais, confira Chaves de API com escopo.


Publicar com dotnet nuget push
Na pasta que contém o arquivo .nupkg, execute o comando a seguir. Especifique o
nome do arquivo .nupkg e substitua o valor da chave pela chave da API.

CLI do .NET

dotnet nuget push Contoso.08.28.22.001.Test.1.0.0.nupkg --api-key


qz2jga8pl3dvn2akksyquwcs9ygggg4exypy3bhxy6w6x6 --source
https://api.nuget.org/v3/index.json

A janela de saída mostra os resultados do processo de publicação.

Saída

Pushing Contoso.08.28.22.001.Test.1.0.0.nupkg to
'https://www.nuget.org/api/v2/package'...
PUT https://www.nuget.org/api/v2/package/
warn : All published packages should have license information specified.
Learn more: https://aka.ms/nuget/authoring-best-practices#licensing.
Created https://www.nuget.org/api/v2/package/ 1221ms
Your package was pushed.

Para obter mais informações, confira dotnet nuget push.

7 Observação

Para evitar que seu pacote de teste fique permaneça em nuget.org, você pode
enviá-lo por push para o site de teste de nuget.org em https://int.nugettest.org .
Observe que os pacotes carregados para int.nugettest.org podem não ser
preservados.

Erros de publicação
Os erros do comando push geralmente indicam o problema. Por exemplo, talvez você
tenha esquecido de atualizar o número de versão em seu projeto e, portanto, está
tentando publicar um pacote que já existe.

Você também verá erros se sua chave de API for inválida ou tiver expirado, ou se você
tentar publicar um pacote usando um identificador que já existe no host. Suponha, por
exemplo, que o identificador AppLogger-test já exista em nuget.org. Se você tentar
publicar um pacote com esse identificador, o comando push resultará no seguinte erro:
Saída

Response status code does not indicate success: 403 (The specified API key
is invalid,
has expired, or does not have permission to access the specified package.).

Se você receber esse erro, verifique se está usando uma chave de API válida que ainda
não expirou. Se estiver, o erro indicará que o identificador do pacote já existe no host.
Para corrigir o erro, altere o identificador do pacote para torná-lo único, recompile o
projeto, recrie o arquivo .nupkg e tente novamente o comando push .

Gerenciar o pacote publicado


Quando seu pacote for publicado com êxito, você receberá um e-mail de confirmação.
Para ver o pacote que você acabou de publicar, em nuget.org , selecione seu nome de
usuário no canto superior direito e selecione Gerenciar pacotes.

7 Observação

Poderá demorar algum tempo para o pacote ser indexado e aparecer nos
resultados da pesquisa, onde outras pessoas podem encontrá-lo. Durante esse
tempo, o pacote aparece em Pacotes não listados e a página do pacote mostra a
seguinte mensagem:

Você acabou de publicar um pacote NuGet em nuget.org, o qual pode ser usado por
outros desenvolvedores podem usar em seus próprios projetos.

Se você criou um pacote que não é útil (como este pacote de exemplo que foi criado
com uma biblioteca de classes vazia) ou decidiu que não deseja que o pacote fique
visível, você poderá remover o pacote da lista para ocultá-lo dos resultados da pesquisa:

1. Depois que o pacote aparecer em Pacotes Publicados na página Gerenciar


Pacotes, selecione o ícone de lápis ao lado da listagem de pacotes.
2. Na próxima página, selecione Listagem, desmarque a caixa de seleção Listar nos
resultados da pesquisa e selecione Salvar.

O pacote agora aparece em Pacotes não listados em Gerenciar pacotes e não aparece
mais nos resultados da pesquisa.

7 Observação

Para evitar que seu pacote de teste fique ativo em nuget.org, você pode enviá-lo
por push para o site de teste de nuget.org em https://int.nugettest.org . Observe
que os pacotes carregados para int.nugettest.org podem não ser preservados.

Parabéns por criar e publicar seu primeiro pacote NuGet!

Vídeo relacionado
https://learn.microsoft.com/shows/NuGet-101/Create-and-Publish-a-NuGet-Package-
with-the-NET-CLI-5-of-5/player

Encontre mais vídeos sobre o NuGet no Channel 9 e no YouTube .

Próximas etapas
Veja mais detalhes sobre como criar pacotes com a CLI do dotnet:

Criar um pacote NuGet com a CLI do dotnet

Obtenha mais informações sobre como criar e publicar pacotes NuGet:

Publicar um pacote
Pacotes de pré-lançamento
Suporte a várias estruturas de destino
Controle de versão do pacote
Adicionar uma expressão ou arquivo de licença
Criar pacotes localizados
Criar pacotes de símbolos
Assinar pacotes

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Tutorial: Criar um aplicativo de console
.NET usando o Visual Studio para Mac
Artigo • 11/09/2023

Este tutorial mostra como criar e executar um aplicativo de console do .NET usando o
Visual Studio para Mac.

) Importante

A Microsoft anunciou a desativação do Visual Studio para Mac. O Visual Studio


para Mac não terá mais suporte a partir de 31 de agosto de 2024. As alternativas
incluem:

Visual Studio Code com o Kit de Desenvolvimento em C# e extensões


relacionadas, como o .NET MAUI e o Unity .
Visual Studio em execução no Windows em uma VM no Mac.
Visual Studio em execução no Windows em uma VM na Nuvem .

Para obter mais informações, confira o comunicado de desativação do Visual


Studio para Mac .

Pré-requisitos
Visual Studio para Mac versão 8.8 ou posterior. Selecione a opção para instalar o
.NET Core. A instalação do Xamarin é opcional para o desenvolvimento do .NET.
Para saber mais, consulte os recursos a seguir:
Tutorial: Instalar o Visual Studio para Mac.
Versões para macOS com suporte.
Versões do .NET compatíveis com Visual Studio para Mac.

Criar o aplicativo
1. Inicie o Visual Studio para Mac.

2. Escolha Novo na janela de início.


3. Na caixa de diálogo Novo projeto, selecione Aplicativo no nó Web e Console.
Selecione o modelo Aplicativo de Console e depois Avançar.

4. Na lista suspensa Estrutura de destino da caixa de diálogo Configurar seu novo


Aplicativo de Console, selecione .NET 5.0 e Avançar.

5. Digite "Olá, Mundo" como o Nome do projeto e selecione Criar.


O modelo cria um simples aplicativo “Olá, Mundo”. Ele chama o método
Console.WriteLine(String) para exibir "Olá, Mundo!" na janela do terminal.

O código do modelo define uma classe, Program , com um único método, Main , que usa
uma matriz String como um argumento:

C#

using System;

namespace HelloWorld
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}

Main é o ponto de entrada do aplicativo, o método que é chamado automaticamente

pelo runtime quando ele inicia o aplicativo. Quaisquer argumentos de linha de comando
fornecidos quando o aplicativo for iniciado estão disponíveis na matriz args .

Executar o aplicativo
1. Pressione ⌥ ⌘ ↵ ( opção + comando + enter ) para executar o aplicativo sem
depuração.
2. Feche a janela do terminal.

Aprimorar o aplicativo
Aprimore seu aplicativo para solicitar ao usuário seu nome e exibi-lo junto com a data e
hora.
1. Em Program.cs, substitua o conteúdo do método Main , que é a linha que chama
Console.WriteLine , pelo seguinte código:

C#

Console.WriteLine("What is your name?");


var name = Console.ReadLine();
var currentDate = DateTime.Now;
Console.WriteLine($"{Environment.NewLine}Hello, {name}, on
{currentDate:d} at {currentDate:t}!");
Console.Write($"{Environment.NewLine}Press any key to exit...");
Console.ReadKey(true);

Esse código mostra um prompt na janela do console e aguarda até que o usuário
insira uma cadeia de caracteres seguida da tecla Enter . Ele armazena essa cadeia
de caracteres em uma variável chamada name . Ele também recupera o valor da
propriedade DateTime.Now, que contém a hora local atual e o atribui a uma
variável chamada currentDate . E exibe esses valores na janela do console. Por fim,
ele exibe um prompt na janela do console e chama o método
Console.ReadKey(Boolean) para aguardar a entrada do usuário.

NewLine é uma maneira independente de plataforma e de linguagem para


representar uma quebra de linha. As alternativas são \n em C# e vbCrLf no Visual
Basic.

O sinal de dólar ( $ ) na frente de uma cadeia de caracteres permite colocar


expressões como nomes de variáveis em chaves na cadeia de caracteres. O valor
da expressão é inserido na cadeia de caracteres no lugar da expressão. Essa sintaxe
é conhecida como cadeia de caracteres interpolada.

2. Pressione ⌥ ⌘ ↵ ( opção + comando + enter ) para executar o aplicativo.

3. Responda à solicitação inserindo um nome e pressionando a tecla Enter .


4. Feche o terminal.

Próximas etapas
Neste tutorial, você criou um aplicativo de console .NET. No próximo tutorial, você
depura o aplicativo.

Depurar um aplicativo de console do .NET usando o Visual Studio para Mac

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Tutorial: Depurar um aplicativo de
console do .NET usando Visual Studio
para Mac
Artigo • 11/09/2023

) Importante

A Microsoft anunciou a desativação do Visual Studio para Mac. O Visual Studio


para Mac não terá mais suporte a partir de 31 de agosto de 2024. As alternativas
incluem:

Visual Studio Code com o Kit de Desenvolvimento em C# e extensões


relacionadas, como o .NET MAUI e o Unity .
Visual Studio em execução no Windows em uma VM no Mac.
Visual Studio em execução no Windows em uma VM na Nuvem .

Para obter mais informações, confira o comunicado de desativação do Visual


Studio para Mac .

Este tutorial apresenta as ferramentas de depuração disponíveis no Visual Studio para


Mac.

Pré-requisitos
Este tutorial funciona com o aplicativo de console que você cria em Criar um
aplicativo de console .NET usando o Visual Studio para Mac.

Usar a configuração do build de depuração


Depuração e Lançamento são as configurações de build internas do Visual Studio. Você
usa a configuração de build de depuração para depuração e a configuração de
lançamento para a distribuição da versão final.

Na configuração de depuração, um programa é compilado com informações de


depuração simbólicas e sem otimização. A otimização complica a depuração, porque a
relação entre o código fonte e as instruções geradas é mais complexa. A configuração
de versão de um programa não possui informações de depuração simbólica e é
totalmente otimizada.

Por padrão, o Visual Studio para Mac usa a configuração de compilação de depuração,
portanto, não é necessário alterá-la antes da depuração.

1. Inicie o Visual Studio para Mac.

2. Abra o projeto que você criou em Criar um aplicativo de console .NET usando o
Visual Studio para Mac.

A configuração de build atual é mostrada na barra de ferramentas. A seguinte


imagem da barra de ferramentas mostra que o Visual Studio está configurado para
compilar na versão de Depuração do aplicativo:

Definir um ponto de interrupção


Um ponto de interrupção interrompe temporariamente a execução do aplicativo antes
de a linha com o ponto de interrupção ser executada.

1. Defina um ponto de interrupção na linha que exibe o nome, a data e a hora. Para
fazer isso, posicione o cursor na linha de código e pressione ⌘ \ ( comando + \ ).
Outra maneira de definir um ponto de interrupção é selecionando
Depurar>Ativar/desativar pontos de interrupção no menu.

O Visual Studio indica a linha na qual o ponto de interrupção é definido


destacando-a e exibindo um ponto vermelho na margem esquerda.
2. Pressione ⌘ ↵ ( comando + enter ) para iniciar o programa no modo de depuração.
Outra maneira de iniciar a depuração é escolhendo Depurar>Iniciar Depuração no
menu.

3. Digite uma cadeia de caracteres na janela do terminal quando o programa solicitar


um nome e, em seguida, pressione Enter .

4. A execução do programa para quando ele atinge o ponto de interrupção antes de


o método Console.WriteLine ser executado.

Usar a janela Imediata


A janela Imediata permite interagir com o aplicativo que você está depurando. Você
pode alterar o valor das variáveis de maneira interativa para ver como isso afeta o
programa.

1. Se a janela Imediata não estiver visível, exiba-a escolhendo Exibir>Janelas de


Depurar>Imediata.

2. Insira name = "Gracie" na janela Imediato e pressione Enter .

3. Digite currentDate = currentDate.AddDays(1) na janela Imediato e pressione


Enter .

A janela Imediata exibe o novo valor da variável de cadeia de caracteres e as


propriedades do valor DateTime.
A janela Locais exibe os valores das variáveis que são definidas no método que
está sendo executado no momento. Os valores das variáveis que você acabou de
alterar são atualizados na janela Locais.

4. Pressione ⌘ ↵ ( comando + enter ) para continuar a depuração.

Os valores exibidos no terminal também correspondem às alterações feitas na


janela Imediata.

Se o Terminal não foi exibido, selecione Terminal – HelloWorld na barra de


navegação inferior.

5. Pressione qualquer tecla para encerrar o programa.


6. Feche a janela do terminal.

Definir um ponto de interrupção condicional


O programa exibe uma cadeia de caracteres que o usuário insere. O que acontecerá se o
usuário não inserir nada? Isso pode ser testado com um recurso de depuração útil
chamado ponto de interrupção condicional.

1. ctrl -clique no ponto vermelho que representa o ponto de interrupção. No menu


de contexto, selecione Editar ponto de interrupção.

2. Na caixa de diálogo Editar ponto de interrupção, insira o código a seguir no


campo após E a seguinte condição é verdadeira e selecione Aplicar.

C#

String.IsNullOrEmpty(name)
Em todas as ocorrências do ponto de interrupção, o depurador chama o método
String.IsNullOrEmpty(name) e ele é interrompido nessa linha somente quando a

chamada do método retorna true .

Em vez de uma expressão condicional, você pode especificar uma contagem de


ocorrências, que interrompe a execução do programa antes que uma instrução seja
executada um determinado número de vezes.

3. Pressione ⌘ ↵ ( comando + enter ) para iniciar a depuração.

4. Na janela do terminal, pressione Enter quando solicitado a inserir seu nome.

Como a condição especificada ( name é null ou String.Empty) foi atendida, a


execução do programa para quando ele chega no ponto de interrupção.

5. Selecione a janela Locais, que mostra os valores das variáveis que são locais para o
método em execução no momento. Nesse caso, Main é o método em execução no
momento. Observe que o valor da variável name é "" , ou seja, String.Empty.

6. Também é possível ver se o valor é uma cadeia de caracteres vazia inserindo o


nome da variável name na janela Imediata e pressionando Enter .
7. Pressione ⌘ ↵ ( comando + enter ) para continuar a depuração.

8. Na janela do terminal, pressione qualquer tecla para sair do programa.

9. Feche a janela do terminal.

10. Desmarque o ponto de interrupção clicando no ponto vermelho na margem


esquerda da janela de código. Outra maneira de desmarcar um ponto de
interrupção é escolhendo Depurar > Ativar/desativar pontos de interrupção
enquanto a linha de código está selecionada.

Percorrer um programa
O Visual Studio também permite percorrer linha por linha de um programa e monitorar
sua execução. Normalmente, você define um ponto de interrupção e segue o fluxo do
programa em uma pequena parte do código do programa. Como esse programa é
pequeno, você pode percorrer todo o programa.

1. Defina um ponto de interrupção na chave que marca o início do método Main


(pressione o comando + \ ).

2. Pressione ⌘ ↵ ( comando + enter ) para iniciar a depuração.

O Visual Studio para na linha com o ponto de interrupção.

3. Pressione ⇧ ⌘ I ( shift + comando + I ) ou selecione Depurar>Intervir para


avançar uma linha.

O Visual Studio realça e exibe uma seta ao lado da próxima linha de execução.
Neste ponto, a janela Locais mostra que a matriz args está vazia e name e
currentDate têm valores padrão. Além disso, o Visual Studio abriu um terminal em

branco.

4. Pressione ⇧ ⌘ I ( shift + comando + I ).

O Visual Studio destaca a instrução que inclui a atribuição de variável name . A


janela Locais mostra que name é null , e o terminal exibe a cadeia de caracteres
"Qual é o seu nome?".

5. Responda à solicitação inserindo uma cadeia de caracteres na janela do console e


pressionando Enter .

6. Pressione ⇧ ⌘ I ( shift + comando + I ).

O Visual Studio destaca a instrução que inclui a atribuição de variável currentDate .


A janela Locais mostra o valor retornado pela chamada para o método
Console.ReadLine. Terminal exibe a cadeia de caracteres inserida no prompt.

7. Pressione ⇧ ⌘ I ( shift + comando + I ).

A janela Locais mostra o valor da variável currentDate após a atribuição da


propriedade DateTime.Now. O terminal permanece inalterado.

8. Pressione ⇧ ⌘ I ( shift + comando + I ).

O Visual Studio chama o método Console.WriteLine(String, Object, Object). O


terminal exibe a cadeia de caracteres formatada.

9. Pressione ⇧ ⌘ U ( shift + comando + U ) ou selecione Run>Depuração Circular.

O terminal exibe uma mensagem e aguarda até que uma tecla seja pressionada.

10. Pressione qualquer tecla para encerrar o programa.

Usar a configuração de build da Versão


Quando tiver testado a versão da depuração do seu aplicativo, você deverá compilar e
testar a versão de lançamento. A versão de lançamento incorpora otimizações do
compilador que podem afetar negativamente o comportamento de um aplicativo. Por
exemplo, as otimizações de compilador que são projetadas para melhorar o
desempenho podem criar condições de corrida em aplicativos multi-threaded.

Para compilar e testar a versão de Lançamento do aplicativo de console, execute as


seguintes etapas:
1. Altere a configuração de build na barra de ferramentas de Depuração para
Lançamento.

2. Pressione ⌥ ⌘ ↵ ( opção + comando + enter ) para executar sem depuração.

Próximas etapas
Neste tutorial, você usou as ferramentas de depuração do Visual Studio. No próximo
tutorial, você publicará uma versão implantável do aplicativo.

Publicar um aplicativo de console do .NET usando Visual Studio para Mac

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Tutorial: Publicar um aplicativo de
console do .NET usando Visual Studio
para Mac
Artigo • 12/09/2023

) Importante

A Microsoft anunciou a desativação do Visual Studio para Mac. O Visual Studio


para Mac não terá mais suporte a partir de 31 de agosto de 2024. As alternativas
incluem:

O Visual Studio Code com o Kit de Desenvolvimento em C# e extensões


relacionadas, como .NET MAUI e Unity .
Visual Studio em execução no Windows em uma VM no Mac.
Visual Studio em execução no Windows em uma VM na Nuvem .

Para obter mais informações, confira o comunicado de desativação do Visual


Studio para Mac .

Este tutorial mostra como publicar um aplicativo de console para que outros usuários
possam executá-lo. A publicação cria o conjunto de arquivos necessários para executar
seu aplicativo. Para implantar os arquivos, copie-os para o computador de destino.

Pré-requisitos
Este tutorial funciona com o aplicativo de console que você cria em Criar um
aplicativo de console .NET usando o Visual Studio para Mac.

Publicar o aplicativo
1. Inicie o Visual Studio para Mac.

2. Abra o projeto OlaMundo que você criou em Criar um aplicativo de console .NET
usando o Visual Studio para Mac.

3. Certifique-se de que o Visual Studio esteja compilando a versão de lançamento de


seu aplicativo. Se necessário, altere a configuração de build na barra de
ferramentas de Depuração para Lançamento.
4. No menu principal, escolha Compilar>Publicar na Pasta....

5. Na caixa de diálogo Publicar na Pasta, selecione Publicar.

A pasta de publicação é aberta, mostrando os arquivos que foram criados.


6. Selecione o ícone de engrenagem e selecione Copiar "publicar" como nome do
caminho no menu de contexto.

Inspecionar os arquivos
O processo de publicação cria uma implantação dependente de estrutura, que é um
tipo de implantação em que o aplicativo publicado é executado em um computador
que tenha o runtime do .NET instalado. Os usuários podem executar o aplicativo
publicado executando o comando dotnet HelloWorld.dll em um prompt de comando.

Como mostra a imagem anterior, a saída publicada inclui os seguintes arquivos:

HelloWorld.deps.json
Esse é o arquivo de dependências de runtime do aplicativo. Ele define os
componentes e as bibliotecas do .NET (incluindo a biblioteca de vínculo dinâmico
que contém o aplicativo) necessários para executar o aplicativo. Para obter mais
informações, confira Arquivos de configuração de runtime .

HelloWorld.dll

Essa é a versão de implantação dependente de estrutura do aplicativo. Para


executar essa biblioteca de links dinâmicos, insira dotnet HelloWorld.dll em um
prompt de comando. Esse método de execução do aplicativo funciona em
qualquer plataforma que tenha o runtime do .NET instalado.

HelloWorld.pdb (opcional para implantação)

Esse é o arquivo de símbolos de depuração. Não é necessário implantar esse


arquivo juntamente com seu aplicativo, embora você deva salvá-lo caso precise
depurar a versão publicada do seu aplicativo.

HelloWorld.runtimeconfig.json

Esse é o arquivo de configuração de runtime do aplicativo. Ele identifica a versão


do .NET com base na qual o aplicativo foi criado para ser executado. Você também
pode adicionar opções de configuração a ele. Para obter mais informações, confira
Definições de configuração de runtime do .NET.

Executar o aplicativo publicado


1. Abra um terminal e navegue até a pasta publicar. Para fazer isso, insira cd e cole o
caminho copiado anteriormente. Por exemplo:

Console

cd ~/Projects/HelloWorld/HelloWorld/bin/Release/net5.0/publish/

2. Execute o aplicativo usando o comando dotnet :

a. Insira dotnet HelloWorld.dll e pressione Enter .

b. Insira um nome em resposta à solicitação e pressione qualquer tecla para sair.

Recursos adicionais
Implantação de aplicativos .NET
Publicar aplicativos .NET com a CLI do .NET
dotnet publish
Tutorial: publicar um aplicativo de console do .NET usando Visual Studio Code
Usar o SDK do .NET em ambientes de CI (integração contínua)

Próximas etapas
Neste tutorial, você publicou um aplicativo de console. No próximo tutorial, você criará
uma biblioteca de classes.

Criar uma biblioteca .NET usando o Visual Studio para Mac

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Tutorial: criar uma biblioteca de classes
.NET usando o Visual Studio para Mac
Artigo • 11/09/2023

) Importante

A Microsoft anunciou a desativação do Visual Studio para Mac. O Visual Studio


para Mac não terá mais suporte a partir de 31 de agosto de 2024. As alternativas
incluem:

O Visual Studio Code com o Kit de Desenvolvimento em C# e extensões


relacionadas, como .NET MAUI e Unity .
Visual Studio em execução no Windows em uma VM no Mac.
Visual Studio em execução no Windows em uma VM na Nuvem .

Para obter mais informações, consulte o comunicado de desativação do Visual


Studio para Mac .

Neste tutorial, você vai criar uma biblioteca de classes que contém um só método de
manipulação de cadeia de caracteres.

Uma biblioteca de classes define tipos e métodos que são chamados por um aplicativo.
Se a biblioteca for direcionada ao .NET Standard 2.0, ela poderá ser chamada por
qualquer implementação do .NET (incluindo o .NET Framework) que dê suporte ao .NET
Standard 2.0. Se a biblioteca for direcionada ao .NET 5, ela poderá ser chamada por
qualquer aplicativo direcionado ao .NET 5. Este tutorial mostra como fazer o
direcionamento ao .NET 5.

7 Observação

Seus comentários são muito importantes. Há duas maneiras de enviar comentários


à equipe de desenvolvimento no Visual Studio para Mac:

No Visual Studio para Mac, escolha Ajuda>Relatar um Problema no menu, ou


Relatar um Problema na tela de boas-vindas. Isso abrirá uma janela para
registrar um relatório de bug. Você pode acompanhar seus comentários no
portal Developer Community (Comunidade do Desenvolvedor) .
Para fazer uma sugestão, escolha Ajuda>Forneça uma Sugestão no menu ou
Forneça uma Sugestão na tela de boas-vindas. Isso leva você até a página da
Web da Comunidade de Desenvolvedores do Visual Studio para Mac .

Pré-requisitos
Instalar o Visual Studio para Mac versão 8.8 ou posterior. Selecione a opção para
instalar o .NET Core. A instalação do Xamarin é opcional para o desenvolvimento
do .NET. Para saber mais, consulte os recursos a seguir:
Tutorial: Instalar o Visual Studio para Mac.
Versões para macOS com suporte.
Versões do .NET compatíveis com Visual Studio para Mac.

Criar uma solução com um projeto de


biblioteca de classes
Uma solução do Visual Studio serve como um contêiner para um ou mais projetos. Crie
uma solução e um projeto de biblioteca de classes na solução. Você adicionará projetos
adicionais relacionados à mesma solução.

1. Inicie o Visual Studio para Mac.

2. Na janela inicial, selecione Novo Projeto.

3. Na caixa de diálogo Escolher um modelo para o novo projeto, selecioneWeb e


console>Biblioteca>Biblioteca de classes e então selecione Avançar.
4. Na caixa de diálogo Configurar sua nova Biblioteca de Classes , escolha .NET 5.0
e selecione Avançar.

5. Nomeie o projeto como "StringLibrary" e a solução "ClassLibraryProjects". Deixe a


opção Criar um diretório de projeto no diretório da solução marcada. Selecione
Criar.
6. No menu principal, selecione Exibir>solução e selecione o ícone de encaixe para
manter o painel aberto.

7. No painel Solução, expanda o nó StringLibrary para revelar o arquivo de classe


fornecido pelo modelo , Class1.cs. Pressione Ctrl-clique no arquivo, selecione
Renomear no menu de contexto e renomeie o arquivo para StringLibrary.cs. Abra o
arquivo e substitua o conteúdo pelo código a seguir:

C#

using System;

namespace UtilityLibraries
{
public static class StringLibrary
{
public static bool StartsWithUpper(this string str)
{
if (string.IsNullOrWhiteSpace(str))
return false;

char ch = str[0];
return char.IsUpper(ch);
}
}
}

8. Pressione ⌘ S ( Command + S ) para salvar o arquivo.

9. Selecione Erros na margem da parte inferior da janela do IDE para abrir o painel
Erros. Selecione o botão Compilar Saída.

10. Selecione Compilar>Compilar Tudo no menu.

A solução é compilada. O painel de saída da compilação mostra que a compilação


foi bem-sucedida.
Adicionar um aplicativo de console à solução
Adicione um aplicativo de console que usa a biblioteca de classes. O aplicativo solicitará
que o usuário insira uma cadeia de caracteres e informe se a cadeia de caracteres
começa com um caractere maiúsculo.

1. No painel Solução, pressione Ctrl -clique na solução ClassLibraryProjects .


Adicione um novo projeto de Aplicativo de Console selecionando o modelo nos
modelos de Web e console>Aplicativo e selecione Avançar.

2. Selecione .NET 5.0 como a Estrutura de Destino e selecione Avançar.

3. Nomeie o projeto ShowCase. Selecione Criar para criar o projeto na solução.

4. Abra o arquivo Program.cs. Substitua o código pelo seguinte código:

C#
using System;
using UtilityLibraries;

class Program
{
static void Main(string[] args)
{
int row = 0;

do
{
if (row == 0 || row >= 25)
ResetConsole();

string? input = Console.ReadLine();


if (string.IsNullOrEmpty(input)) break;
Console.WriteLine($"Input: {input} {"Begins with uppercase?
",30}: " +
$"{(input.StartsWithUpper() ? "Yes" :
"No")}{Environment.NewLine}");
row += 3;
} while (true);
return;

// Declare a ResetConsole local method


void ResetConsole()
{
if (row > 0)
{
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
Console.Clear();
Console.WriteLine($"{Environment.NewLine}Press <Enter> only
to exit; otherwise, enter a string and press <Enter>:
{Environment.NewLine}");
row = 3;
}
}
}

O programa solicita que o usuário insira uma cadeia de caracteres. Ele indica se a
cadeia de caracteres começa com um caractere maiúsculo. Se o usuário pressionar
a tecla Enter sem inserir uma cadeia de caracteres, o aplicativo será encerrado e a
janela do console será fechada.

O código usa a variável row ​para manter uma contagem do número de linhas de
dados gravadas na janela do console. Sempre que ela for igual ou superior a 25, o
código limpa a janela do console e exibe uma mensagem para o usuário.
Adicionar uma referência ao projeto
Inicialmente, o novo projeto de aplicativo de console não tem acesso à biblioteca de
classes. Para permitir que ele chame métodos na biblioteca de classes, crie uma
referência de projeto para o projeto da biblioteca de classes.

1. No painel Soluções, pressione Ctrl -clique no nó de Dependências do novo


projeto ShowCase. Selecione Adicionar referência no menu de contexto.

2. Na caixa de diálogo Referências, selecione o projeto StringLibrary e selecione OK.

Executar o aplicativo
1. Pressione Ctrl -clique no projeto ShowCase e selecione Executar projeto no menu
de contexto.

2. Experimente o programa inserindo cadeias de caracteres e pressionando Enter .


Depois pressione Enter para sair.

Recursos adicionais
Desenvolver bibliotecas com a CLI do .NET
Notas sobre a versão do Visual Studio 2019 para Mac
Versões do .NET Standard e plataformas com suporte.

Próximas etapas
Neste tutorial, você criou uma solução e um projeto de biblioteca, e adicionou um
projeto de aplicativo de console que usa a biblioteca. No próximo tutorial, você
adicionará um projeto de teste de unidade à solução.

Testar uma biblioteca de classes .NET usando o Visual Studio para Mac
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Testar uma biblioteca de classes .NET
usando o Visual Studio
Artigo • 12/09/2023

) Importante

A Microsoft anunciou a desativação do Visual Studio para Mac. O Visual Studio


para Mac não terá mais suporte a partir de 31 de agosto de 2024. As alternativas
incluem:

O Visual Studio Code com o Kit de Desenvolvimento em C# e extensões


relacionadas, como .NET MAUI e Unity .
Visual Studio em execução no Windows em uma VM no Mac.
Visual Studio em execução no Windows em uma VM na Nuvem .

Para obter mais informações, confira o comunicado de desativação do Visual


Studio para Mac .

Este tutorial mostra como automatizar o teste de unidade adicionando um projeto de


teste a uma solução.

Pré-requisitos
Este tutorial funciona com a solução que você cria em Criar uma biblioteca de
classes do .NET usando o Visual Studio para Mac.

Criar um projeto de teste de unidade


As unidade de teste fornecem testes de software automatizados durante o
desenvolvimento e a publicação. MSTest é uma das três estruturas de teste que você
pode escolher. As outras são xUnit e nUnit .

1. Inicie o Visual Studio para Mac.

2. Abra a solução ClassLibraryProjects criada em Criar uma biblioteca de classes do


.NET usando o Visual Studio para Mac.

3. No painel Solução, pressione ctrl e clique na solução ClassLibraryProjects e


selecione Adicionar>Novo Projeto.
4. Na caixa de diálogo Novo Projeto, selecione Testes no nó Web e Console.
Selecione o Projeto MSTest e depois Avançar.

5. Selecione .NET 5.0 como a Estrutura de Destino e selecione Avançar.

6. Nomeie o novo projeto como "StringLibraryTest" e selecione Criar.


O Visual Studio cria um arquivo de classe com o seguinte código:

C#

using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
}
}
}

O código-fonte criado pelo modelo de teste de unidade faz o seguinte:

Ele importa o namespace Microsoft.VisualStudio.TestTools.UnitTesting, que


contém os tipos usados para o teste de unidade.
Ele aplica o atributoTestClassAttribute à classe UnitTest1 .
Ele aplica o atributo TestMethodAttribute para TestMethod1 .

Cada método marcado com [TestMethod] em uma classe de teste marcada com
[TestClass] é executado automaticamente quando o teste de unidade é executado.

Adicionar uma referência ao projeto


Para que o projeto de teste funcione com a classe StringLibrary , adicione uma
referência ao projeto StringLibrary .

1. No painel Solução , pressione ctrl e clique em Dependências em


StringLibraryTest. Selecione Adicionar referências no menu de contexto.

2. Na caixa de diálogo Referências , selecione o projeto StringLibrary . Selecione OK.


Adicionar e executar métodos de teste de
unidade
Quando o Visual Studio executa um teste de unidade, ele executa cada método
marcado com o atributo TestMethodAttribute em uma classe marcada com o atributo
TestClassAttribute. Um método de teste termina quando a primeira falha é encontrada
ou quando todos os testes contidos no método têm êxito.

Os testes mais comuns chamam membros da classe Assert. Muitos métodos assert
incluem pelo menos dois parâmetros, um deles é o resultado esperado do teste, e o
outro é o resultado real do teste. Alguns dos métodos chamados com mais frequência
da classe Assert são mostrados na tabela a seguir:

Métodos assert Função

Assert.AreEqual Verifica se os dois valores ou objetos são iguais. O assert falha se os valores
ou objetos não forem iguais.

Assert.AreSame Verifica se duas variáveis de objeto se referem ao mesmo objeto. A assert


falhará se as variáveis se referirem a objetos diferentes.

Assert.IsFalse Verifica se uma condição é false . O assert falhará se a condição for true .

Assert.IsNotNull Verifica se um objeto não é null . A assert falhará se o objeto for null .

Você também pode usar o método Assert.ThrowsException em um método de teste


para indicar o tipo de exceção que ele deve gerar. O teste falhará se a exceção
especificada não for lançada.

Ao testar o método StringLibrary.StartsWithUpper , você quer fornecer um número de


cadeias de caracteres que comecem com um caractere maiúsculo. Você espera que o
método retorne true nesses casos, para que possa chamar o método Assert.IsTrue. Da
mesma forma, você deseja fornecer um número de cadeias de caracteres que comecem
com algo diferente de um caractere maiúsculo. Você espera que o método retorne
false nesses casos, para que possa chamar o método Assert.IsFalse.

Como seu método de biblioteca lida com cadeias de caracteres, convém ter certeza de
que ele manipulará com êxito uma cadeia de caracteres vazia (String.Empty), uma cadeia
de caracteres válida sem caracteres e cujo Length é 0 e uma cadeia de caracteres null
que não foi inicializada. Você pode chamar StartsWithUpper diretamente como um
método estático e passar um único argumento String. Ou você pode chamar
StartsWithUpper como um método de extensão em uma variável string atribuída a
null .

Você definirá três métodos, cada um deles chamará um método Assert para cada
elemento em uma matriz de cadeia de caracteres. Você chamará uma sobrecarga de
método que permite especificar uma mensagem de erro a ser exibida em caso de falha
no teste. A mensagem identifica a cadeia de caracteres que causou a falha.

Para criar os métodos de teste:

1. Abra o arquivo UnitTest1.cs e substitua o código pelo seguinte código:

C#

using Microsoft.VisualStudio.TestTools.UnitTesting;
using UtilityLibraries;

namespace StringLibraryTest
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestStartsWithUpper()
{
// Tests that we expect to return true.
string[] words = { "Alphabet", "Zebra", "ABC", "Αθήνα",
"Москва" };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsTrue(result,
string.Format("Expected for '{0}': true; Actual:
{1}",
word, result));
}
}

[TestMethod]
public void TestDoesNotStartWithUpper()
{
// Tests that we expect to return false.
string[] words = { "alphabet", "zebra", "abc",
"αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };
foreach (var word in words)
{
bool result = word.StartsWithUpper();
Assert.IsFalse(result,
string.Format("Expected for '{0}': false;
Actual: {1}",
word, result));
}
}

[TestMethod]
public void DirectCallWithNullOrEmpty()
{
// Tests that we expect to return false.
string?[] words = { string.Empty, null };
foreach (var word in words)
{
bool result = StringLibrary.StartsWithUpper(word);
Assert.IsFalse(result,
string.Format("Expected for '{0}': false;
Actual: {1}",
word == null ? "<null>" : word,
result));
}
}
}
}

O teste de caracteres maiúsculos no método TestStartsWithUpper inclui a letra


maiúscula grega alfa (U+0391) e a letra maiúscula cirílica EM (U+041C). O teste de
caracteres minúsculos no método TestDoesNotStartWithUpper inclui a letra
minúscula grega alfa (U+03B1) e a letra minúscula cirílica Ghe (U+0433).

2. Na barra de menus, selecione Arquivo>Salvar como. Na caixa de diálogo, verifique


se a Codificação está definida como Unicode (UTF-8).
3. Quando for perguntado se deseja substituir o arquivo existente, selecione
Substituir.

Se você não salvar seu código-fonte como um arquivo codificado em UTF8, o


Visual Studio poderá salvá-lo como um arquivo ASCII. Quando isso acontecer, o
runtime não decodificará de forma precisa os caracteres UTF8 fora do intervalo
ASCII e os resultados de teste não serão corretos.

4. Abra o painel Testes de Unidade no lado direito da tela. Selecione Exibir>Testes


no menu.

5. Clique no ícone Dock para manter o painel aberto.

6. Clique no botão Executar Tudo.

Todos os testes serão aprovados.


Identificar falhas de teste
Se você estiver fazendo o TDD (Desenvolvimento Orientado por Testes), você grava os
testes primeiro e eles falharão na execução. Em seguida, você adiciona o código ao
aplicativo que faz com que o teste seja bem-sucedido. Neste tutorial, você criou o teste
depois de gravar o código do aplicativo que ele valida, portanto, você não viu o teste
falhar. Para validar se um teste falha conforme o esperado, adicione um valor inválido à
entrada de teste.

1. Modifique a matriz words no método TestDoesNotStartWithUpper para incluir a


cadeia de caracteres “Error”. Não é necessário salvar o arquivo porque o Visual
Studio salva automaticamente os arquivos abertos quando uma solução é criada
para executar testes.

C#

string[] words = { "alphabet", "Error", "zebra", "abc",


"αυτοκινητοβιομηχανία", "государство",
"1234", ".", ";", " " };

2. Execute os testes novamente.

Dessa vez, a Janela Gerenciador de Testes indica que dois testes tiveram êxito e
um falhou.
3. Pressione ctrl e clique no teste com falha, TestDoesNotStartWithUpper , e
selecione Mostrar painel de resultados no menu de contexto.

O painel de Resultados mostra a mensagem produzida pelo assert: "Assert.IsFalse


falhou. Esperado para 'Error': false, real: True". Devido à falha, nenhuma cadeia de
caracteres na matriz após "Error" foi testada.
4. Remova a cadeia de caracteres "Error" que você adicionou na etapa 1. Execute
novamente o teste, e ele será aprovado.

Testar a versão de lançamento da biblioteca


Agora que todos os nossos testes foram aprovados ao executar a compilação de
Depuração da biblioteca, execute os testes mais uma vez na compilação de Lançamento
da biblioteca. Vários fatores, incluindo as otimizações do compilador, podem produzir
um comportamento diferente entre as compilações de Depuração e Lançamento.

Para testar a compilação de Lançamento:

1. Na barra de ferramentas do Visual Studio, altere a configuração de compilação de


Depurar para Lançamento.
2. No painel Solução, pressione ctrl e clique no projeto StringLibrary; selecione
Compilar no menu de contexto para compilar a biblioteca novamente.

3. Execute os testes de unidade novamente.

Os testes são aprovados.

Depurar testes
Se você estiver usando o Visual Studio para Mac como seu IDE, será possível usar o
mesmo processo mostrado no Tutorial: Depurar um aplicativo de console do .NET
usando o Visual Studio para Mac para depurar o código usando seu projeto de teste de
unidade. Em vez de iniciar o projeto do aplicativo ShowCase, pressione ctrl e clique
com no projeto StringLibraryTests e selecione Iniciar depuração do projeto no menu
de contexto.

O Visual Studio inicia o projeto de teste com o depurador anexado. A execução será
interrompida em qualquer ponto de interrupção adicionado ao projeto de teste ou ao
código da biblioteca subjacente.

Recursos adicionais
Teste de unidade no .NET

Próximas etapas
Neste tutorial, você testou uma biblioteca de classes. Você pode disponibilizar a
biblioteca para outras pessoas publicando-a no NuGet como um pacote. Para saber
como, siga um tutorial do NuGet:

Criar e publicar um pacote (CLI do dotnet)


Se você publicar uma biblioteca como um pacote NuGet, outras pessoas poderão
instalá-la e usá-la. Para saber como, siga um tutorial do NuGet:

Instalar e usar um pacote no Visual Studio para Mac

Uma biblioteca não precisa ser distribuída como um pacote. Ela pode ser empacotada
com um aplicativo de console que a usa. Para saber como publicar um aplicativo de
console, consulte o tutorial anterior nesta série:

Publicar um aplicativo de console do .NET usando Visual Studio para Mac

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Início Rápido: Instalar e usar um pacote
no Visual Studio para Mac
Artigo • 10/08/2023

Os pacotes NuGet contém código reutilizável que outros desenvolvedores


disponibilizam para uso em seus projetos. Consulte O que há de novo no NuGet para
obter mais informações. Os pacotes são instalados em um projeto de Visual Studio para
Mac usando o NuGet Gerenciador de Pacotes. Este artigo demonstra o processo usando
o popular pacote Newtonsoft.Json e um projeto de console do .NET Core. O mesmo
processo se aplica a qualquer outro projeto do Xamarin ou do .NET Core.

Depois de instalado, consulte o pacote no código com using <namespace> em que


<namespace> é específico para o pacote que você está usando. Depois que a referência
é feita, você pode chamar o pacote por meio de sua API.

 Dica

Comece com nuget.org: navegar nuget.org é como os desenvolvedores do .NET


normalmente encontram componentes que podem reutilizar em seus próprios
aplicativos. Você pode pesquisar no nuget.org diretamente ou localizar e instalar
pacotes de dentro do Visual Studio, conforme mostrado neste artigo. Para saber
mais, confira Localizar e avaliar pacotes do NuGet.

Pré-requisitos
Visual Studio 2019 para Mac.

É possível instalar a edição Community 2019 gratuitamente em visualstudio.com ou


usar as edições Professional ou Enterprise.

Se você estiver usando Visual Studio no Windows, consulte Instalar e usar um pacote
em Visual Studio (somente Windows).

Criar um projeto
É possível instalar pacotes do NuGet em qualquer projeto .NET, desde que o pacote
ofereça suporte à mesma estrutura de destino do projeto.
Para este passo a passo, use um aplicativo simples do Console do .NET Core. Crie um
projeto em Visual Studio para Mac usando a Nova Solução de Arquivo>..., selecione o
modelo de Aplicativo do Console de Aplicativo > do .NET Core>. Clique em Próximo.
Aceite os valores padrão da Estrutura de Destino quando solicitado.

O Visual Studio criará o projeto, que será aberto no Gerenciador de Soluções.

Adicionar o pacote do NuGet Newtonsoft.Json


Para instalar o pacote, use o NuGet Gerenciador de Pacotes. Ao instalar um pacote, o
NuGet registra a dependência no arquivo de projeto ou em um arquivo
packages.config (dependendo do formato do projeto). Para obter mais informações,

veja Visão geral e fluxo de trabalho do consumo de pacote.

Gerenciador de Pacotes NuGet


1. Em Gerenciador de Soluções, clique com o botão direito do mouse em
Dependências e escolha Adicionar Pacotes....

2. Escolha "nuget.org" como a origem do pacote no canto superior esquerdo da


caixa de diálogo e pesquise Newtonsoft.Json, selecione esse pacote na lista e
selecione Adicionar Pacotes...:
Se você quiser obter mais informações sobre o NuGet Gerenciador de Pacotes,
consulte Instalar e gerenciar pacotes usando Visual Studio para Mac.

Use a API Newtonsoft.Json no aplicativo


Com o pacote Newtonsoft.Json no projeto, você pode chamar seu método
JsonConvert.SerializeObject para converter um objeto em uma cadeia de caracteres

legível.

1. Abra o Program.cs arquivo (localizado no Painel de Soluções) e substitua o


conteúdo do arquivo pelo seguinte código:

cs

using System;
using Newtonsoft.Json;

namespace NuGetDemo
{
public class Account
{
public string Name { get; set; }
public string Email { get; set; }
public DateTime DOB { get; set; }
}

class Program
{
static void Main(string[] args)
{
Account account = new Account()
{
Name = "Joe Doe",
Email = "joe@test.com",
DOB = new DateTime(1976, 3, 24)
};
string json = JsonConvert.SerializeObject(account);
Console.WriteLine(json);
}
}
}

2. Crie e execute o aplicativo selecionando Executar > Iniciar Depuração:

3. Depois que o aplicativo for executado, você verá a saída JSON serializada aparecer
no console:

Próximas etapas
Parabéns por instalar e usar seu primeiro pacote do NuGet!

Instalar e gerenciar pacotes usando Visual Studio para Mac

Para ver o que mais o NuGet tem a oferecer, selecione os links abaixo.
Visão geral e fluxo de trabalho do consumo de pacote
Referências de pacotes em arquivos de projeto

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Explore estes tutoriais e conheça o .NET
e as ferramentas do SDK do .NET
Artigo • 15/02/2023

Os tutoriais a seguir mostram como desenvolver aplicativos de console e bibliotecas


para .NET Core, .NET 5 e versões posteriores. Para outros tipos de aplicativos, consulte
Tutoriais para começar a usar o .NET.

Use Visual Studio.


Criar um aplicativo de console
Depurar um aplicativo
Publicar um aplicativo
Criar uma biblioteca de classes
Teste de unidade de uma biblioteca de classes
Instalar e usar um pacote
Criar e publicar um pacote
Criar um aplicativo de console F#

Usar o Visual Studio Code


Escolha estes tutoriais se quiser usar o Visual Studio Code ou algum outro editor de
código. Todos usam a CLI para tarefas de desenvolvimento do .NET Core, portanto,
todos, exceto o tutorial de depuração, podem ser usados com qualquer editor de
código.

Criar um aplicativo de console


Depurar um aplicativo
Publicar um aplicativo
Criar uma biblioteca de classes
Teste de unidade de uma biblioteca de classes
Instalar e usar um pacote
Criar e publicar um pacote
Criar um aplicativo de console F#

Usar o Visual Studio para Mac


Criar um aplicativo de console
Depurar um aplicativo
Publicar um aplicativo
Criar uma biblioteca de classes
Teste de unidade de uma biblioteca de classes
Instalar e usar um pacote
Criar um aplicativo de console F#

Tópicos avançados
Como criar bibliotecas
Fazer teste de unidade de um aplicativo com xUnit
Teste de unidade usando C#/VB/F# com NUnit/xUnit/MSTest
Teste de unidade ao vivo com Visual Studio
Criar modelos para a CLI
Criar e usar ferramentas para a CLI
Criar um aplicativo com plug-ins
Novidades do .NET 9
Artigo • 18/03/2024

Saiba mais sobre os novos recursos do .NET 9 e encontre links para documentação
adicional.

O .NET 9, o sucessor do .NET 8, tem um foco especial em desempenho e aplicativos


nativos da nuvem. Ele terá suporte por 18 meses como uma versão STS (prazo padrão
de suporte). Você pode baixar o .NET 9 aqui .

Uma novidade no .NET 9, a equipe de engenharia publica atualizações de versão prévia


do .NET 9 nas Discussões do GitHub . Esse é um ótimo lugar para fazer perguntas e
fornecer comentários sobre o lançamento.

Este artigo foi atualizado para o .NET 9 versão prévia 2. As seções a seguir descrevem as
atualizações para as bibliotecas principais do .NET no .NET 9.

Runtime do .NET
Serialização
LINQ
Coleções
Criptografia
Reflexão
Desempenho

Serialização
No System.Text.Json, o .NET 9 tem novas opções para serializar JSON e um novo
singleton que facilita a serialização usando padrões da Web.

Opções de recuo
O JsonSerializerOptions inclui novas propriedades que permitem personalizar o
caractere de recuo e o tamanho de recuo do JSON escrito.

C#

var options = new JsonSerializerOptions


{
WriteIndented = true,
IndentCharacter = '\t',
IndentSize = 2,
};

string json = JsonSerializer.Serialize(


new { Value = 1 },
options
);
Console.WriteLine(json);
//{
// "Value": 1
//}

Opções padrão da Web


Se você quiser serializar com as opções padrão que o ASP.NET Core usa para aplicativos
Web, use o novo singleton JsonSerializerOptions.Web.

C#

string webJson = JsonSerializer.Serialize(


new { SomeValue = 42 },
JsonSerializerOptions.Web // Defaults to camelCase naming policy.
);
Console.WriteLine(webJson);
// {"someValue":42}

LINQ
Os novos métodos CountBy e AggregateBy foram introduzidos. Esses métodos tornam
possível agregar estado por chave sem a necessidade de alocar agrupamentos
intermediários via GroupBy.

CountBy permite calcular rapidamente a frequência de cada chave. O exemplo a seguir


localiza a palavra que ocorre com mais frequência em uma cadeia de caracteres de
texto.

C#

string sourceText = """


Lorem ipsum dolor sit amet, consectetur adipiscing elit.
Sed non risus. Suspendisse lectus tortor, dignissim sit amet,
adipiscing nec, ultricies sed, dolor. Cras elementum ultrices amet diam.
""";

// Find the most frequent word in the text.


KeyValuePair<string, int> mostFrequentWord = sourceText
.Split(new char[] { ' ', '.', ',' },
StringSplitOptions.RemoveEmptyEntries)
.Select(word => word.ToLowerInvariant())
.CountBy(word => word)
.MaxBy(pair => pair.Value);

Console.WriteLine(mostFrequentWord.Key); // amet

AggregateBy permite implementar fluxos de trabalho de uso mais geral. O exemplo a


seguir mostra como você pode calcular pontuações associadas a uma determinada
chave.

C#

(string id, int score)[] data =


[
("0", 42),
("1", 5),
("2", 4),
("1", 10),
("0", 25),
];

var aggregatedData =
data.AggregateBy(
keySelector: entry => entry.id,
seed: 0,
(totalScore, curr) => totalScore + curr.score
);

foreach (var item in aggregatedData)


{
Console.WriteLine(item);
}
//(0, 67)
//(1, 15)
//(2, 4)

Index<TSource>(IEnumerable<TSource>) torna possível extrair rapidamente o índice


implícito de uma enumeração. Agora você pode escrever código como o trecho a seguir
para indexar itens automaticamente em uma coleção.

C#

IEnumerable<string> lines2 = File.ReadAllLines("output.txt");


foreach ((int index, string line) in lines2.Index())
{
Console.WriteLine($"Line number: {index + 1}, Line: {line}");
}
Coleções
O tipo de coleção PriorityQueue<TElement,TPriority> no System.Collections.Generic
namespace inclui um novo método Remove(TElement, TElement, TPriority,
IEqualityComparer<TElement>) que você pode usar para atualizar a prioridade de um
item na fila.

Método PriorityQueue.Remove()

O .NET 6 introduziu a coleção PriorityQueue<TElement,TPriority>, que fornece uma


implementação simples e rápida de heap de matriz. Um problema com heaps de matriz
em geral é que eles não dão suporte a atualizações prioritárias , o que os torna
proibitivos para uso em algoritmos, como variações do algoritmo de Dijkstra .

Embora não seja possível implementar atualizações de prioridade O(log n) eficientes na


coleção existente, o novo método PriorityQueue<TElement,TPriority>.Remove(TElement,
TElement, TPriority, IEqualityComparer<TElement>) torna possível emular atualizações
de prioridade (embora em tempo O(n)):

C#

public static void UpdatePriority<TElement, TPriority>(


this PriorityQueue<TElement, TPriority> queue,
TElement element,
TPriority priority
)
{
// Scan the heap for entries matching the current element.
queue.Remove(element, out _, out _);
// Re-insert the entry with the new priority.
queue.Enqueue(element, priority);
}

Esse método desbloqueia usuários que desejam implementar algoritmos de grafo em


contextos onde o desempenho assintótico não é um bloqueador. (Esses contextos
incluem educação e prototipagem.) Por exemplo, aqui está um exemplo de
implementação do algoritmo de Dijkstra que usa a nova API.

Criptografia
Para criptografia, o .NET 9 adiciona um novo método de hash de uso único no tipo
CryptographicOperations. Ele também adiciona novas classes que usam o algoritmo
KMAC.
Método CryptographicOperations.HashData()
O .NET inclui várias implementações estáticas de uso único de funções de hash e
funções relacionadas. Essas APIs incluem SHA256.HashData e HMACSHA256.HashData.
As APIs de uso único são preferíveis de usar porque podem fornecer o melhor
desempenho possível e reduzir ou eliminar alocações.

Se um desenvolvedor quiser fornecer uma API que ofereça suporte a hash em que o
chamador define qual algoritmo de hash usar, isso geralmente é feito aceitando um
argumento HashAlgorithmName. No entanto, usar esse padrão com APIs de uso único
exigiria alternar todos os possíveis HashAlgorithmName e, em seguida, usar o método
apropriado. Para resolver esse problema, o .NET 9 apresenta a API
CryptographicOperations.HashData. Essa API permite que você produza um hash ou
HMAC sobre uma entrada como uso único, em que o algoritmo usado é determinado
por um arquivo HashAlgorithmName.

C#

static void HashAndProcessData(HashAlgorithmName hashAlgorithmName, byte[]


data)
{
byte[] hash = CryptographicOperations.HashData(hashAlgorithmName, data);
ProcessHash(hash);
}

Algoritmo KMAC
O .NET 9 fornece o algoritmo KMAC conforme especificado pelo NIST SP-800-185 .O
KMAC (Código de Autenticação de Mensagens) KECCAK é uma função pseudoaleatória
e função hash com chave baseada em KECCAK.

As novas classes a seguir usam o algoritmo KMAC. Use instâncias para acumular dados
a fim de produzir um MAC ou use o método estático HashData para uso único em
apenas uma entrada.

Kmac128
Kmac256
KmacXof128
KmacXof256

O KMAC está disponível no Linux com OpenSSL 3.0 ou posterior e no Windows 11 Build
26016 ou posterior. Você pode usar a propriedade estática IsSupported para determinar
se a plataforma dá suporte ao algoritmo desejado.
C#

if (Kmac128.IsSupported)
{
byte[] key = GetKmacKey();
byte[] input = GetInputToMac();
byte[] mac = Kmac128.HashData(key, input, outputLength: 32);
}
else
{
// Handle scenario where KMAC isn't available.
}

Reflexão
Nas versões do .NET Core e do .NET 5-8, o suporte para criar um assembly e emitir
metadados de reflexão para tipos criados dinamicamente era limitado a um executável
AssemblyBuilder. A falta de suporte para salvar um assembly geralmente era um
bloqueador para os clientes que migravam do .NET Framework para o .NET. O .NET 9
adiciona APIs públicas a AssemblyBuilder para salvar um assembly emitido.

A nova implementação AssemblyBuilder persistente é independente de runtime e


plataforma. Para criar uma instância de AssemblyBuilder persistente, use a nova API
AssemblyBuilder.DefinePersistedAssembly. A API
AssemblyBuilder.DefineDynamicAssembly existente aceita o nome do assembly e
atributos personalizados opcionais. Para usar a nova API, passe o assembly principal,
System.Private.CoreLib , que é usado para fazer referência a tipos base de runtime. Não

há opção para AssemblyBuilderAccess. E, por enquanto, a implementação persistente


AssemblyBuilder só dá suporte a salvar, não a executar. Depois de criar uma instância

do AssemblyBuilder persistente, as etapas subsequentes para definir um módulo, tipo,


método ou enum, escrita de IL e todos os outros usos permanecem inalterados. Isso
significa que você pode usar o código existente System.Reflection.Emit no estado em
que se encontra para salvar o assembly. O código a seguir mostra um exemplo.

C#

public void CreateAndSaveAssembly(string assemblyPath)


{
AssemblyBuilder ab = AssemblyBuilder.DefinePersistedAssembly(
new AssemblyName("MyAssembly"),
typeof(object).Assembly
);
TypeBuilder tb = ab.DefineDynamicModule("MyModule")
.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);

MethodBuilder mb = tb.DefineMethod(
"SumMethod",
MethodAttributes.Public | MethodAttributes.Static,
typeof(int), [typeof(int), typeof(int)]
);
ILGenerator il = mb.GetILGenerator();
il.Emit(OpCodes.Ldarg_0);
il.Emit(OpCodes.Ldarg_1);
il.Emit(OpCodes.Add);
il.Emit(OpCodes.Ret);

tb.CreateType();
ab.Save(assemblyPath); // or could save to a Stream
}

public void UseAssembly(string assemblyPath)


{
Assembly assembly = Assembly.LoadFrom(assemblyPath);
Type type = assembly.GetType("MyType");
MethodInfo method = type.GetMethod("SumMethod");
Console.WriteLine(method.Invoke(null, [5, 10]));
}

Desempenho
O .NET 9 inclui aprimoramentos para o compilador JIT de 64 bits que visam melhorar o
desempenho do aplicativo. Esses aprimoramentos do compilador incluem:

Melhor geração de código para loops.


Mais inlining de método para AOT nativo.
Verificações de tipo mais rápidas.

Vetorização Arm64 é outro novo recurso do runtime.

Otimizações de loop

Melhorar a geração de código para loops é uma prioridade para .NET 9, e o compilador
de 64 bits apresenta uma nova otimização chamada Ampliação de váriável de indução
(IV).

Uma IV é uma variável cujo valor é alterado conforme a iteração do loop contido nela.
No loop for a seguir, i é uma IV: for (int i = 0; i < 10; i++) . Se o compilador
puder analisar como o valor de um IV evolui em relação às iterações do loop, ele poderá
produzir um código com melhor desempenho para expressões relacionadas.

Considere o exemplo a seguir que itera por meio de uma matriz:

C#
static int Sum(int[] arr)
{
int sum = 0;
for (int i = 0; i < arr.Length; i++)
{
sum += arr[i];
}

return sum;
}

A variável de índice, i , tem 4 bytes de tamanho. No nível do assembly, os registros de


64 bits normalmente são usados para armazenar índices de matriz no x64 e, em versões
anteriores do .NET, o compilador gerou um código de zero estendido i a 8 bytes para
o acesso à matriz, mas continuou a tratar i como um inteiro de 4 bytes em outros
lugares. No entanto, estender i para 8 bytes requer uma instrução adicional sobre x64.
Com a ampliação do IV, o compilador JIT de 64 bits agora amplia i para 8 bytes em
todo o loop, omitindo a extensão zero. O loop sobre matrizes é muito comum, e os
benefícios dessa remoção de instrução aumentam rapidamente.

Melhorias inline para AOT Nativo


Um dos objetivos do .NET para o inliner do compilador JIT de 64 bits é remover o
máximo possível de restrições que impedem um método de ser inline. O .NET 9 permite
inlining de acessos a estática local de thread no Windows x64, Linux x64 e Linux Arm64.

Para membros da classe static , existe exatamente uma instância do membro em todas
as instâncias da classe, que “compartilham” o membro. Se o valor de um membro
static for exclusivo para cada thread, fazer com que esse valor thread-local pode

melhorar o desempenho, pois eliminará a necessidade de um primitivo de


simultaneidade acessar com segurança o membro static do thread incluído.

Anteriormente, os acessos à estática local do thread em programas compilados pelo


AOT nativo exigiam que o compilador JIT de 64 bits emitisse uma chamada no tempo de
execução para obter o endereço base do armazenamento local do thread. Agora, o
compilador pode incorporar essas chamadas, resultando em muito menos instruções
para acessar esses dados.

Melhorias do PGO: verificações de tipo e conversões


O .NET 8 habilitou a PGO (otimização guiada por perfil dinâmico) por padrão. O NET 9
expande a implementação PGO do compilador JIT de 64 bits para criar mais padrões de
código. Quando a compilação em camadas está habilitada, o compilador JIT de 64 bits
já insere a instrumentação em seu programa para criar o perfil de seu comportamento.
Quando ele recompila com otimizações, o compilador aproveita o perfil criado em
tempo de execução para tomar decisões específicas para a execução atual do seu
programa. No .NET 9, o compilador JIT de 64 bits usa dados PGO para melhorar o
desempenho de verificações de tipo.

Determinar o tipo de um objeto requer uma chamada para o tempo de execução, que
vem com uma penalidade de desempenho. Quando o tipo de um objeto precisa ser
verificado, o compilador JIT de 64 bits emite essa chamada para fins de correção (os
compiladores geralmente não podem descartar nenhuma possibilidade, mesmo que
pareçam improváveis). No entanto, se os dados de PGO sugerirem que um objeto
provavelmente será um tipo específico, o compilador JIT de 64 bits agora emitirá um
caminho rápido que verifica barato esse tipo e volta ao caminho lento de chamar para o
runtime somente se necessário.

Vetorização do Arm64 em bibliotecas do .NET

Uma nova implementação de EncodeToUtf8 aproveita a capacidade do compilador JIT


de 64 bits de emitir instruções de carregamento/armazenamento de vários registros no
Arm64. Esse comportamento permite que os programas processem partes maiores de
dados com menos instruções. Os aplicativos .NET em vários domínios devem ver
melhorias de taxa de transferência no hardware Arm64 que dá suporte a esses recursos.
Alguns parâmetros de comparação reduzem o tempo de execução em mais da
metade.

SDK .NET
Teste de unidade
Roll-forward de ferramenta do .NET

Teste de unidade
Esta seção descreve as atualizações do teste de unidade no .NET 9: a execução de testes
em paralelo e a saída de teste do agente de terminal.

Executar testes em paralelo


No .NET 9, dotnet test é mais totalmente integrado ao MSBuild. Como o MSBuild dá
suporte à criação em paralelo, você pode executar testes para o mesmo projeto em
diferentes estruturas de destino em paralelo. Por padrão, o MSBuild limita o número de
processos paralelos ao número de processadores no computador. Você também pode
definir seu próprio limite usando a opção -maxcpucount. Se você quiser recusar o
paralelismo, defina a propriedade MSBuild TestTfmsInParallel como false .

Exibição de teste do agente de terminal

O relatório de resultados de teste para dotnet test agora tem suporte diretamente no
agente de terminal do MSBuild. Você obtém relatórios de teste mais completos
enquanto os testes de estão em execução (exibe o nome do teste em execução) e após a
conclusão dos testes de (todos os erros de teste são renderizados de uma maneira
melhor).

Para obter mais informações sobre o registrador de terminal, consulte opções de dotnet
build.

Roll-forward de ferramenta do .NET


As ferramentas do .NET são aplicativos dependentes da estrutura que podem ser
instalados global ou localmente, em seguida, executados usando o SDK do .NET e os
tempos de execução do .NET instalados. Essas ferramentas, como todos os aplicativos
.NET, têm como destino uma versão principal específica do .NET. Por padrão, os
aplicativos não são executados em versões mais recentes do .NET. Os autores de
ferramentas foram capazes de aceitar a execução de suas ferramentas em versões mais
recentes do runtime do .NET definindo a propriedade RollForward MSBuild. No entanto,
nem todas as ferramentas fazem isso.

Uma nova opção para dotnet tool install permite que usuários decidam como as
ferramentas do .NET devem ser executadas. Ao instalar uma ferramenta por meio de
dotnet tool install , ou ao executar a ferramenta por meio de dotnet tool run

<toolname>, você pode especificar um novo sinalizador chamado --allow-roll-


forward . Essa opção configura a ferramenta com o modo roll-forward Major . Esse modo

permite que a ferramenta seja executada em uma versão principal mais recente do .NET
se a versão do .NET correspondente não estiver disponível. Esse recurso ajuda os
usuários pioneiros a usarem ferramentas do .NET sem que os autores de ferramentas
precisem alterar os códigos.

Confira também
Postagem no blog Nossa visão para o .NET 9
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Alterações interruptivas no .NET 9
Artigo • 16/02/2024

Se você estiver migrando um aplicativo para o .NET 9, poderá ser afetado pelas
alterações interruptivas listadas aqui. As alterações são agrupadas por área de
tecnologia, como ASP.NET Core ou Windows Forms.

Este artigo categoriza cada alteração interruptiva como incompatível binário ou


incompatível com a origem ou alteração comportamental:

Incompatível binário – Quando executado em relação ao novo runtime ou


componente, os binários existentes podem encontrar uma alteração interruptiva
no comportamento, como falha ao carregar ou executar e, nesse caso, exigir
recompilação.

Incompatível com a origem – Quando recompilado usando o novo SDK ou


componente ou para direcionar ao novo runtime, o código-fonte existente pode
exigir alterações de origem para que seja compilado com êxito.

Alteração comportamental – O código e os binários existentes podem se


comportar de modo diferente em tempo de execução. Se o novo comportamento
for indesejável, o código existente precisará ser atualizado e recompilado.

7 Observação

Este artigo é um trabalho em andamento. Esta não é uma lista completa de


alterações interruptivas no .NET 9. Para consultar alterações interruptivas que ainda
estão pendentes de publicação, confira Problemas do .NET .

Bibliotecas principais do .NET


ノ Expandir a tabela

Título Tipo de alteração Versão


introduzida

Não é possível criar um tipo de matriz System.Void Alteração de Preview 1


comportamento

O limite de tamanho da estrutura da matriz Alteração de Preview 1


embutida é aplicado comportamento
Título Tipo de alteração Versão
introduzida

InMemoryDirectoryInfo acrescenta rootDir aos Alteração de Preview 1


arquivos comportamento

RuntimeHelpers.GetSubArray retorna um tipo Alteração de Preview 1


diferente comportamento

Rede
ノ Expandir a tabela

Título Tipo de alteração Versão introduzida

HttpListenerRequest.UserAgent é anulável Incompatível com a origem Preview 1

SDK e MSBuild
ノ Expandir a tabela

Título Tipo de alteração Versão


introduzida

Alteração de saída de comandos dotnet Alteração de Preview 1


workload comportamento

O agente de terminal é o padrão Alteração de Preview 1


comportamento

Windows Forms
ノ Expandir a tabela

Título Tipo de alteração Versão


introduzida

BindingSource.SortDescriptions não retorna nulo Alteração de Preview 1


comportamento

Alterações em anotações de nulidade Incompatível com a Preview 1


origem
Título Tipo de alteração Versão
introduzida

ComponentDesigner.Initialize gera Alteração de Preview 1


ArgumentNullException comportamento

DataGridViewRowAccessibleObject.Name índice da Alteração de Preview 1


linha inicial comportamento

Nenhuma exceção se DataGridView for nulo Alteração de Preview 1


comportamento

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Novidades do .NET 8
Artigo • 09/02/2024

O .NET 8 é o sucessor do .NET 7. Ele terá suporte por três anos como uma versão LTS
(suporte de longo prazo). Você pode baixar o .NET 8 aqui .

Runtime do .NET
O runtime do .NET 8 inclui aprimoramentos no desempenho, na coleta de lixo e nas
bibliotecas principal e de extensão. Também inclui um novo modo de globalização para
aplicativos móveis e novos geradores de origem para interoperabilidade COM e
associação de configuração. Para obter mais informações, confira Novidades do runtime
do .NET 8.

SDK .NET
Para obter informações sobre as novidades do SDK do .NET, do AOT nativo, da análise
de código e do diagnóstico, confira Novidades do SDK e das ferramentas do .NET 8.

C# 12
C# 12 fornecido com o SDK do .NET 8. Para obter mais informações, confira Novidades
do C# 12.

.NET Aspire
O .NET Aspire é uma inovadora pilha pronta para nuvem para criação de aplicativos
observáveis, prontos para produção e distribuídos.​Ele é fornecido por meio de uma
coleção de pacotes NuGet que trata preocupações nativas de nuvem específicas e está
disponível em versão prévia no .NET 8. Para obter mais informações, confira .NET Aspire
(versão prévia).

ASP.NET Core
O ASP.NET Core inclui aprimoramentos no Blazor, no SignalR, nas APIs mínimas, no AOT
nativo, nos servidores Kestrel e HTTP.sys, além da autenticação e da autorização. Para
obter mais informações, confira Novidades do ASP.NET Core 8.0.
.NET MAUI
O .NET MAUI inclui novas funcionalidades para controles, reconhecedores de gestos,
aplicativos do Windows, navegação e integração de plataforma. Também inclui algumas
alterações de comportamento e vários aprimoramentos de desempenho. Para obter
mais informações, confira Novidades do .NET MAUI para .NET 8.

EF Core
O Entity Framework Core inclui aprimoramentos em objetos de tipos complexos,
coleções de tipos primitivos, mapeamento de colunas JSON, consultas SQL brutas,
carregamento lento, acesso a entidades controladas, criação de modelos, conversões
matemáticas e outros recursos. Também inclui um novo tipo HierarchyId . Para obter
mais informações, confira Novidades do EF Core 8.

Windows Forms
O Windows Forms inclui aprimoramentos na associação de dados, no DPI do Visual
Studio e no DPI alto. Os comandos de botão também já estão totalmente habilitados.
Para obter mais informações, confira Novidades do .NET 8 (Windows Forms).

Windows Presentation Foundation


O WPF (Windows Presentation Foundation) adiciona a capacidade de usar aceleração de
hardware e um novo controle OpenFolderDialog. Para obter mais informações, confira
Novidades do WPF para .NET 8.

Confira também
Alterações interruptivas no .NET 8

Comunicados da versão prévia do .NET


Comunicado sobre o .NET 8
Anúncio do .NET 8 RC 2
Anunciando o .NET 8 RC 1
Anunciando o .NET 8 Versão Prévia 7
Anunciando o .NET 8 versão prévia 6
Anunciando o .NET 8 versão prévia 5
Anunciando o .NET 8 versão prévia 4
Anunciando o .NET 8 versão prévia 3
Anunciando o .NET 8 versão prévia 2
Anunciando o .NET 8 versão prévia 1

Comunicados da versão prévia do ASP.NET Core


ASP.NET Core no .NET 8
Atualizações do ASP.NET Core no .NET 8 RC 2
atualizações do ASP.NET Core no .NET 8 RC 1
Atualizações do ASP.NET Core no .NET 8 Versão Prévia 7
Atualizações do ASP.NET Core no .NET 8 versão prévia 6
Atualizações do ASP.NET Core no .NET 8 versão prévia 5
Atualizações do ASP.NET Core no .NET 8 versão prévia 4
Atualizações do ASP.NET Core no .NET 8 versão prévia 3
Atualizações do ASP.NET Core no .NET 8 versão prévia 2
Atualizações do ASP.NET Core no .NET 8 versão prévia 1

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Novidades no runtime do .NET 8
Artigo • 09/02/2024

Este artigo descreve novos recursos no runtime do .NET para .NET 8.

Aprimoramentos de desempenho
O .NET 8 inclui melhorias na geração de código e na compilação JIT (just-in-time):

Aprimoramentos no desempenho do Arm64


Aprimoramentos no SIMD
Suporte para extensões ISA AVX-512 (confira Vector512 e AVX-512)
Aprimoramentos nativos de nuvem
Aprimoramentos na taxa de transferência JIT
Otimizações gerais e do loop
Acesso otimizado para campos marcados com ThreadStaticAttribute
Alocação de registro consecutiva. O Arm64 tem duas instruções para pesquisa de
vetor de tabela, que exigem que todas as entidades em seus operandos de tupla
estejam presentes em registros consecutivos.
JIT/NativeAOT agora pode cancelar o registro e vetorizar automaticamente
algumas operações de memória com SIMD, como comparação, cópia e zero, se
puder determinar seus tamanhos em tempo de compilação.

Além disso, a PGO (otimização guiada por perfil dinâmico) foi aprimorada e agora está
habilitada por padrão. Você não precisa mais usar uma opção de configuração de
runtime para habilitá-la. O PGO dinâmico funciona de forma estreita com a compilação
em camadas para otimizar ainda mais o código com base na instrumentação adicional
que é implementada durante a camada 0.

Em média, o PGO dinâmico aumenta o desempenho em cerca de 15%. Em um conjunto


de parâmetros de comparação de ~4600 testes, 23% tiveram melhorias de desempenho
de 20% ou mais.

Promoção de struct codegen


O .NET 8 inclui um novo passe de otimização de promoção física para codegen que
generaliza a capacidade do JIT de promover variáveis de struct. Essa otimização
(também chamada de substituição escalar de agregações) substitui os campos de
variáveis struct por variáveis primitivas que o JIT é capaz de raciocinar e otimizar com
mais precisão.
O JIT já tinha suporte para essa otimização, mas com várias limitações grandes,
incluindo:

Ele só tinha suporte para structs com quatro ou menos campos.


Só havia suporte se cada campo fosse um tipo primitivo ou um struct simples
encapsulando um tipo primitivo.

A promoção física remove essas limitações, o que corrige vários problemas de JIT de
longa data.

Coleta de lixo
O .NET 8 adiciona uma funcionalidade para ajustar o limite de memória em tempo real.
Isso é útil em cenários de serviço de nuvem, onde a demanda vem e vai. Para serem
econômicos, os serviços devem escalar e reduzir verticalmente o consumo de recursos à
medida que a demanda flutua. Quando um serviço detecta uma diminuição na
demanda, ele pode reduzir o consumo de recursos reduzindo seu limite de memória.
Anteriormente, isso falhava porque o coletor de lixo (GC) não sabia da alteração e
poderia alocar mais memória do que o novo limite. Com essa alteração, você pode
chamar a API RefreshMemoryLimit() para atualizar o GC com o novo limite de memória.

Existem algumas limitações a serem consideradas, como:

Em plataformas de 32 bits (por exemplo, Windows x86 e Linux ARM), o .NET não
poderá estabelecer um novo limite rígido de heap se ainda não houver um.
A API pode retornar um código de status diferente de zero indicando que a
atualização falhou. Isso pode acontecer se a redução horizontal for muito agressiva
e não deixar espaço para a GC manobrar. Nesse caso, chame GC.Collect(2,
GCCollectionMode.Aggressive) para reduzir o uso de memória atual e tente

novamente.
Se você escalar verticalmente o limite de memória além do tamanho que o GC
acredita que o processo pode lidar durante a inicialização, a chamada
RefreshMemoryLimit terá êxito, mas não poderá usar mais memória do que o que

ele percebe como o limite.

O snippet de código a seguir mostra como chamar a API.

C#

GC.RefreshMemoryLimit();
Você também pode atualizar algumas das configurações de GC relacionadas ao limite
de memória. O snippet de código a seguir define o limite rígido do heap como 100
mebibytes (MiB):

C#

AppContext.SetData("GCHeapHardLimit", (ulong)100 * 1_024 * 1_024);


GC.RefreshMemoryLimit();

A API pode gerar um InvalidOperationException se o limite rígido for inválido, por


exemplo, no caso de percentuais de limite rígido de heap negativo e se o limite rígido
for muito baixo. Isso pode acontecer se o limite rígido do heap definido pela
atualização, devido às novas configurações do AppData ou implícito pelas alterações de
limite de memória do contêiner, for menor do que o que já está confirmado.

Globalização para aplicativos móveis


Os aplicativos móveis no iOS, tvOS e MacCatalyst podem aceitar um novo modo de
globalização híbrido que usa um pacote de ICU mais leve. No modo híbrido, os dados
de globalização são extraídos parcialmente do pacote ICU e parcialmente de chamadas
para as APIs nativas. O modo híbrido atende a todas as localidades compatíveis com
dispositivo móvel .

O modo híbrido é mais adequado para aplicativos que não funcionam no modo de
globalização invariável e que usam culturas que foram cortadas dos dados de ICU em
dispositivos móveis. Você também pode usá-lo quando quiser carregar um arquivo de
dados de ICU menor. (O arquivo icudt_hybrid.dat é 34,5 % menor que o arquivo de
dados de ICU padrão icudt.dat.)

Para usar o modo híbrido de globalização, defina a propriedade MSBuild


HybridGlobalization como true:

XML

<PropertyGroup>
<HybridGlobalization>true</HybridGlobalization>
</PropertyGroup>

Existem algumas limitações a serem consideradas, como:

Devido a limitações da API nativa, nem todas as APIs de globalização têm suporte
no modo híbrido.
Algumas das APIs com suporte têm um comportamento diferente.
Para verificar se o aplicativo foi afetado, confira Diferenças comportamentais .

Interoperabilidade COM gerada pela origem


O .NET 8 inclui um novo gerador de origem que dá suporte à Interoperabilidade com
interfaces COM. Você pode usar o GeneratedComInterfaceAttribute para marcar uma
interface como uma interface COM para o gerador de origem. Em seguida, o gerador de
origem gerará código para habilitar a chamada do código C# para o código não
gerenciado. Ele também gera código para habilitar a chamada de código não
gerenciado em C#. Esse gerador de origem se integra ao LibraryImportAttribute e você
pode usar tipos com o GeneratedComInterfaceAttribute como parâmetros e tipos de
retorno em métodos com atributos de LibraryImport .

C#

using System.Runtime.InteropServices;
using System.Runtime.InteropServices.Marshalling;

[GeneratedComInterface]
[Guid("5401c312-ab23-4dd3-aa40-3cb4b3a4683e")]
partial interface IComInterface
{
void DoWork();
}

internal partial class MyNativeLib


{
[LibraryImport(nameof(MyNativeLib))]
public static partial void GetComInterface(out IComInterface
comInterface);
}

O gerador de origem também dá suporte ao novo atributo


GeneratedComClassAttribute para permitir que você passe tipos que implementam
interfaces com o atributo GeneratedComInterfaceAttribute para código não gerenciado.
O gerador de origem gerará o código necessário para expor um objeto COM que
implementa as interfaces e encaminha chamadas para a implementação gerenciada.

Os métodos em interfaces com o atributo GeneratedComInterfaceAttribute dão suporte


a todos os mesmos tipos que LibraryImportAttribute , e LibraryImportAttribute agora
dá suporte a tipos com o atributo GeneratedComInterface e a tipos com o atributo
GeneratedComClass .

Se o código C# usar apenas uma interface com GeneratedComInterface atribuído para


encapsular um objeto COM de código não gerenciado ou encapsular um objeto
gerenciado de C# para expor a código não gerenciado, você poderá usar as opções na
propriedade Options para personalizar qual código será gerado. Essas opções significam
que você não precisa escrever marshallers para cenários que você sabe que não serão
usados.

O gerador de origem usa o novo tipo StrategyBasedComWrappers para criar e gerenciar


os wrappers de objeto COM e os wrappers de objeto gerenciado. Esse novo tipo
manipula o fornecimento da experiência de usuário esperada do .NET para a
interoperabilidade COM, ao mesmo tempo em que fornece pontos de personalização
para usuários avançados. Se seu aplicativo tiver seu próprio mecanismo para definir
tipos de COM ou se você precisar dar suporte a cenários que o COM gerado pela
origem atualmente não dá suporte, considere usar o novo tipo
StrategyBasedComWrappers para adicionar os recursos ausentes para seu cenário e
obter a mesma experiência de usuário do .NET para seus tipos COM.

Se você estiver usando o Visual Studio, novos analisadores e correções de código


facilitam a conversão do código de interoperabilidade COM existente para usar a
interoperabilidade gerada pela origem. Ao lado de cada interface que tem o
ComImportAttribute, uma lâmpada oferece uma opção para converter em
interoperabilidade gerada pela origem. A correção altera a interface para usar o atributo
GeneratedComInterfaceAttribute. E ao lado de cada classe que implementa uma
interface com GeneratedComInterfaceAttribute , uma lâmpada oferece uma opção para
adicionar o atributo GeneratedComClassAttribute ao tipo . Depois que os tipos forem
convertidos, você poderá mover seus métodos DllImport para usar o
LibraryImportAttribute .

Limitações
O gerador de origem COM não dá suporte à afinidade de apartamento, usando a
palavra-chave new para ativar um CoClass COM e as seguintes APIs:

Interfaces baseadas em IDispatch.


Interfaces baseadas em IInspectable.
Propriedades e eventos COM.

Gerador de origem para configuração da


associação
O .NET 8 apresenta um gerador de origem para fornecer AOT e configuração favorável
ao corte no ASP.NET Core. O gerador é uma alternativa à implementação baseada em
reflexão pré-existente.

O gerador investiga chamadas para Configure(TOptions), Bind e Get para recuperar


informações de tipo. Quando o gerador é habilitado em um projeto, o compilador
escolhe implicitamente os métodos gerados em vez das implementações de estrutura
baseadas em reflexão pré-existentes.

Nenhuma alteração de código-fonte é necessária para usar o gerador. Ele é habilitado


por padrão em aplicativos Web com AOT. Para outros tipos de projeto, o gerador de
origem está desativado por padrão, mas você pode ativar a opção definindo a
propriedade EnableConfigurationBindingGenerator como true no arquivo de projeto:

XML

<PropertyGroup>

<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerat
or>
</PropertyGroup>

O código a seguir mostra um exemplo de invocação do associador.

C#

public class ConfigBindingSG


{
static void RunIt(params string[] args)
{
WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IConfigurationSection section =
builder.Configuration.GetSection("MyOptions");

// !! Configure call - to be replaced with source-gen'd


implementation
builder.Services.Configure<MyOptions>(section);

// !! Get call - to be replaced with source-gen'd implementation


MyOptions? options0 = section.Get<MyOptions>();

// !! Bind call - to be replaced with source-gen'd implementation


MyOptions options1 = new();
section.Bind(options1);

WebApplication app = builder.Build();


app.MapGet("/", () => "Hello World!");
app.Run();
}

public class MyOptions


{
public int A { get; set; }
public string S { get; set; }
public byte[] Data { get; set; }
public Dictionary<string, string> Values { get; set; }
public List<MyClass> Values2 { get; set; }
}

public class MyClass


{
public int SomethingElse { get; set; }
}
}

Bibliotecas principais do .NET


Esta seção contém os seguintes subtópicos:

Reflexão
Serialização
Abstração de tempo
Melhorias UTF8
Métodos para trabalhar com aleatoriedade
Tipos focados em desempenho
System.Numerics e System.Runtime.Intrinsics
Validação de dados
Métricas
Criptografia
Rede
Métodos ZipFile baseados em fluxo

Reflexão
Os ponteiros de função foram introduzidos no .NET 5. No entanto, o suporte
correspondente para reflexão não foi adicionado naquele momento. Ao usar typeof ou
reflexão em um ponteiro de função, por exemplo, typeof(delegate*<void>()) ou
FieldInfo.FieldType respectivamente, um IntPtr foi retornado. Do .NET 8 em diante, um

objeto System.Type é retornado. Esse tipo fornece acesso aos metadados do ponteiro
de função, incluindo as convenções de chamada, o tipo de retorno e os parâmetros.

7 Observação

Uma instância de ponteiro de função, que é um endereço físico para uma função,
continua a ser representada como um IntPtr. Somente o tipo de reflexão foi
alterado.

Atualmente, a nova funcionalidade é implementada apenas no runtime do CoreCLR e no


MetadataLoadContext.

Novas APIs foram adicionadas a System.Type, como IsFunctionPointer, e a


System.Reflection.PropertyInfo, System.Reflection.FieldInfo e
System.Reflection.ParameterInfo. O código a seguir mostra como usar algumas das
novas APIs para reflexão.

C#

using System;
using System.Reflection;

// Sample class that contains a function pointer field.


public unsafe class UClass
{
public delegate* unmanaged[Cdecl, SuppressGCTransition]<in int, void>
_fp;
}

internal class FunctionPointerReflection


{
public static void RunIt()
{
FieldInfo? fieldInfo = typeof(UClass).GetField(nameof(UClass._fp));

// Obtain the function pointer type from a field.


Type? fpType = fieldInfo?.FieldType;

// New methods to determine if a type is a function pointer.


Console.WriteLine(
$"IsFunctionPointer: {fpType?.IsFunctionPointer}");
Console.WriteLine(
$"IsUnmanagedFunctionPointer:
{fpType?.IsUnmanagedFunctionPointer}");

// New methods to obtain the return and parameter types.


Console.WriteLine($"Return type:
{fpType?.GetFunctionPointerReturnType()}");

if (fpType is not null)


{
foreach (Type parameterType in
fpType.GetFunctionPointerParameterTypes())
{
Console.WriteLine($"Parameter type: {parameterType}");
}
}
// Access to custom modifiers and calling conventions requires a
"modified type".
Type? modifiedType = fieldInfo?.GetModifiedFieldType();

// A modified type forwards most members to its underlying type.


Type? normalType = modifiedType?.UnderlyingSystemType;

if (modifiedType is not null)


{
// New method to obtain the calling conventions.
foreach (Type callConv in
modifiedType.GetFunctionPointerCallingConventions())
{
Console.WriteLine($"Calling convention: {callConv}");
}
}

// New method to obtain the custom modifiers.


Type[]? modifiers =
modifiedType?.GetFunctionPointerParameterTypes()
[0].GetRequiredCustomModifiers();

if (modifiers is not null)


{
foreach (Type modreq in modifiers)
{
Console.WriteLine($"Required modifier for first parameter:
{modreq}");
}
}
}
}

O exemplo anterior produz a seguinte saída:

Output

IsFunctionPointer: True
IsUnmanagedFunctionPointer: True
Return type: System.Void
Parameter type: System.Int32&
Calling convention:
System.Runtime.CompilerServices.CallConvSuppressGCTransition
Calling convention: System.Runtime.CompilerServices.CallConvCdecl
Required modifier for first parameter:
System.Runtime.InteropServices.InAttribute

Serialização
Muitas melhorias foram feitas na System.Text.Json funcionalidade de serialização e
desserialização no .NET 8. Por exemplo, você pode personalizar a manipulação de
membros que não estão no conteúdo JSON.

As seções a seguir descrevem outras melhorias de serialização:

Suporte interno para tipos adicionais


Gerador de origem
Hierarquias de interface
Políticas de nomenclatura
Propriedades somente leitura
Desabilitar o padrão baseado em reflexão
Novos métodos de API JsonNode
Membros não públicos
APIs de desserialização de streaming
Método de extensão WithAddedModifier
New JsonContent.Create overloads
Congelar uma instância de JsonSerializerOptions

Para obter mais informações gerais sobre a serialização JSON, confira Serialização e
desserialização JSON no .NET.

Suporte interno para tipos adicionais


O serializador tem suporte interno para os seguintes tipos adicionais.

Tipos numéricos Half, Int128 e UInt128.

C#

Console.WriteLine(JsonSerializer.Serialize(
[ Half.MaxValue, Int128.MaxValue, UInt128.MaxValue ]
));
//
[65500,170141183460469231731687303715884105727,340282366920938463463374
607431768211455]

Valores Memory<T> e ReadOnlyMemory<T>. Os valores byte são serializados


para cadeias de caracteres Base64 e outros tipos para matrizes JSON.

C#

JsonSerializer.Serialize<ReadOnlyMemory<byte>>(new byte[] { 1, 2, 3 });


// "AQID"
JsonSerializer.Serialize<Memory<int>>(new int[] { 1, 2, 3 }); //
[1,2,3]

Gerador de origem
O .NET 8 inclui aprimoramentos do gerador de origem System.Text.Json que visam
tornar a experiência AOT nativa em par com o serializador baseado em reflexão. Por
exemplo:

O gerador de origem agora dá suporte à serialização de tipos required e init


propriedades. Ambos já tinham suporte na serialização baseada em reflexão.

Formatação aprimorada do código gerado pela origem.

Paridade de recursos JsonSourceGenerationOptionsAttribute com


JsonSerializerOptions. Para obter mais informações, confira Especificar opções
(gerador de origens).

Diagnósticos adicionais (como SYSLIB1034 e SYSLIB1039).

Não inclui tipos de propriedades ignoradas ou inacessíveis.

Suporte para aninhamento declarações JsonSerializerContext em tipos


arbitrários.

Suporte para tipos gerados pelo compilador ou indesejáveis em cenários de


geração de origem fracamente tipados. Como os tipos gerados pelo compilador
não podem ser especificados explicitamente pelo gerador de origem, o
System.Text.Json agora executa a resolução ancestral mais próxima em tempo de
execução. Essa resolução determina o supertipo mais apropriado com o qual
serializar o valor.

Novo tipo JsonStringEnumConverter<TEnum> de conversor . Não há suporte para a


classe JsonStringEnumConverter existente no AOT nativo. Você pode anotar seus
tipos de enumeração da seguinte maneira:

C#

[JsonConverter(typeof(JsonStringEnumConverter<MyEnum>))]
public enum MyEnum { Value1, Value2, Value3 }

[JsonSerializable(typeof(MyEnum))]
public partial class MyContext : JsonSerializerContext { }
Para obter mais informações, consulte Serializar campos de enumeração como
cadeias de caracteres.

A nova JsonConverter.Type propriedade permite que você pesquise o tipo de uma


instância não genérica JsonConverter :

C#

Dictionary<Type, JsonConverter>
CreateDictionary(IEnumerable<JsonConverter> converters)
=> converters.Where(converter => converter.Type != null)
.ToDictionary(converter => converter.Type!);

A propriedade é anulável, pois retorna null para instâncias JsonConverterFactory


e typeof(T) para instâncias JsonConverter<T> .

Encadear geradores de origem

A classe JsonSerializerOptions inclui uma nova propriedade TypeInfoResolverChain que


complementa a propriedade TypeInfoResolver existente. Essas propriedades são usadas
na personalização de contrato para encadear geradores de origem. A adição da nova
propriedade significa que você não precisa especificar todos os componentes
encadeados em um site de chamada, eles podem ser adicionados após o fato. O
TypeInfoResolverChain também permite que você introspecte a cadeia ou remova
componentes dela. Para obter mais informações, confira Combinar geradores de
origem.

Além disso, JsonSerializerOptions.AddContext<TContext>() agora está obsoleto. Ela foi


substituída pelas propriedades TypeInfoResolver e TypeInfoResolverChain. Para obter
mais informações, confira SYSLIB0049.

Hierarquias de interface

O .NET 8 adiciona suporte para serialização de propriedades de hierarquias de interface.

O código a seguir mostra um exemplo em que as propriedades da interface


imediatamente implementada e da interface base são serializadas.

C#

public static void InterfaceHierarchies()


{
IDerived value = new DerivedImplement { Base = 0, Derived = 1 };
string json = JsonSerializer.Serialize(value);
Console.WriteLine(json); // {"Derived":1,"Base":0}
}

public interface IBase


{
public int Base { get; set; }
}

public interface IDerived : IBase


{
public int Derived { get; set; }
}

public class DerivedImplement : IDerived


{
public int Base { get; set; }
public int Derived { get; set; }
}

Políticas de nomenclatura
JsonNamingPolicy inclui novas políticas de nomenclatura para as conversões de nome
de propriedade snake_case (com um sublinhado) e kebab-case (com hífen). Use essas
políticas de modo semelhante à política JsonNamingPolicy.CamelCase:

C#

var options = new JsonSerializerOptions


{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower
};
JsonSerializer.Serialize(new { PropertyName = "value" }, options);
// { "property_name" : "value" }

Para obter mais informações, consulte Use uma política de nomenclatura interna.

Propriedades somente leitura

Agora você pode desserializar em campos ou propriedades somente leitura (ou seja,
aqueles que não têm um acessador set ).

Para aceitar esse suporte globalmente, defina uma nova opção,


PreferredObjectCreationHandling, como JsonObjectCreationHandling.Populate. Se a
compatibilidade for uma preocupação, você também poderá habilitar a funcionalidade
de forma mais granular colocando o atributo
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)] em tipos

específicos cujas propriedades devem ser preenchidas ou em propriedades individuais.

Por exemplo, considere o código a seguir que desserializa em um tipo CustomerInfo


que tem duas propriedades somente leitura.

C#

public static void ReadOnlyProperties()


{
CustomerInfo customer = JsonSerializer.Deserialize<CustomerInfo>("""
{ "Names":["John Doe"], "Company":{"Name":"Contoso"} }
""")!;

Console.WriteLine(JsonSerializer.Serialize(customer));
}

class CompanyInfo
{
public required string Name { get; set; }
public string? PhoneNumber { get; set; }
}

[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class CustomerInfo
{
// Both of these properties are read-only.
public List<string> Names { get; } = new();
public CompanyInfo Company { get; } = new()
{
Name = "N/A",
PhoneNumber = "N/A"
};
}

Antes do .NET 8, os valores de entrada eram ignorados e as propriedades Names e


Company mantinham seus valores padrão.

Saída

{"Names":[],"Company":{"Name":"N/A","PhoneNumber":"N/A"}}

Agora os valores de entrada são usados para preencher as propriedades somente leitura
durante a desserialização.

Saída

{"Names":["John Doe"],"Company":{"Name":"Contoso","PhoneNumber":"N/A"}}
Para obter mais informações sobre o comportamento de desserialização de popular,
consulte Preencher propriedades inicializadas.

Desabilitar o padrão baseado em reflexão

Agora você pode desabilitar usando o serializador baseado em reflexão por padrão.
Essa desabilitação é útil para evitar a raiz acidental de componentes de reflexão que
nem estão em uso, especialmente em aplicativos AOT cortados e nativos. Para
desabilitar a serialização padrão baseada em reflexão, exigindo que um argumento
JsonSerializerOptions seja passado para os métodos de serialização e desserialização
JsonSerializer, defina a propriedade JsonSerializerIsReflectionEnabledByDefault
MSBuild para false no seu arquivo do projeto.

Use a nova API IsReflectionEnabledByDefault para verificar o valor da alternância do


recurso. Se você for um autor de biblioteca com base no System.Text.Json, poderá
contar com a propriedade para configurar seus padrões sem criar componentes de
reflexão de raiz acidentalmente.

Para obter mais informações, consulte Desabilitar os padrões de reflexão.

Novos métodos de API JsonNode


Os tipos JsonNode e System.Text.Json.Nodes.JsonArray incluem os novos métodos a
seguir.

C#

public partial class JsonNode


{
// Creates a deep clone of the current node and all its descendants.
public JsonNode DeepClone();

// Returns true if the two nodes are equivalent JSON representations.


public static bool DeepEquals(JsonNode? node1, JsonNode? node2);

// Determines the JsonValueKind of the current node.


public JsonValueKind GetValueKind(JsonSerializerOptions options = null);

// If node is the value of a property in the parent


// object, returns its name.
// Throws InvalidOperationException otherwise.
public string GetPropertyName();

// If node is the element of a parent JsonArray,


// returns its index.
// Throws InvalidOperationException otherwise.
public int GetElementIndex();
// Replaces this instance with a new value,
// updating the parent object/array accordingly.
public void ReplaceWith<T>(T value);

// Asynchronously parses a stream as UTF-8 encoded data


// representing a single JSON value into a JsonNode.
public static Task<JsonNode?> ParseAsync(
Stream utf8Json,
JsonNodeOptions? nodeOptions = null,
JsonDocumentOptions documentOptions = default,
CancellationToken cancellationToken = default);
}

public partial class JsonArray


{
// Returns an IEnumerable<T> view of the current array.
public IEnumerable<T> GetValues<T>();
}

Membros não públicos


Você pode optar por membros não públicos no contrato de serialização para um
determinado tipo usando as anotações de atributo JsonIncludeAttribute e
JsonConstructorAttribute.

C#

public static void NonPublicMembers()


{
string json = JsonSerializer.Serialize(new MyPoco(42));
Console.WriteLine(json);
// {"X":42}

JsonSerializer.Deserialize<MyPoco>(json);
}

public class MyPoco


{
[JsonConstructor]
internal MyPoco(int x) => X = x;

[JsonInclude]
internal int X { get; }
}

Para obter mais informações, confira Usar tipos imutáveis e membros e acessadores não
públicos.
APIs de desserialização de streaming
O .NET 8 inclui novos IAsyncEnumerable<T> métodos de extensão de desserialização de
streaming, por exemplo GetFromJsonAsAsyncEnumerable. Existem métodos
semelhantes que retornam Task<TResult>, por exemplo,
HttpClientJsonExtensions.GetFromJsonAsync. Os novos métodos de extensão invocam
APIs de streaming e retornam IAsyncEnumerable<T>.

O código a seguir mostra como você pode usar os novos métodos de extensão.

C#

public async static void StreamingDeserialization()


{
const string RequestUri = "https://api.contoso.com/books";
using var client = new HttpClient();
IAsyncEnumerable<Book?> books =
client.GetFromJsonAsAsyncEnumerable<Book>(RequestUri);

await foreach (Book? book in books)


{
Console.WriteLine($"Read book '{book?.title}'");
}
}

public record Book(int id, string title, string author, int publishedYear);

Método de extensão WithAddedModifier


O novo método de extensão WithAddedModifier(IJsonTypeInfoResolver,
Action<JsonTypeInfo>) permite que você introduza facilmente modificações nos
contratos de serialização de instâncias IJsonTypeInfoResolver arbitrárias.

C#

var options = new JsonSerializerOptions


{
TypeInfoResolver = MyContext.Default
.WithAddedModifier(static typeInfo =>
{
foreach (JsonPropertyInfo prop in typeInfo.Properties)
{
prop.Name = prop.Name.ToUpperInvariant();
}
})
};
New JsonContent.Create overloads
Agora você pode criar JsonContent instâncias usando contratos trim-safe ou gerados
pela origem. Os novos métodos são:

JsonContent.Create(Object, JsonTypeInfo, MediaTypeHeaderValue)


JsonContent.Create<T>(T, JsonTypeInfo<T>, MediaTypeHeaderValue)

C#

var book = new Book(id: 42, "Title", "Author", publishedYear: 2023);


HttpContent content = JsonContent.Create(book, MyContext.Default.Book);

public record Book(int id, string title, string author, int publishedYear);

[JsonSerializable(typeof(Book))]
public partial class MyContext : JsonSerializerContext
{
}

Congelar uma instância de JsonSerializerOptions


Os novos métodos a seguir permitem controlar quando uma JsonSerializerOptions
instância é congelada:

JsonSerializerOptions.MakeReadOnly()

Essa sobrecarga foi projetada para ser trim-safe e, portanto, gerará uma exceção
nos casos em que a instância de opções não foi configurada com um resolvedor.

JsonSerializerOptions.MakeReadOnly(Boolean)

Se você passar true para essa sobrecarga, ela preencherá a instância de opções
com o resolvedor de reflexão padrão se houver uma ausente. Esse método é
marcado RequiresUnreferenceCode / RequiresDynamicCode e, portanto, é inadequado
para aplicativos AOT nativos.

A nova IsReadOnly propriedade permite verificar se a instância de opções está


congelada.

Abstração de tempo
A nova classe TimeProvider e a interface ITimer adicionam funcionalidade de abstração
de tempo, o que permite simular o tempo em cenários de teste. Além disso, você pode
usar a abstração de tempo para simular operações Task que dependem da progressão
de tempo usando Task.Delay e Task.WaitAsync. A abstração de tempo dá suporte às
seguintes operações de tempo essenciais:

Recuperar hora local e UTC


Obter um carimbo de data/hora para medir o desempenho
Criar um temporizador

O snippet de código a seguir mostra alguns exemplos de uso.

C#

// Get system time.


DateTimeOffset utcNow = TimeProvider.System.GetUtcNow();
DateTimeOffset localNow = TimeProvider.System.GetLocalNow();

TimerCallback callback = s => ((State)s!).Signal();

// Create a timer using the time provider.


ITimer timer = _timeProvider.CreateTimer(
callback, null, TimeSpan.Zero, Timeout.InfiniteTimeSpan);

// Measure a period using the system time provider.


long providerTimestamp1 = TimeProvider.System.GetTimestamp();
long providerTimestamp2 = TimeProvider.System.GetTimestamp();

TimeSpan period = _timeProvider.GetElapsedTime(providerTimestamp1,


providerTimestamp2);

C#

// Create a time provider that works with a


// time zone that's different than the local time zone.
private class ZonedTimeProvider(TimeZoneInfo zoneInfo) : TimeProvider()
{
private readonly TimeZoneInfo _zoneInfo = zoneInfo ??
TimeZoneInfo.Local;

public override TimeZoneInfo LocalTimeZone => _zoneInfo;

public static TimeProvider FromLocalTimeZone(TimeZoneInfo zoneInfo) =>


new ZonedTimeProvider(zoneInfo);
}

Melhorias UTF8
Se você quiser habilitar a gravação de uma representação semelhante a uma cadeia de
caracteres do seu tipo em um intervalo de destino, implemente a nova interface
IUtf8SpanFormattable em seu tipo. Essa nova interface está intimamente relacionada a
ISpanFormattable, mas tem como alvo UTF8 e Span<byte> , em vez de UTF16 e
Span<char> .

O IUtf8SpanFormattable foi implementado em todos os tipos primitivos (mais outros),


com exatamente a mesma lógica compartilhada, seja direcionando string , Span<char>
ou Span<byte> . Ele tem suporte completo para todos os formatos (incluindo o novo
especificador binário "B") e todas as culturas. Isso significa que você agora pode
formatar diretamente para UTF8 de Byte , Complex , Char , DateOnly , DateTime ,
DateTimeOffset , Decimal , Double , Guid , Half , IPAddress , IPNetwork , Int16 , Int32 ,
Int64 , Int128 , IntPtr , NFloat , SByte , Single , Rune , TimeOnly , TimeSpan , UInt16 ,

UInt32 , UInt64 , UInt128 , UIntPtr e Version .

Novos métodos Utf8.TryWrite fornecem um equivalente baseado em UTF8 para os


métodos existentes MemoryExtensions.TryWrite, que são baseados em UTF16. Você
pode usar a sintaxe de cadeia de caracteres interpolada para formatar uma expressão
complexa diretamente em um intervalo de bytes UTF8, por exemplo:

C#

static bool FormatHexVersion(


short major,
short minor,
short build,
short revision,
Span<byte> utf8Bytes,
out int bytesWritten) =>
Utf8.TryWrite(
utf8Bytes,
CultureInfo.InvariantCulture,
$"{major:X4}.{minor:X4}.{build:X4}.{revision:X4}",
out bytesWritten);

A implementação reconhece IUtf8SpanFormattable nos valores de formato e usa suas


implementações para gravar suas representações UTF8 diretamente no intervalo de
destino.

A implementação também utiliza o novo método


Encoding.TryGetBytes(ReadOnlySpan<Char>, Span<Byte>, Int32), que, juntamente com
seu equivalente Encoding.TryGetChars(ReadOnlySpan<Byte>, Span<Char>, Int32), dá
suporte à codificação e à decodificação em um intervalo de destino. Se o intervalo não
for longo o suficiente para manter o estado resultante, os métodos retornarão false
em vez de lançar uma exceção.

Métodos para trabalhar com aleatoriedade


Os tipos System.Random e System.Security.Cryptography.RandomNumberGenerator
introduzem dois novos métodos para trabalhar com aleatoriedade.

GetItems<T>()

Os novos métodos System.Random.GetItems e


System.Security.Cryptography.RandomNumberGenerator.GetItems permitem que você
escolha aleatoriamente um número especificado de itens de um conjunto de entrada. O
exemplo a seguir mostra como usar System.Random.GetItems<T>() (na instância
fornecida pela propriedade Random.Shared) para inserir aleatoriamente 31 itens em
uma matriz. Este exemplo pode ser usado em um jogo do tipo "Genius" em que os
jogadores precisam se lembrar de uma sequência de botões coloridos.

C#

private static ReadOnlySpan<Button> s_allButtons = new[]


{
Button.Red,
Button.Green,
Button.Blue,
Button.Yellow,
};

// ...

Button[] thisRound = Random.Shared.GetItems(s_allButtons, 31);


// Rest of game goes here ...

Shuffle<T>()
Os novos métodos Random.Shuffle e RandomNumberGenerator.Shuffle<T>(Span<T>)
permitem que você randomize a ordem de um intervalo. Esses métodos são úteis para
reduzir o desvio de treinamento no machine learning (portanto, a primeira etapa nem
sempre é o treinamento e a última é sempre um teste).

C#

YourType[] trainingData = LoadTrainingData();


Random.Shared.Shuffle(trainingData);

IDataView sourceData = mlContext.Data.LoadFromEnumerable(trainingData);

DataOperationsCatalog.TrainTestData split =
mlContext.Data.TrainTestSplit(sourceData);
model = chain.Fit(split.TrainSet);
IDataView predictions = model.Transform(split.TestSet);
// ...

Tipos focados em desempenho


O .NET 8 apresenta vários novos tipos destinados a aprimorar o desempenho do
aplicativo.

O novo namespace System.Collections.Frozen inclui os tipos de coleção


FrozenDictionary<TKey,TValue> e FrozenSet<T>. Esses tipos não permitem
alterações em chaves e valores depois que uma coleção é criada. Esse requisito
permite operações de leitura mais rápidas (por exemplo, TryGetValue() ). Esses
tipos são particularmente úteis para coleções que são preenchidas no primeiro uso
e persistidas durante a execução de um serviço de longa duração, por exemplo:

C#

private static readonly FrozenDictionary<string, bool>


s_configurationData =
LoadConfigurationData().ToFrozenDictionary(optimizeForReads: true);

// ...
if (s_configurationData.TryGetValue(key, out bool setting) && setting)
{
Process();
}

Métodos como MemoryExtensions.IndexOfAny procuram a primeira ocorrência de


qualquer valor na coleção passada. O novo tipo System.Buffers.SearchValues<T>
foi projetado para ser passado para esses métodos. De maneira correspondente, o
.NET 8 adiciona novas sobrecargas de métodos como
MemoryExtensions.IndexOfAny que aceitam uma instância do novo tipo. Quando
você cria uma instância do SearchValues<T>, todos os dados necessários para
otimizar pesquisas seguintes são derivados naquele momento, significando que o
trabalho é feito antecipadamente.

O novo tipo System.Text.CompositeFormat é útil para otimizar cadeias de


caracteres de formato que não são conhecidas no tempo de compilação (por
exemplo, se a cadeia de caracteres de formato for carregada em um arquivo de
recurso). Um pouco de tempo extra é gasto antecipadamente para ações como
analisar a cadeia de caracteres, mas isso evita que o trabalho precise ser feito em
cada uso.

C#
private static readonly CompositeFormat s_rangeMessage =
CompositeFormat.Parse(LoadRangeMessageResource());

// ...
static string GetMessage(int min, int max) =>
string.Format(CultureInfo.InvariantCulture, s_rangeMessage, min,
max);

Novos tipos System.IO.Hashing.XxHash3 e System.IO.Hashing.XxHash128 fornecem


implementações dos algoritmos de hash XXH3 e XXH128 rápidos.

System.Numerics e System.Runtime.Intrinsics
Esta seção aborda melhorias nos namespaces System.Numerics e
System.Runtime.Intrinsics.

Vector256<T>, Matrix3x2 e Matrix4x4 aprimoraram a aceleração de hardware no


.NET 8. Por exemplo, Vector256<T> foi reimplementado para ter operações 2x
Vector128<T> internas, sempre que possível. Isso permite a aceleração parcial de

algumas funções quando Vector128.IsHardwareAccelerated == true , mas


Vector256.IsHardwareAccelerated == false , por exemplo, no Arm64.

Os intrínsecos de hardware agora são anotados com o atributo ConstExpected .


Isso garante que os usuários estejam cientes quando o hardware subjacente
espera uma constante e, portanto, quando um valor não constante possa
prejudicar inesperadamente o desempenho.
A API Lerp(TSelf, TSelf, TSelf) Lerp foi adicionada a IFloatingPointIeee754<TSelf> e,
portanto, float (Single), double (Double) e Half. Essa API permite que uma
interpolação linear entre dois valores seja executada de modo eficiente e correto.

Vector512 e AVX-512

O .NET Core 3.0 expandiu o suporte ao SIMD para incluir as APIs intrínsecas de
hardware específicas da plataforma para x86/x64. O .NET 5 adicionou suporte para
Arm64 e .NET 7 adicionados aos intrínsecos de hardware multiplataforma. O .NET 8
oferece suporte a SIMD apresentando Vector512<T> e dando suporte a instruções do
Intel Advanced Vector Extensions 512 (AVX-512 ).

Especificamente, o .NET 8 inclui suporte para os seguintes principais recursos do AVX-


512:

Operações de vetor de 512 bits


Mais 16 registros SIMD
Instruções adicionais disponíveis para vetores de 128 bits, 256 bits e 512 bits

Se você tiver hardware que dê suporte à funcionalidade, o


Vector512.IsHardwareAccelerated agora relatará true .

O .NET 8 também adiciona várias classes específicas da plataforma no namespace


System.Runtime.Intrinsics.X86:

Avx512F (fundamental)
Avx512BW (byte e palavra)
Avx512CD (detecção de conflitos)
Avx512DQ (palavra dupla e palavra quádrupla)
Avx512Vbmi (instruções de manipulação de bytes vetoriais)

Essas classes seguem a mesma forma geral que outras ISAs (arquiteturas de conjunto de
instruções) na medida em que expõem uma propriedade IsSupported e uma classe
aninhada Avx512F.X64 para obter instruções disponíveis apenas para processos de 64
bits. Além disso, cada classe tem uma classe aninhada Avx512F.VL que expõe as
extensões Avx512VL (comprimento do vetor) para o conjunto de instruções
correspondente.

Mesmo que você não use instruções específicas para Vector512 ou Avx512F em seu
código, provavelmente ainda se beneficiará do novo suporte do AVX-512. O JIT pode
aproveitar os registros e instruções adicionais implicitamente ao usar Vector128<T> ou
Vector256<T>. A biblioteca de classes base usa esses intrínsecos de hardware
internamente na maioria das operações expostas por Span<T> e ReadOnlySpan<T> em
muitas das APIs matemáticas expostas para os tipos primitivos.

Validação de dados
O namespace System.ComponentModel.DataAnnotations inclui novos atributos de
validação de dados destinados a cenários de validação em serviços nativos de nuvem.
Embora os validadores pré-existentes DataAnnotations sejam voltados para a validação
típica de entrada de dados da interface do usuário, como campos em um formulário, os
novos atributos são projetados para validar dados de entrada não usuário, como opções
de configuração. Além dos novos atributos, novas propriedades foram adicionadas aos
tipos RangeAttribute e RequiredAttribute.

ノ Expandir a tabela
Nova API Descrição

RangeAttribute.MinimumIsExclusive Especifica se os limites


RangeAttribute.MaximumIsExclusive estão incluídos no intervalo
permitido.

System.ComponentModel.DataAnnotations.LengthAttribute Especifica os limites inferior


e superior para cadeias de
caracteres ou coleções. Por
exemplo, [Length(10, 20)]
requer pelo menos 10
elementos e no máximo 20
elementos em uma coleção.

System.ComponentModel.DataAnnotations.Base64StringAttribute Valida se uma cadeia de


caracteres é uma
representação base64
válida.

System.ComponentModel.DataAnnotations.AllowedValuesAttribute Especifique listas de


System.ComponentModel.DataAnnotations.DeniedValuesAttribute permissões e listas de
negação, respectivamente.
Por exemplo,
[AllowedValues("apple",
"banana", "mango")] .

Métricas
Novas APIs permitem anexar marcas de par chave-valor a objetos Meter e Instrument
ao criá-las. Os agregadores de medidas de métrica publicadas podem usar as marcas
para diferenciar os valores agregados.

C#

var options = new MeterOptions("name")


{
Version = "version",
// Attach these tags to the created meter.
Tags = new TagList()
{
{ "MeterKey1", "MeterValue1" },
{ "MeterKey2", "MeterValue2" }
}
};

Meter meter = meterFactory!.Create(options);

Counter<int> counterInstrument = meter.CreateCounter<int>(


"counter", null, null, new TagList() { { "counterKey1", "counterValue1"
} }
);
counterInstrument.Add(1);

As novas APIs incluem o seguinte:

MeterOptions
Meter(MeterOptions)
CreateCounter<T>(String, String, String,
IEnumerable<KeyValuePair<String,Object>>)

Criptografia
O .NET 8 adiciona suporte para os primitivos de hash SHA-3. (No momento, SHA-3 tem
suporte do Linux com OpenSSL 1.1.1 ou posterior e Windows 11 Build 25324 ou
posterior.) APIs em que SHA-2 está disponível agora oferecem um complemento SHA-3.
Isso inclui SHA3_256 , SHA3_384 e SHA3_512 para hash; HMACSHA3_256 , HMACSHA3_384 e
HMACSHA3_512 para HMAC; HashAlgorithmName.SHA3_256 , HashAlgorithmName.SHA3_384 e
HashAlgorithmName.SHA3_512 para hash em que o algoritmo é configurável; e

RSAEncryptionPadding.OaepSHA3_256 , RSAEncryptionPadding.OaepSHA3_384 e

RSAEncryptionPadding.OaepSHA3_512 para criptografia OAEP RSA.

O exemplo a seguir mostra como usar as APIs, incluindo a propriedade


SHA3_256.IsSupported para determinar se a plataforma dá suporte ao SHA-3.

C#

// Hashing example
if (SHA3_256.IsSupported)
{
byte[] hash = SHA3_256.HashData(dataToHash);
}
else
{
// ...
}

// Signing example
if (SHA3_256.IsSupported)
{
using ECDsa ec = ECDsa.Create(ECCurve.NamedCurves.nistP256);
byte[] signature = ec.SignData(dataToBeSigned,
HashAlgorithmName.SHA3_256);
}
else
{
// ...
}

No momento, o suporte ao SHA-3 destina-se a dar suporte a primitivos criptográficos.


Construções e protocolos de nível superior não devem dar inicialmente suporte total ao
SHA-3. Esses protocolos incluem certificados X.509, SignedXml e COSE.

Rede

Suporte para proxy HTTPS


Até agora, os tipos de proxy que HttpClient suportavam todos permitiam que um "man-
in-the-middle" visse a qual site o cliente está se conectando, mesmo para URIs HTTPS.
HttpClient agora dá suporte ao proxy HTTPS, que cria um canal criptografado entre o
cliente e o proxy para que todas as solicitações possam ser tratadas com privacidade
total.

Para habilitar o proxy HTTPS, defina a all_proxy variável de ambiente ou use a classe
WebProxy para controlar o proxy programaticamente.

Unix: export all_proxy=https://x.x.x.x:3218 Windows: set


all_proxy=https://x.x.x.x:3218

Você também pode usar a classe WebProxy para controlar o proxy programaticamente.

Métodos ZipFile baseados em fluxo


O .NET 8 inclui novas sobrecargas de ZipFile.CreateFromDirectory que permitem coletar
todos os arquivos incluídos em um diretório e compactá-los e, em seguida, armazenar o
arquivo zip resultante no fluxo fornecido. Da mesma forma, novas sobrecargas de
ZipFile.ExtractToDirectory permitem que você forneça um fluxo que contém um arquivo
compactado e extraia seu conteúdo para o sistema de arquivos. Estas são as novas
sobrecargas:

C#

namespace System.IO.Compression;

public static partial class ZipFile


{
public static void CreateFromDirectory(
string sourceDirectoryName, Stream destination);

public static void CreateFromDirectory(


string sourceDirectoryName,
Stream destination,
CompressionLevel compressionLevel,
bool includeBaseDirectory);

public static void CreateFromDirectory(


string sourceDirectoryName,
Stream destination,
CompressionLevel compressionLevel,
bool includeBaseDirectory,
Encoding? entryNameEncoding);

public static void ExtractToDirectory(


Stream source, string destinationDirectoryName) { }

public static void ExtractToDirectory(


Stream source, string destinationDirectoryName, bool overwriteFiles)
{ }

public static void ExtractToDirectory(


Stream source, string destinationDirectoryName, Encoding?
entryNameEncoding) { }

public static void ExtractToDirectory(


Stream source, string destinationDirectoryName, Encoding?
entryNameEncoding, bool overwriteFiles) { }
}

Essas novas APIs podem ser úteis quando o espaço em disco é restrito, pois evitam ter
que usar o disco como uma etapa intermediária.

Bibliotecas de extensão
Esta seção contém os seguintes subtópicos:

Validação de opções
Construtores LoggerMessageAttribute
Métricas de extensões
Serviços de ciclo de vida hospedados
Serviços de DI com chave
System.Numerics.Tensors.TensorPrimitives

Serviços de DI com chave


Os serviços de injeção de dependência (DI) com chave fornecem um meio de registrar e
recuperar serviços de DI usando chaves. Ao usar chaves, você pode definir o escopo de
como registrar e consumir serviços. Estas são algumas das novas APIs:
A interface IKeyedServiceProvider.
O atributo ServiceKeyAttribute, que pode ser usado para injetar a chave usada
para registro/resolução no construtor.
O atributo FromKeyedServicesAttribute, que pode ser usado em parâmetros de
construtor de serviço para especificar qual serviço com chave usar.
Vários novos métodos de extensão para IServiceCollection dar suporte a serviços
com chave, por exemplo, ServiceCollectionServiceExtensions.AddKeyedScoped.
A implementação ServiceProvider de IKeyedServiceProvider.

O exemplo a seguir mostra como usar os serviços de DI com chave.

C#

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);


builder.Services.AddSingleton<BigCacheConsumer>();
builder.Services.AddSingleton<SmallCacheConsumer>();
builder.Services.AddKeyedSingleton<ICache, BigCache>("big");
builder.Services.AddKeyedSingleton<ICache, SmallCache>("small");
WebApplication app = builder.Build();
app.MapGet("/big", (BigCacheConsumer data) => data.GetData());
app.MapGet("/small", (SmallCacheConsumer data) => data.GetData());
app.MapGet("/big-cache", ([FromKeyedServices("big")] ICache cache) =>
cache.Get("data"));
app.MapGet("/small-cache", (HttpContext httpContext) =>
httpContext.RequestServices.GetRequiredKeyedService<ICache>
("small").Get("data"));
app.Run();

class BigCacheConsumer([FromKeyedServices("big")] ICache cache)


{
public object? GetData() => cache.Get("data");
}

class SmallCacheConsumer(IServiceProvider serviceProvider)


{
public object? GetData() =>
serviceProvider.GetRequiredKeyedService<ICache>("small").Get("data");
}

public interface ICache


{
object Get(string key);
}

public class BigCache : ICache


{
public object Get(string key) => $"Resolving {key} from big cache.";
}

public class SmallCache : ICache


{
public object Get(string key) => $"Resolving {key} from small cache.";
}

Para obter mais informações, confira dotnet/runtime#64427 .

Serviços de ciclo de vida hospedados


Os serviços hospedados agora têm mais opções para execução durante o ciclo de vida
do aplicativo. IHostedService forneceu StartAsync e StopAsync , e agora
IHostedLifecycleService fornece estes métodos adicionais:

StartingAsync(CancellationToken)
StartedAsync(CancellationToken)
StoppingAsync(CancellationToken)
StoppedAsync(CancellationToken)

Esses métodos são executados antes e depois dos pontos existentes, respectivamente.

O exemplo a seguir mostra como usar as novas APIs.

C#

using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

internal class HostedLifecycleServices


{
public async static void RunIt()
{
IHostBuilder hostBuilder = new HostBuilder();
hostBuilder.ConfigureServices(services =>
{
services.AddHostedService<MyService>();
});

using (IHost host = hostBuilder.Build())


{
await host.StartAsync();
}
}

public class MyService : IHostedLifecycleService


{
public Task StartingAsync(CancellationToken cancellationToken) => /*
add logic here */ Task.CompletedTask;
public Task StartAsync(CancellationToken cancellationToken) => /*
add logic here */ Task.CompletedTask;
public Task StartedAsync(CancellationToken cancellationToken) => /*
add logic here */ Task.CompletedTask;
public Task StopAsync(CancellationToken cancellationToken) => /* add
logic here */ Task.CompletedTask;
public Task StoppedAsync(CancellationToken cancellationToken) => /*
add logic here */ Task.CompletedTask;
public Task StoppingAsync(CancellationToken cancellationToken) => /*
add logic here */ Task.CompletedTask;
}
}

Para obter mais informações, confira dotnet/runtime#86511 .

Validação de opções

Gerador de origem

Para reduzir a sobrecarga de inicialização e melhorar o conjunto de recursos de


validação, introduzimos um gerador de código-fonte que implementa a lógica de
validação. O código a seguir mostra modelos de exemplo e classes de validador.

C#

public class FirstModelNoNamespace


{
[Required]
[MinLength(5)]
public string P1 { get; set; } = string.Empty;

[Microsoft.Extensions.Options.ValidateObjectMembers(
typeof(SecondValidatorNoNamespace))]
public SecondModelNoNamespace? P2 { get; set; }
}

public class SecondModelNoNamespace


{
[Required]
[MinLength(5)]
public string P4 { get; set; } = string.Empty;
}

[OptionsValidator]
public partial class FirstValidatorNoNamespace
: IValidateOptions<FirstModelNoNamespace>
{
}

[OptionsValidator]
public partial class SecondValidatorNoNamespace
: IValidateOptions<SecondModelNoNamespace>
{
}

Se o aplicativo usar injeção de dependência, você poderá injetar a validação conforme


mostrado no código de exemplo a seguir.

C#

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);


builder.Services.AddControllersWithViews();
builder.Services.Configure<FirstModelNoNamespace>(
builder.Configuration.GetSection("some string"));

builder.Services.AddSingleton<
IValidateOptions<FirstModelNoNamespace>, FirstValidatorNoNamespace>();
builder.Services.AddSingleton<
IValidateOptions<SecondModelNoNamespace>, SecondValidatorNoNamespace>();

Tipo ValidateOptionsResultBuilder

O .NET 8 apresenta o tipo ValidateOptionsResultBuilder para facilitar a criação de um


objeto ValidateOptionsResult. É importante ressaltar que esse construtor permite o
acúmulo de vários erros. Anteriormente, a criação do objeto ValidateOptionsResult
necessário para implementar IValidateOptions<TOptions>.Validate(String, TOptions) era
difícil e, às vezes, resultava em erros de validação em camadas. Se ocorressem vários
erros, o processo de validação geralmente parava no primeiro erro.

O snippet de código a seguir mostra um exemplo de uso de


ValidateOptionsResultBuilder.

C#

ValidateOptionsResultBuilder builder = new();


builder.AddError("Error: invalid operation code");
builder.AddResult(ValidateOptionsResult.Fail("Invalid request parameters"));
builder.AddError("Malformed link", "Url");

// Build ValidateOptionsResult object has accumulating multiple errors.


ValidateOptionsResult result = builder.Build();

// Reset the builder to allow using it in new validation operation.


builder.Clear();

Construtores LoggerMessageAttribute
LoggerMessageAttribute agora oferece sobrecargas de construtor adicionais.
Anteriormente, era necessário escolher o construtor sem parâmetros ou o construtor
que exigia todos os parâmetros (ID do evento, nível de log e mensagem). As novas
sobrecargas oferecem maior flexibilidade na especificação dos parâmetros necessários
com código reduzido. Se você não fornecer uma ID de evento, o sistema gerará uma
automaticamente.

C#

public LoggerMessageAttribute(LogLevel level, string message);


public LoggerMessageAttribute(LogLevel level);
public LoggerMessageAttribute(string message);

Métricas de extensões

Interface IMeterFactory

Você pode registrar a nova interface IMeterFactory em contêineres de DI (injeção de


dependência) e usá-la para criar objetos Meter de maneira isolada.

Registre o IMeterFactory no contêiner de DI usando a implementação padrão da fábrica


de medidores:

C#

// 'services' is the DI IServiceCollection.


services.AddMetrics();

Em seguida, os consumidores podem obter a fábrica de medidores e usá-la para criar


um objeto Meter.

C#

IMeterFactory meterFactory =
serviceProvider.GetRequiredService<IMeterFactory>();

MeterOptions options = new MeterOptions("MeterName")


{
Version = "version",
};

Meter meter = meterFactory.Create(options);


Classe MetricCollector<T>
A nova classe MetricCollector<T> permite registrar medidas de métrica junto com
carimbos de data/hora. Além disso, a classe oferece a flexibilidade de usar um provedor
de tempo de sua escolha para uma geração precisa do carimbo de data/hora.

C#

const string CounterName = "MyCounter";


DateTimeOffset now = DateTimeOffset.Now;

var timeProvider = new FakeTimeProvider(now);


using var meter = new Meter(Guid.NewGuid().ToString());
Counter<long> counter = meter.CreateCounter<long>(CounterName);
using var collector = new MetricCollector<long>(counter, timeProvider);

Assert.IsNull(collector.LastMeasurement);

counter.Add(3);

// Verify the update was recorded.


Assert.AreEqual(counter, collector.Instrument);
Assert.IsNotNull(collector.LastMeasurement);

Assert.AreSame(collector.GetMeasurementSnapshot().Last(),
collector.LastMeasurement);
Assert.AreEqual(3, collector.LastMeasurement.Value);
Assert.AreEqual(now, collector.LastMeasurement.Timestamp);

System.Numerics.Tensors.TensorPrimitives
O pacote NuGet System.Numerics.Tensors atualizado inclui APIs no novo namespace
TensorPrimitives que adicionam suporte para operações de tensor. Os primitivos
tensoriais otimizam cargas de trabalho com uso intensivo de dados, como as de IA e
aprendizado de máquina.

Cargas de trabalho de IA, como pesquisa semântica e RAG (geração aumentada por
recuperação), estendem os recursos de linguagem natural de modelos de linguagem
grandes, como ChatGPT, aumentando prompts com dados relevantes. Para essas cargas
de trabalho, as operações em vetores, como similaridade de cosseno para encontrar os
dados mais relevantes para responder a uma pergunta, são cruciais. O pacote
System.Numerics.Tensors.TensorPrimitives fornece APIs para operações vetoriais, o que
significa que você não precisa usar uma dependência externa ou escrever sua própria
implementação.

Esse pacote substitui o pacote System.Numerics.Tensors .


Para obter mais informações, confira a postagem no blog Anúncio do .NET 8 RC 2 .

Confira também
Novidades do .NET 8

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Novidades do SDK e das ferramentas do
.NET 8
Artigo • 09/02/2024

Este artigo descreve os novos recursos do SDK do .NET e as ferramentas do .NET 8.

.
Esta seção contém os seguintes subtópicos:

Avaliação de projetos baseada em CLI


Saída de build do terminal
Caminhos de saída simplificados
Comando 'dotnet workload clean'
Ativos 'dotnet publish' e 'dotnet pack'
Mecanismo de modelo
Source Link
SDK do build de origem

Avaliação de projeto baseados em CLI


O MSBuild inclui um novo recurso que facilita a incorporação de dados do MSBuild em
seus scripts ou ferramentas. Os novos sinalizadores a seguir estão disponíveis para
comandos da CLI, como o dotnet publish, para obter dados para uso em pipelines de CI
e em outros lugares.

ノ Expandir a tabela

Sinalizador Descrição

--getProperty:<PROPERTYNAME> Recupera a propriedade MSBuild com o nome especificado.

--getItem:<ITEMTYPE> Recupera itens do MSBuild do tipo especificado.

--getTargetResults:<TARGETNAME> Recupera as saídas da execução do destino especificado.

Os valores são gravados na saída padrão. Valores múltiplos ou complexos têm saída
JSON, conforme mostrado nos exemplos a seguir.

CLI do .NET
>dotnet publish --getProperty:OutputPath
bin\Release\net8.0\

CLI do .NET

>dotnet publish -p PublishProfile=DefaultContainer --


getProperty:GeneratedContainerDigest --
getProperty:GeneratedContainerConfiguration
{
"Properties": {
"GeneratedContainerDigest":
"sha256:ef880a503bbabcb84bbb6a1aa9b41b36dc1ba08352e7cd91c0993646675174c4",
"GeneratedContainerConfiguration": "{\u0022config\u0022:
{\u0022ExposedPorts\u0022:{\u00228080/tcp\u0022:{}},\u0022Labels\u0022...}}"
}
}

CLI do .NET

>dotnet publish -p PublishProfile=DefaultContainer --


getItem:ContainerImageTags
{
"Items": {
"ContainerImageTags": [
{
"Identity": "latest",
...
]
}
}

Saída de build do terminal


O dotnet build tem uma nova opção para produzir uma saída de build mais
modernizada. Essa saída do agente de terminal agrupa erros com o projeto de onde eles
vieram, diferencia melhor as estruturas de destino para projetos de vários destinos e
fornece informações em tempo real sobre o que o build está fazendo. Para aceitar a
nova saída, use a opção --tl . Para obter mais informações sobre essa opção, confira
opções de build dotnet.

Caminhos de saída simplificados


O .NET 8 apresenta uma opção para simplificar o caminho de saída e a estrutura de
pastas para saídas de build. Anteriormente, aplicativos .NET produziam um conjunto
profundo e complexo de caminhos de saída para diferentes artefatos de build. A nova
estrutura de caminho de saída simplificada reúne todas as saídas de build em um local
comum, o que facilita a previsão pelas ferramentas.

Para obter mais informações, confira Layout de saída de artefatos.

Comando dotnet workload clean


O .NET 8 apresenta um novo comando para limpar pacotes de carga de trabalho que
podem ser deixados por várias atualizações do SDK do .NET ou do Visual Studio. Se
você encontrar problemas ao gerenciar cargas de trabalho, considere usar workload
clean para restaurar com segurança para um estado conhecido antes de tentar

novamente. O comando tem dois modos:

dotnet workload clean

Executa a coleta de lixo de carga de trabalho para cargas de trabalho baseadas


em arquivo ou MSI, o que limpa pacotes órfãos. Os pacotes órfãos são de versões
desinstaladas do SDK do .NET ou pacotes em que os registros de instalação não
existem mais.

Se o Visual Studio estiver instalado, o comando também listará todas as cargas de


trabalho que você deve limpar manualmente usando o Visual Studio.

dotnet workload clean --all

Esse modo é mais agressivo e limpa todos os pacotes no computador do tipo de


instalação de carga de trabalho atual do SDK (que não é do Visual Studio). Ele
também remove todos os registros de instalação de carga de trabalho para a faixa
de recursos do SDK do .NET em execução e abaixo.

Ativos dotnet publish e dotnet pack


Como os comandos dotnet publish e dotnet pack destinam-se a produzir ativos de
produção, eles agora produzem ativos Release por padrão.

A saída a seguir mostra o comportamento diferente entre dotnet build e dotnet


publish , e como você pode revertê-lo para ativos Debug de publicação definindo a

propriedade PublishRelease como false .

Console
/app# dotnet new console
/app# dotnet build
app -> /app/bin/Debug/net8.0/app.dll
/app# dotnet publish
app -> /app/bin/Release/net8.0/app.dll
app -> /app/bin/Release/net8.0/publish/
/app# dotnet publish -p:PublishRelease=false
app -> /app/bin/Debug/net8.0/app.dll
app -> /app/bin/Debug/net8.0/publish/

Para obter mais informações, confira 'dotnet pack' usa configuração de versão e 'dotnet
publish' usa configuração de versão.

Auditoria de segurança do dotnet restore


A partir do .NET 8, você pode optar por verificações de segurança para vulnerabilidades
conhecidas quando os pacotes de dependência são restaurados. Essa auditoria produz
um relatório de vulnerabilidades de segurança com o nome do pacote afetado, a
gravidade da vulnerabilidade e um link para o aviso para obter mais detalhes. Quando
você executar dotnet add ou dotnet restore , os avisos NU1901-NU1904 serão exibidos
para quaisquer vulnerabilidades encontradas. Para obter mais informações, confira
Auditar vulnerabilidades de segurança.

Mecanismo de modelo
O mecanismo de modelo fornece uma experiência mais segura no .NET 8 integrando
alguns dos recursos relacionados à segurança do NuGet. As melhorias incluem:

Evite baixar pacotes de feeds http:// por padrão. Por exemplo, o comando a
seguir não instalará o pacote de modelo porque a URL de origem não usa HTTPS.

dotnet new install console --add-source

"http://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-

public/nuget/v3/index.json"

É possível substituir essa limitação usando-se o sinalizador --force .

Para dotnet new , dotnet new install e dotnet new update , verifique se há
vulnerabilidades conhecidas no pacote de modelo. Se forem encontradas
vulnerabilidades e você quiser continuar, use o sinalizador --force .

Para dotnet new , forneça informações sobre o proprietário do pacote de modelo.


A propriedade é verificada pelo portal do NuGet e pode ser considerada uma
característica confiável.

Para dotnet search e dotnet uninstall , indique se um modelo está instalado de


um pacote "confiável", ou seja, ele usa um prefixo reservado.

Source Link
O Source Link agora está incluído no SDK do .NET. A meta é que, agrupando o Source
Link no SDK, em vez de exigir um <PackageReference> separado para o pacote, mais
pacotes incluirão essas informações por padrão. Essas informações melhorarão a
experiência do IDE para desenvolvedores.

7 Observação

Como efeito colateral dessa alteração, as informações de confirmação são incluídas


no valor InformationalVersion de bibliotecas e aplicativos criados, mesmo aqueles
destinados ao .NET 7 ou a uma versão anterior. Para obter mais informações,
consulte Source Link incluído no SDK do .NET.

SDK do build de origem


O SDK da distribuição do Linux (build de origem) agora tem a capacidade de criar
aplicativos autossuficientes usando os pacotes de runtime de build de origem. O pacote
de runtime específico da distribuição é agrupado com o SDK do build de origem.
Durante a implantação autocontida, esse pacote de runtime empacotado será
referenciado, habilitando assim o recurso para os usuários.

Suporte a AOT nativo


A opção de publicar como AOT nativo foi introduzida pela primeira vez no .NET 7. A
publicação de um aplicativo com AOT nativo cria uma versão totalmente independente
do seu aplicativo que não precisa de um runtime, já que tudo está incluso em um
arquivo. O .NET 8 traz as seguintes melhorias para a publicação nativa de AOT:

Adiciona suporte para as arquiteturas x64 e Arm64 no macOS.

Reduz os tamanhos de aplicativos AOT nativos no Linux em até 50%. A tabela a


seguir mostra o tamanho de um aplicativo "Olá, Mundo" publicado com AOT
nativo que inclui todo o runtime do .NET no .NET 7 em relação ao .NET 8:
ノ Expandir a tabela

Sistema operacional .NET 7 .NET 8

Linux x64 (com -p:StripSymbols=true ) 3,76 MB 1,84 MB

Windows x64 2,85 MB 1,77 MB

Permite especificar uma preferência de otimização: tamanho ou velocidade. Por


padrão, o compilador escolhe gerar código rápido estando atento ao tamanho do
aplicativo. No entanto, você pode usar a propriedade MSBuild
<OptimizationPreference> para otimizar especificamente para um ou outro. Para

obter mais informações, confira Otimizar implantações do AOT.

Modelo de aplicativo de console


O modelo de aplicativo de console padrão agora inclui suporte para AOT pronto para
uso. Para criar um projeto configurado para compilação AOT, basta executar dotnet new
console --aot . A configuração do projeto adicionada por --aot tem três efeitos:

Gera um executável autônomo nativo com AOT nativo quando você publica o
projeto, por exemplo, com o dotnet publish ou Visual Studio.
Habilita analisadores de compatibilidade para corte, AOT e arquivo único. Esses
analisadores alertam você para partes potencialmente problemáticas do seu
projeto (se houver alguma).
Habilita a emulação de tempo de depuração do AOT para que, quando você
depurar seu projeto sem compilação AOT, obtenha uma experiência semelhante à
AOT. Por exemplo, se você usar System.Reflection.Emit em um pacote NuGet que
não foi anotado para AOT (e, portanto, foi perdido pelo analisador de
compatibilidade), a emulação significa que você não terá surpresas ao tentar
publicar o projeto com a AOT.

Focar em plataformas semelhantes a iOS com AOT nativo


O .NET 8 inicia o trabalho para habilitar o suporte do AOT nativo para plataformas
semelhantes a iOS. Agora você pode compilar e executar aplicativos .NET iOS e .NET
MAUI com AOT nativo nas seguintes plataformas:

ios
iossimulator

maccatalyst

tvos
tvossimulator

Testes preliminares mostram que o tamanho do aplicativo em disco diminui cerca de


35% para os aplicativos .NET iOS que usam o AOT nativo em vez do Mono. O tamanho
do aplicativo em disco para os aplicativos .NET MAUI iOS diminui em até 50%. Além
disso, o tempo de inicialização também é mais rápido. Os aplicativos .NET iOS têm um
tempo de inicialização cerca de 28% mais rápido, enquanto os aplicativos .NET MAUI
iOS têm um desempenho de inicialização cerca de 50% melhor em comparação com o
Mono. O suporte ao .NET 8 é experimental e apenas a primeira etapa para o recurso
como um todo. Para obter mais informações, confira a postagem no blog
Aprimoramentos de desempenho do .NET 8 no .NET MAUI .

O suporte AOT nativo está disponível como um recurso de aceitação destinado à


implantação do aplicativo; o Mono ainda é o runtime padrão para desenvolvimento e
implantação de aplicativos. Para compilar e executar um aplicativo MAUI do .NET com
AOT nativo em um dispositivo iOS, use dotnet workload install maui para instalar a
carga de trabalho MAUI do .NET e dotnet new maui -n HelloMaui para criar o aplicativo.
Em seguida, defina a propriedade PublishAot do MSBuild como true no arquivo de
projeto.

XML

<PropertyGroup>
<PublishAot>true</PublishAot>
</PropertyGroup>

Quando você definir a propriedade obrigatória e executar dotnet publish conforme


mostrado no exemplo a seguir, o aplicativo será implantado por meio do AOT nativo.

CLI do .NET

dotnet publish -f net8.0-ios -c Release -r ios-arm64 /t:Run

Limitações

Nem todos os recursos do iOS são compatíveis com o AOT nativo. Da mesma forma,
nem todas as bibliotecas comumente usadas no iOS são compatíveis com o NativeAOT.
Além das limitações existentes na implantação nativa do AOT, a lista a seguir mostra
algumas das outras limitações ao focar em plataformas semelhantes a iOS:

O uso de AOT nativo só é habilitado durante a implantação do aplicativo ( dotnet


publish ).
A depuração de código gerenciado só tem suporte com Mono.
A compatibilidade com a estrutura .NET MAUI é limitada.

Compilação AOT para aplicativos Android


Para diminuir o tamanho do aplicativo, os aplicativos .NET e .NET MAUI voltados para o
Android usam o modo de compilação AOT (ahead-of-time) com perfil quando são
criados no modo Release. A compilação AOT com perfil afeta menos métodos do que a
compilação AOT regular. O .NET 8 apresenta a propriedade <AndroidStripILAfterAOT>
que permite que você opte pela compilação AOT adicional para aplicativos Android para
diminuir ainda mais o tamanho do aplicativo.

XML

<PropertyGroup>
<AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
</PropertyGroup>

Por padrão, a configuração AndroidStripILAfterAOT para true substituir a configuração


padrão AndroidEnableProfiledAot , permitindo que (quase) todos os métodos
compilados por AOT sejam cortados. Você também pode usar a remoção de perfil de
AOT e IL definindo explicitamente ambas as propriedades como true :

XML

<PropertyGroup>
<AndroidStripILAfterAOT>true</AndroidStripILAfterAOT>
<AndroidEnableProfiledAot>true</AndroidEnableProfiledAot>
</PropertyGroup>

Aplicativos do Windows integrados


Quando você cria aplicativos destinados ao Windows em plataformas que não são do
Windows, o executável resultante agora é atualizado com todos os recursos—do Win32
especificados, por exemplo, ícone do aplicativo, manifesto, informações de versão.

Anteriormente, os aplicativos tinham que ser criados no Windows para ter esses
recursos. Corrigir essa lacuna no suporte entre construções tem sido uma solicitação
popular, pois foi um ponto de dor significativo que afeta a complexidade da
infraestrutura e o uso de recursos.
.NET no Linux

Linhas de base de suporte mínimas para Linux


As linhas de base de suporte mínimas para Linux foram atualizadas para o .NET 8. O
.NET é criado visando o Ubuntu 16.04, para todas as arquiteturas. Isso é importante
principalmente para definir a versão mínima de glibc para o .NET 8. O .NET 8 não será
iniciado em versões de distribuição que incluem um glibc mais antigo, como o Ubuntu
14.04 ou o Red Hat Enterprise Linux 7.

Para obter mais informações, confira Suporte à família Red Hat Enterprise Linux .

Compilar o próprio .NET no Linux


Em versões anteriores do .NET, você poderia compilar o .NET por meio da origem, mas
isso exigia a criação de um "tarball de origem" do commit do repositório
dotnet/installer que correspondia a uma versão. No .NET 8, isso não é mais necessário
e você pode compilar o .NET no Linux diretamente no repositório dotnet/dotnet . Esse
repositório usa dotnet/source-build para compilar runtimes, ferramentas e SDKs do
.NET. Esse é o mesmo build que o Red Hat e a Canonical usam para compilar o .NET.

A compilação em um contêiner é a abordagem mais fácil para a maioria das pessoas, já


que as imagens de contêiner dotnet-buildtools/prereqs contêm todas as dependências
necessárias. Para obter mais informações, confira as instruções de build .

Verificação de assinatura do NuGet


A partir do .NET 8, o NuGet verifica os pacotes assinados no Linux por padrão. O NuGet
continua a verificar os pacotes assinados no Windows também.

A maioria dos usuários não deve observar a verificação. No entanto, se você tiver um
pacote de certificado raiz existente localizado em /etc/pki/ca-
trust/extracted/pem/objsign-ca-bundle.pem, poderá ver falhas de confiança
acompanhadas do aviso NU3042.

Você pode recusar a verificação definindo a variável de ambiente


DOTNET_NUGET_SIGNATURE_VERIFICATION como false .

Análise de código
O .NET 8 inclui vários novos analisadores de código e reparadores para ajudar a verificar
se você está usando APIs de biblioteca do .NET de maneira correta e eficiente. A tabela
a seguir resume os novos analisadores.

ノ Expandir a tabela

ID da Categoria Descrição
regra

CA1856 Desempenho É acionado quando o atributo ConstantExpectedAttribute não é


aplicado corretamente em um parâmetro.

CA1857 Desempenho É acionado quando um parâmetro é anotado com


ConstantExpectedAttribute, mas o argumento fornecido não é uma
constante.

CA1858 Desempenho Para determinar se uma cadeia de caracteres começa com um


determinado prefixo, é melhor chamar String.StartsWith do que
String.IndexOf e comparar o resultado com zero.

CA1859 Desempenho Essa regra recomenda atualizar o tipo de variáveis locais


específicas, campos, propriedades, parâmetros de método e tipos
de retorno de método de tipos de interface ou abstratos para tipos
concretos quando possível. O uso de tipos concretos leva a um
código gerado de maior qualidade.

CA1860 Desempenho Para determinar se um tipo de coleção tem elementos, é melhor


usar Length , Count ou IsEmpty do que chamar Enumerable.Any.

CA1861 Desempenho Matrizes constantes passadas como argumentos não são


reutilizados quando chamados repetidamente, o que implica que
uma nova matriz é criada a cada vez. Para aprimorar o
desempenho, extraia a matriz para um campo estático somente
leitura.

CA1865- Desempenho A sobrecarga de char é uma sobrecarga de melhor desempenho


CA1867 para uma cadeia de caracteres com um único char.

CA2021 Confiabilidade Enumerable.Cast<TResult>(IEnumerable) e


Enumerable.OfType<TResult>(IEnumerable) exigem tipos
compatíveis para funcionar corretamente. Não há suporte para a
ampliação e conversões definidas pelo usuário com tipos
genéricos.

CA1510- Facilidade de Auxiliares de lançamento são mais simples e eficientes do que um


CA1513 manutenção bloco if que constrói uma nova instância de exceção. Esses
quatro analisadores foram criados para as seguintes exceções:
ArgumentNullException, ArgumentException,
ArgumentOutOfRangeException e ObjectDisposedException.
Diagnósticos

A Recarga Dinâmica do C# dá suporte para a modificação


de genéricos
Iniciando no .NET 8, a Recarga Dinâmica do C# dá suporte para a modificação de tipos
genéricos e métodos genéricos . Ao depurar aplicativos de console, área de trabalho
do Windows, aplicativos para dispositivos móveis ou WebAssembly com o Visual Studio,
será possível aplicar alterações a classes e métodos genéricos no código C# ou nas
páginas do Razor. Para obter mais informações, confira a lista completa de edições com
suporte do Roslyn .

Confira também
Novidades do .NET 8

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Novidades nos contêineres para .NET 8
Artigo • 11/02/2024

Este artigo descreve os novos recursos em contêineres para o .NET 8.

Imagens de contêiner
As seguintes alterações foram feitas nas imagens de contêiner do .NET para .NET 8:

Padrões de imagem gerada


Debian 12
Usuário não raiz
Imagens cinzeladas do Ubuntu
Criar imagens de contêiner de várias plataformas
Imagens compostas ASP.NET

Padrões de imagem gerada


A nova capacidadenon-root dos contêineres do Microsoft .NET agora é o padrão, o que
ajuda seus aplicativos a permanecerem seguros por padrão. Altere esse padrão a
qualquer momento definindo seu próprio ContainerUser .

A marca de contêiner padrão agora é latest . Esse padrão está em linha com outras
ferramentas no espaço de contêineres e facilita o uso de contêineres em loops de
desenvolvimento interno.

Debian 12
As imagens de contêiner agora usam o Debian 12 (Bookworm) . O Debian é a
distribuição padrão do Linux nas imagens de contêiner do .NET.

Usuário não raiz


As imagens incluem um usuário non-root . Esse usuário torna as imagens non-root
compatíveis. Para executar como non-root , adicione a seguinte linha no final do
Dockerfile (ou uma instrução semelhante nos manifestos do Kubernetes):

Dockerfile
USER app

O .NET 8 adiciona uma variável de ambiente para o UID para o usuário non-root , que é
64198. Essa variável de ambiente é útil para o teste runAsNonRoot do Kubernetes , que
exige que o usuário do contêiner seja definido por meio de UID e não por nome. Este
dockerfile mostra um exemplo de uso.

A porta padrão também foi alterada da porta 80 para 8080 . Para dar suporte a essa
alteração, uma nova variável de ambiente ASPNETCORE_HTTP_PORTS está disponível para
facilitar a alteração das portas. A variável aceita uma lista de portas, que é mais simples
do que o formato exigido por ASPNETCORE_URLS . Se você retornar a porta para a porta
80 usando uma dessas variáveis, não poderá executá-la como non-root .

Imagens cinzelada do Ubuntu


As imagens cinzeladas do Ubuntu estão disponíveis no .NET 8. As imagens cinzeladas
têm uma superfície de ataque reduzida porque são ultra pequenas, não têm nenhum
gerenciador de pacotes ou shell e são non-root . Esse tipo de imagem é para
desenvolvedores que desejam os benefícios da computação estilo dispositivo. As
imagens cinzeladas são publicadas no Registro de artefato nightly do .NET .

Criar imagens de contêiner de várias plataformas


O Docker dá suporte ao uso e à criação de imagens multiplataforma que funcionam
em vários ambientes. O .NET 8 apresenta um novo padrão que permite misturar e
combinar arquiteturas com as imagens do .NET que você cria. Por exemplo, se você
estiver usando macOS e quiser direcionar um serviço de nuvem x64 no Azure, poderá
criar a imagem usando a opção --platform da seguinte maneira:

docker build --pull -t app --platform linux/amd64

O SDK do .NET agora dá suporte a valores $TARGETARCH e ao argumento -a na


restauração. O seguinte snippet de código mostra um exemplo:

Dockerfile

RUN dotnet restore -a $TARGETARCH

# Copy everything else and build app.


COPY aspnetapp/. .
RUN dotnet publish -a $TARGETARCH --self-contained false --no-restore -o
/app

Para obter mais informações, consulte a postagem no blog Aprimorando o suporte a


contêineres de várias plataformas .

Imagens compostas ASP.NET


Como parte de um esforço para melhorar o desempenho da conteinerização, novas
imagens do ASP.NET Docker estão disponíveis com uma versão composta do tempo de
execução. Essa composição é criada compilando vários assemblies MSIL em um único
binário de saída R2R (pronto para execução). Como esses assemblies são inseridos em
uma única imagem, o jitting leva menos tempo e o desempenho de inicialização dos
aplicativos melhora. A outra grande vantagem da composição sobre a imagem de
ASP.NET regular é que as imagens compostas têm um tamanho menor no disco.

Há uma ressalva a ser considerada. Como as composições têm vários assemblies


inseridos em um, eles têm um acoplamento de versão mais apertado. Os aplicativos não
podem usar versões personalizadas de binários de estrutura ou ASP.NET.

As imagens compostas estão disponíveis para as plataformas Alpine Linux, Jammy


Chiseled e Mariner Distroless no repositório mcr.microsoft.com/dotnet/nightly/aspnet .
As marcas são listadas com o sufixo -composite na página do ASP.NET Docker .

Publicação de contêiner
Desempenho e compatibilidade
Autenticação
Publicar no arquivo tar.gz

Desempenho e compatibilidade
O .NET 8 melhorou o desempenho para enviar contêineres por push para registros
remotos, especialmente registros do Azure. A aceleração vem de camadas de push em
uma operação e, para registros que não dão suporte a uploads atômicos, um
mecanismo de agrupamento mais confiável.

Essas melhorias também significam que há suporte para mais registros: Harbor,
Artifactory, Quay.io e Podman.

Autenticação
O .NET 8 adiciona suporte à autenticação de troca de token OAuth (Identidade
Gerenciada do Azure) ao enviar contêineres por push para registros. Esse suporte
significa que agora você pode enviar por push para registros como o Registro de
Contêiner do Azure sem erros de autenticação. Os comandos a seguir mostram um
exemplo de fluxo de publicação:

Console

> az acr login -n <your registry name>


> dotnet publish -r linux-x64 -p PublishProfile=DefaultContainer

Para obter mais informações sobre o contêiner de aplicativos .NET, consulte


Containerize um aplicativo .NET com a dotnet publish.

Publicar no arquivo tar.gz


A partir do .NET 8, você pode criar um contêiner diretamente como um arquivo tar.gz.
Esse recurso será útil se o fluxo de trabalho não for simples e exigir que você, por
exemplo, execute uma ferramenta de verificação em suas imagens antes de efetuar push
delas. Depois que o arquivo é criado, você pode movê-lo, examiná-lo ou carregá-lo em
uma cadeia de ferramentas local do Docker.

Para publicar em um arquivo morto, adicione a propriedade


ContainerArchiveOutputPath ao seu comando dotnet publish , por exemplo:

CLI do .NET

dotnet publish \
-p PublishProfile=DefaultContainer \
-p ContainerArchiveOutputPath=./images/sdk-container-demo.tar.gz

Você pode especificar um nome de pasta ou um caminho com um nome de arquivo


específico.

Confira também
Novidades do .NET 8

6 Colaborar conosco no Comentários do .NET


GitHub
A fonte deste conteúdo pode O .NET é um projeto código aberto.
ser encontrada no GitHub, onde Selecione um link para fornecer
você também pode criar e comentários:
revisar problemas e solicitações
de pull. Para obter mais  Abrir um problema de
informações, confira o nosso documentação
guia para colaboradores.
 Fornecer comentários sobre o
produto
Alterações interruptivas no .NET 8
Artigo • 22/02/2024

Se você estiver migrando um aplicativo para o .NET 8, poderá ser afetado pelas
alterações interruptivas listadas aqui. As alterações são agrupadas por área de
tecnologia, como ASP.NET Core ou Windows Forms.

Este artigo categoriza cada alteração interruptiva como incompatível binário ou


incompatível com a origem ou alteração comportamental:

Incompatível binário – Quando executado em relação ao novo runtime ou


componente, os binários existentes podem encontrar uma alteração interruptiva
no comportamento, como falha ao carregar ou executar e, nesse caso, exigir
recompilação.

Incompatível com a origem – Quando recompilado usando o novo SDK ou


componente ou para direcionar ao novo runtime, o código-fonte existente pode
exigir alterações de origem para que seja compilado com êxito.

Alteração comportamental – O código e os binários existentes podem se


comportar de modo diferente em tempo de execução. Se o novo comportamento
for indesejável, o código existente precisará ser atualizado e recompilado.

ASP.NET Core
ノ Expandir a tabela

Título Tipo de alteração

ConcurrencyLimiterMiddleware está obsoleto Incompatível com a origem

Conversores personalizados para serialização foram removidos Alteração de


comportamento

ISystemClock está obsoleto Incompatível com a origem

APIs mínimas: os parâmetros IFormFile exigem verificações anti- Alteração de


falsificação comportamento

O middleware de limitação de taxa requer AddRateLimiter Alteração de


comportamento

Os eventos de token de autenticação retornam um JsonWebToken Alteração de


comportamento
Título Tipo de alteração

O TrimMode usa o padrão completo para projetos do SDK da Web Incompatível com a origem

Contêineres
ノ Expandir a tabela

Título Tipo de alteração

Pacotes "ca-certificates" e "krb5-libs" removidos Incompatível com binários


das imagens do Alpine

Imagens de contêiner do Debian atualizadas para Incompatibilidade de binário/alteração de


o Debian 12 comportamento

Porta ASP.NET Core padrão alterada para 8080 Alteração de comportamento

Pacote "libintl" removido das imagens do Alpine Alteração de comportamento

As marcas de contêiner de várias plataformas são Alteração de comportamento


somente Linux

Novo usuário "app" em imagens do Linux Alteração de comportamento

Bibliotecas principais do .NET


ノ Expandir a tabela

Título Tipo de alteração

Nome da operação de atividade quando nulo Alteração de


comportamento

Comportamento AnonymousPipeServerStream.Dispose Alteração de


comportamento

Obsolescências de API com IDs de diagnóstico personalizadas Incompatível com a


origem

Mapeamento de barra invertida em caminhos de arquivo UNIX Alteração de


comportamento

Métodos Base64.DecodeFromUtf8 ignoram espaço em branco Alteração de


comportamento
Título Tipo de alteração

Suporte ao tipo de enumeração com suporte booliano removido Alteração de


comportamento

Enumeração do caminho do diretório atual do drive Alteração de


comportamento

Enumerable.Sum lança novo OverflowException para algumas Alteração de


entradas comportamento

Gravações do FileStream quando o pipe é fechado Alteração de


comportamento

GC.GetGeneration pode retornar Int32.MaxValue Alteração de


comportamento

Comportamento GetFolderPath no UNIX Alteração de


comportamento

O GetSystemVersion não retorna mais o ImageRuntimeVersion Alteração de


comportamento

Anotações anuláveis ITypeDescriptorContext Incompatível com a


origem

Console.ReadKey herdado removido Alteração de


comportamento

Os construtores de método geram parâmetros com HasDefaultValue Alteração de


definido como false comportamento

ProcessStartInfo.WindowStyle honrado quando UseShellExecute é Alteração de


falso comportamento

O RuntimeIdentifier retorna a plataforma para a qual o runtime foi Alteração de


criado comportamento

Criptografia
ノ Expandir a tabela

Título Tipo de alteração Introduzida

Tamanho da marca de autenticação do AesGcm no Alteração de Preview 1


macOS comportamento

RSA.EncryptValue e RSA.DecryptValue obsoletos Incompatível com a origem Preview 1


Implantação
ノ Expandir a tabela

Título Tipo de alteração

O host determina ativos específicos do RID Incompatibilidade de binário/alteração de


comportamento

O .NET Monitor inclui apenas imagens sem Alteração de comportamento


distribuição

StripSymbols usa true como padrão Alteração de comportamento

Entity Framework Core


Alterações interruptivas no EF Core 8

Extensões
ノ Expandir a tabela

Título Tipo de alteração

ActivatorUtilities.CreateInstance se comporta de forma consistente Alteração de


comportamento

ActivatorUtilities.CreateInstance requer um provedor não nulo Alteração de


comportamento

ConfigurationBinder é gerado para um valor incompatível Alteração de


comportamento

O pacote ConfigurationManager não faz mais referência a Incompatível com a


System.Security.Permissions origem

O pacote DirectoryServices não faz mais referência a Incompatível com a


System.Security.Permissions origem

Chaves vazias adicionadas ao dicionário pelo fichário de configuração Alteração de


comportamento

HostApplicationBuilderSettings.Args respeitado pelo ctor Alteração de


HostApplicationBuilder comportamento

ManagementDateTimeConverter.ToDateTime retorna uma hora local Alteração de


Título Tipo de alteração

comportamento

Alteração de formatação de System.Formats.Cbor DateTimeOffset Alteração de


comportamento

Globalização
ノ Expandir a tabela

Title Tipo de alteração

Conversores de data e hora respeitam o argumento de cultura Alteração de comportamento

O padrão TwoDigitYearMax é 2049 Alteração de comportamento

Interoperabilidade
ノ Expandir a tabela

Title Tipo de alteração

CreateObjectFlags.Unwrap apenas desembrulha na instância de Alteração de


destino comportamento

Os marshallers personalizados exigem membros adicionais Incompatível com a origem

API IDispatchImplAttribute é removida Incompatível com binários

Construtor padrão público implícito JSFunctionBinding removido Incompatível com binários

Os tipos SafeHandle precisam ter um construtor público Incompatível com a origem

Rede
ノ Expandir a tabela

Título Tipo de alteração

SendFile lança NotSupportedException para soquetes sem Alteração de


conexão comportamento
Reflexão
ノ Expandir a tabela

Título Tipo de alteração

IntPtr não é mais usado para tipos de ponteiro de função Alteração de comportamento

.
ノ Expandir a tabela

Title Tipo de alteração

A saída do console da CLI usa UTF-8 Alteração de comportamento/Incompatível com


o código-fonte e com binários

Codificação de console não UTF-8 após a Alteração de comportamento/Incompatibilidade


conclusão de binário

O padrão dos contêineres é usar a tag 'latest' Alteração de comportamento

O 'dotnet pack' usa a configuração Release Alteração comportamental/Incompatível com a


origem

O 'dotnet publish' usa a configuração Release Alteração comportamental/Incompatível com a


origem

Saída duplicada para -getItem, -getProperty e Alteração de comportamento


-getTargetResult

O using implícito para System.Net.Http não é Alteração comportamental/Incompatível com a


mais adicionado origem

Eventos de build derivados personalizados do Alteração de comportamento


MSBuild preteridos

O MSBuild respeita Alteração de comportamento


DOTNET_CLI_UI_LANGUAGE

Aplicativos específicos de runtime não Origem/incompatibilidade de binário


autônomos

A opção --arch não implica auto-contido Alteração de comportamento

A "restauração dotnet" produz avisos de Alteração de comportamento


vulnerabilidade de segurança
Title Tipo de alteração

O SDK usa um grafo RID menor Alteração comportamental/Incompatível com a


origem

O Source Link está incluído no SDK do .NET Incompatível com a origem

O corte de linha não pode ser utilizado com o Alteração de comportamento


.NET Standard ou o .NET Framework.

Pacotes não listados não instalados por Alteração de comportamento


padrão para ferramentas .NET

arquivo .user importado em compilações Alteração de comportamento


externas

Requisitos de versão para o SDK do .NET 8 Incompatível com a origem

Serialização
ノ Expandir a tabela

Título Tipo de alteração

BinaryFormatter foi desabilitado para a maioria dos projetos Alteração de


comportamento

Falha de serialização baseada em reflexão em projetos Alteração de


PublishedTrimmed comportamento

O desserializador baseado em reflexão resolve metadados Alteração de


rapidamente comportamento

Windows Forms
ノ Expandir a tabela

Título Tipo de alteração

Alterações de layout de âncora Alteração de


comportamento

Certificados verificados antes de carregar imagens remotas na Alteração de


PictureBox comportamento

DefaultValueAttribute removido de algumas propriedades Alteração de


comportamento
Título Tipo de alteração

O ctor ExceptionCollection gera a ArgumentException Alteração de


comportamento

Os formulários são dimensionados de acordo com o AutoScaleMode Alteração de


comportamento

O padrão ImageList.ColorDepth é Depth32Bit Alteração de


comportamento

System.Windows.Extensions não faz referência a Incompatível com a


System.Drawing.Common origem

TableLayoutStyleCollection gera a ArgumentException Alteração de


comportamento

Tamanho mínimo e máximo de escala de formulários de nível Alteração de


superior para DPI comportamento

A obsolescência de WFDEV002 agora é um erro Incompatível com a


origem

Confira também
Novidades do .NET 8

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Novidades do .NET 7
Artigo • 16/03/2023

O .NET 7 é o sucessor do .NET 6 e se concentra em ser unificado, moderno, simples e rápido. O


.NET 7 terá suporte por 18 meses como uma versão STS (suporte de período padrão)
(anteriormente conhecida como uma versão atual).

Este artigo lista os novos recursos do .NET 7 e fornece links para informações mais detalhadas
sobre cada um.

Para encontrar todos os artigos do .NET que foram atualizados para o .NET 7, confira
documentos do .NET: novidades para a versão do .NET 7.

Desempenho
O desempenho é um foco fundamental do .NET 7 e todos os seus recursos são projetados
com o desempenho em mente. Além disso, o .NET 7 inclui os seguintes aprimoramentos
voltados puramente para o desempenho:

A OSR (substituição na pilha) é um complemento para compilação em camadas. Ela


permite que o runtime altere o código executado por um método em execução no
momento no meio de sua execução (ou seja, enquanto ele está "na pilha"). Métodos de
execução longa podem mudar para versões mais otimizadas no meio da execução.
A PGO (otimização guiada por perfil) agora funciona com OSR e é mais fácil de habilitar
(adicionando <TieredPGO>true</TieredPGO> ao arquivo de projeto). A PGO também pode
instrumentar e otimizar itens adicionais, como delegados.
Melhor geração de código para Arm64.
O AOT nativo produz um executável autônomo no formato de arquivo da plataforma de
destino sem dependências externas. Ele é totalmente nativo, sem IL ou JIT, e fornece
tempo de inicialização rápido e uma implantação pequena e independente. No .NET 7, o
AOT Nativo se concentra em aplicativos de console e requer que os aplicativos sejam
cortados.
Melhorias de desempenho no runtime Mono, que fornece mais recursos a aplicativos
Blazor WebAssembly, Android e iOS.

Para obter uma visão detalhada de muitos dos recursos focados no desempenho que tornam
o .NET 7 tão rápido, consulte o artigo sobre melhorias de desempenho no .NET 7 .

Serialização de System.Text.Json
O .NET 7 inclui melhorias na serialização de System.Text.Json nas seguintes áreas:
A personalização do contrato oferece mais controle sobre como os tipos são
serializados e desserializados. Para obter mais informações, confira Personalizar um
contrato JSON.
Serialização polimórfica para hierarquias de tipo definidas pelo usuário. Para obter mais
informações, confira Serializar propriedades de classes derivadas.
Suporte para membros necessários, que são propriedades que devem estar presentes
no conteúdo JSON para que a desserialização tenha êxito. Para obter mais informações,
confira Propriedades necessárias.

Para obter informações sobre essas e outras atualizações, confira a postagem no blog sobre
novidades em System.Text.Json no .NET 7 .

Matemática genérica
O .NET 7 e o C# 11 incluem inovações que permitem executar operações matemáticas
genericamente, ou seja, sem precisar saber o tipo exato com o qual você está trabalhando. Por
exemplo, se você quisesse escrever um método que adicionasse dois números, anteriormente
você precisava adicionar uma sobrecarga do método para cada tipo. Agora você pode
escrever um único método genérico, em que o parâmetro de tipo é restrito a ser um tipo
semelhante a um número. Para obter mais informações, consulte o artigo Matemática
genérica e a postagem no blog sobre matemática genérica .

Expressões regulares
A biblioteca de expressões regulares do .NET tem visto melhorias significativas de
desempenho e funcionais no .NET 7:

A nova opção RegexOptions.NonBacktracking permite a correspondência usando uma


abordagem que evita o rastreamento inverso e garante o processamento de tempo
linear no comprimento da entrada. O mecanismo de não retrocesso não pode ser usado
em uma pesquisa da direita para a esquerda e tem algumas outras restrições, mas é
rápido para todas as entradas e expressões regulares. Para obter mais informações,
confira Modo sem retrocesso.

Os geradores de origem de expressão regular são novos. Os geradores de origem criam


um mecanismo otimizado para seu padrão em tempo de compilação, fornecendo
benefícios de desempenho de taxa de transferência. A origem emitida faz parte do
projeto, para que você possa exibi-la e depurá-la. Além disso, um novo diagnóstico de
gerador de origem SYSLIB1045 alerta você para locais em que você usa o Regex que
poderiam ser convertidos no gerador de origem. Para obter mais informações, confira
Geradores de origem de expressão regular do .NET.

Para pesquisas que não diferenciam maiúsculas de minúsculas, o .NET 7 inclui grandes
ganhos de desempenho. Os ganhos vêm porque especificar RegexOptions.IgnoreCase
não chama mais ToLower em cada caractere no padrão e em cada caractere na entrada.
Em vez disso, todo o trabalho relacionado ao uso de maiúsculas e minúsculas é feito
quando o Regex é construído.

Regex agora dá suporte a intervalos para algumas APIs. Os novos métodos a seguir
foram adicionados como parte deste suporte:
Regex.EnumerateMatches
Regex.Count
Regex.IsMatch(ReadOnlySpan<Char>) (e algumas outras sobrecargas)

Para obter mais informações sobre essas e outras melhorias, consulte a postagem no blog
sobre Aprimoramentos em expressões regulares no .NET 7 .

Bibliotecas .NET
Muitas melhorias foram feitas nas APIs de biblioteca do .NET. Algumas são mencionados em
outras seções dedicadas deste artigo. Algumas outras estão resumidas na tabela a seguir.

Descrição APIs Informações adicionais

Suporte para - DateTime.Microsecond Essas APIs significam que você não precisa
microssegundos e - DateTime.Nanosecond mais executar cálculos no valor "tick" para
nanossegundos - DateTime.AddMicroseconds(Double) determinar valores de microssegundos e
em tipos - Novas sobrecargas de construtor nanossegundos. Para obter mais informações,
TimeSpan, DateTime confira a postagem no blog sobre a Versão
TimeOnly, prévia 4 do .NET 7 .
DateTime e - DateTimeOffset.Microsecond
DateTimeOffset - DateTimeOffset.Nanosecond
-
DateTimeOffset.AddMicroseconds(Double)
- Novas sobrecargas de construtor
DateTimeOffset

- TimeOnly.Microsecond
- TimeOnly.Nanosecond

- TimeSpan.Microseconds
- TimeSpan.Nanoseconds
- TimeSpan.FromMicroseconds(Double)
- E outros...

APIs para ler, System.Formats.Tar Para obter mais informações, confira as


escrever, arquivar postagens no blog sobre Versão prévia 4 do
e extrair arquivos .NET 7 e Versão prévia 6 do .NET 7 .
Tar

APIs de limitação RateLimiter e outros no pacote NuGet Para obter mais informações, confira Limite de
de taxa para de System.Threading.RateLimiting taxa de um manipulador HTTP no .NET e
proteger um Anúncio de limitação de taxa para o .NET .
recurso mantendo
Descrição APIs Informações adicionais

o tráfego em um
nível seguro

APIs para ler - Stream.ReadExactly Stream.Read pode retornar menos dados do


todos os dados de - Stream.ReadAtLeast que o que está disponível no fluxo. Os novos
um Stream métodos ReadExactly leem exatamente o
número de bytes solicitados e os novos
métodos ReadAtLeast leem pelo menos o
número de bytes solicitados. Para obter mais
informações, confira a postagem no blog
sobre a Versão prévia 5 do .NET 7 .

Novos No namespace System.ComponentModel: Os conversores de tipo geralmente são usados


conversores de para converter tipos de valor de e para uma
tipo para - DateOnlyConverter cadeia de caracteres. Essas novas APIs
DateOnly , - TimeOnlyConverter adicionam conversores de tipo para tipos que
TimeOnly , Int128 , - Int128Converter foram adicionados mais recentemente.
UInt128 e Half - UInt128Converter
- HalfConverter

Suporte a - MemoryCacheStatistics GetCurrentStatistics() permite que você use


métricas para - MemoryCache.GetCurrentStatistics() APIs de métricas ou contadores de eventos
IMemoryCache para acompanhar estatísticas de um ou mais
caches de memória. Para obter mais
informações, confira a postagem no blog
sobre a Versão prévia 4 do .NET 7 .

APIs para obter e - enum System.IO.UnixFileMode Para obter mais informações, confira a
definir permissões - File.GetUnixFileMode postagem no blog sobre a Versão prévia 7 do
de arquivo Unix - File.SetUnixFileMode .NET 7 .
- FileSystemInfo.UnixFileMode
- Directory.CreateDirectory(String,
UnixFileMode)
- FileStreamOptions.UnixCreateMode

Atributo para StringSyntaxAttribute Por exemplo, você pode especificar que um


indicar que tipo parâmetro string espera uma expressão
de sintaxe é regular atribuindo o parâmetro com
esperado em uma [StringSyntax(StringSyntaxAttribute.Regex)] .
cadeia de
caracteres

APIs para System.Runtime.InteropServices.JavaScript Os aplicativos JavaScript podem usar o


interoperabilidade suporte expandido do WebAssembly no .NET
com JavaScript ao 7 para reutilizar bibliotecas do .NET do
executar no JavaScript. Para obter mais informações,
navegador ou em confira Usar o .NET de qualquer aplicativo
outras JavaScript no .NET 7 .
arquiteturas
WebAssembly
Observabilidade
O .NET 7 faz melhorias na observabilidade. A observabilidade ajuda você a entender o estado
do aplicativo à medida que ele é dimensionado e à medida que a complexidade técnica
aumenta. A implementação de observabilidade do .NET é criada principalmente em torno do
OpenTelemetry . As melhorias incluem:

O novo evento Activity.CurrentChanged, que você pode usar para detectar quando o
contexto de intervalo de um thread gerenciado é alterado.
Novos métodos de enumerador com desempenho para propriedades Activity:
EnumerateTagObjects(), EnumerateLinks() e EnumerateEvents().

Para obter mais informações, confira a postagem no blog sobre a Versão prévia 4 do .NET 7 .

SDK .NET
O SDK do .NET 7 melhora a experiência de modelo da CLI. Ele também permite a publicação
em contêineres e o gerenciamento central de pacotes com o NuGet.

Modelos
Algumas melhorias bem-vindas foram feitas no comando dotnet new e na criação de modelo.

dotnet new

O comando da CLI dotnet new, que cria um novo projeto, arquivo de configuração ou solução
com base em um modelo, agora dá suporte à conclusão da guia para explorar:

Nomes de modelo disponíveis


Opções de modelo
Valores de opção permitidos

Além disso, para melhor conformidade, os subcomandos install , uninstall , search , list e
update não têm mais o prefixo -- .

Criação
As restrições de modelo, um novo conceito para o .NET 7, permitem definir o contexto no qual
seus modelos são permitidos. As restrições ajudam o mecanismo de modelo a determinar
quais modelos ele deve mostrar em comandos como dotnet new list . Você pode restringir
seu modelo a um sistema operacional, um host de mecanismo de modelo (por exemplo, a CLI
do .NET ou a caixa de diálogo Novo Projeto no Visual Studio) e uma carga de trabalho
instalada. Você define restrições no arquivo de configuração do seu modelo.
Também no arquivo de configuração de modelo, agora você pode anotar um parâmetro de
modelo como permitindo vários valores. Por exemplo, o modelo web permite várias formas de
autenticação.

Para obter mais informações, confira a postagem no blog sobre a Versão prévia 6 do .NET 7 .

Publicar em um contêiner
Os contêineres são uma das maneiras mais fáceis de distribuir e executar uma ampla
variedade de aplicativos e serviços na nuvem. As imagens de contêiner agora são um tipo de
saída com suporte do SDK do .NET e você pode criar versões em contêineres de seus
aplicativos usando dotnet publish. Para obter mais informações sobre o recurso, confira um
artigo sobre o Anúncio do suporte de contêiner integrado para o SDK do .NET . Para acessar
um tutorial, confira Colocar um aplicativo .NET em um contêiner com dotnet publish.

Gerenciamento de pacotes central


Agora você pode gerenciar dependências comuns em seus projetos a partir de um local
usando o recurso de CPM (gerenciamento de pacote central) do NuGet. Para habilitá-lo,
adicione um arquivo Directory.Packages.props à raiz do repositório. Nesse arquivo, defina a
propriedade MSBuild ManagePackageVersionsCentrally como true e adicione versões para
dependência de pacote comum usando itens PackageVersion . Em seguida, nos arquivos de
projeto individuais, você pode omitir atributos Version de todos os itens PackageReference
que se referem a pacotes gerenciados centralmente.

Para obter mais informações, confira Gerenciamento de pacote central.

Geração de origem P/Invoke


O .NET 7 apresenta um gerador de origem para invocações de plataforma (P/Invokes) em C#.
O gerador de origem procura LibraryImportAttribute em métodos static , partial para
disparar a geração de origem de tempo de compilação do código marshalling. Ao gerar o
código marshalling em tempo de compilação, nenhum stub IL precisa ser gerado em tempo
de execução, como faz ao usar DllImportAttribute. O gerador de origem melhora o
desempenho do aplicativo e também permite que o aplicativo seja compilado
antecipadamente (AOT). Para obter mais informações, confira Geração de origem para
invocações de plataforma e Usar marshallers personalizados em P/Invokes gerados pela
origem.

Versões relacionadas
Esta seção contém informações sobre produtos relacionados que têm versões que coincidem
com a versão do .NET 7.
Visual Studio 2022 versão 17.4
Para obter mais informações, confira Novidades no Visual Studio 2022.

C# 11
O C# 11 inclui suporte para matemática genérica, literais de cadeia de caracteres brutos, tipos
com escopo de arquivo e outros novos recursos. Para obter mais informações, confira
Novidades do C# 11.

F# 7
O F# 7 continua o percurso para tornar a linguagem mais simples e melhorar o desempenho e
a interoperabilidade com novos recursos do C#. Para obter mais informações, confira
Apresentação do F# 7 .

.NET MAUI
O .NET MAUI (Interface do usuário de aplicativo multiplataforma) é uma estrutura
multiplataforma para criar aplicativos móveis e desktop nativos com C# e XAML. Ele unifica
APIs do Android, iOS, macOS e Windows em uma única API. Para obter informações sobre as
atualizações mais recentes, confira Novidades no MAUI do .NET para .NET 7.

ASP.NET Core
O ASP.NET Core 7.0 inclui middleware de limitação de taxa, melhorias em APIs mínimas e
transcodificação de JSON gRPC. Para obter informações sobre todas as atualizações, confira
Novidades no ASP.NET Core 7.

EF Core
O Entity Framework Core 7.0 inclui suporte independente de provedor para colunas JSON,
melhor desempenho para salvar alterações e modelos de engenharia reversa personalizados.
Para obter informações sobre todas as atualizações, confira Novidades no EF Core 7.0.

Windows Forms
Muito trabalho foi realizado no Windows Forms para .NET 7. Foram feitas melhorias nas
seguintes áreas:

Acessibilidade
DPI alto e escala
Associação de dados
Para obter mais informações, confira Novidades no Windows Forms no .NET 7 .

WPF
O WPF no .NET 7 inclui várias correções de bugs, bem como melhorias de desempenho e
acessibilidade. Para obter mais informações, confira a postagem de blog sobre Novidades do
WPF no .NET 7 .

Orleans
O Orleans é uma estrutura multiplataforma para a criação de aplicativos distribuídos robustos
e escalonáveis. Para saber mais sobre as atualizações mais recentes do Orleans, confira Migrar
do Orleans 3.x para o 7.0.

Assistente de atualização do .NET e CoreWCF


O Assistente de atualização do .NET agora dá suporte à atualização de aplicativos WCF do
lado do servidor para CoreWCF , que é uma porta criada pela comunidade do WCF para o
.NET (Core). Para obter mais informações, confira Atualizar um projeto do lado do servidor do
WCF para usar o CoreWCF.

ML.NET
O ML.NET agora inclui uma API de classificação de texto que facilita o treinamento de
modelos de classificação de texto personalizados usando as técnicas de aprendizado profundo
de última geração mais recentes. Para obter mais informações, confira as postagens no blog
sobre novidades no AutoML e ferramentas e Introdução à API de classificação de texto
ML.NET .

Confira também
Notas sobre a versão do .NET 7

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open source.
Provide feedback here.
be found on GitHub, where you
can also create and review issues
 Open a documentation issue
and pull requests. For more
 Provide product feedback
information, see our contributor
guide.
Alterações interruptivas no .NET 7
Artigo • 17/08/2023

Se você estiver migrando um aplicativo para o .NET 7, as alterações interruptivas listadas


aqui poderão afetar você. As alterações são agrupadas por área de tecnologia, como
ASP.NET Core ou Windows Forms.

Este artigo indica se cada alteração interruptiva é compatível com binários ou compatível
com o código-fonte:

Compatível com binários – Os binários existentes serão carregados e executados


com êxito sem recompilação e o comportamento em tempo de execução não será
alterado.
Compatível com o código-fonte – O código-fonte será compilado com êxito sem
alterações durante o redirecionamento ao novo runtime ou quando um novo SDK
ou componente for usado.

ASP.NET Core
Título Compatível Compatível
com binários com a origem

As ações do controlador de API tentam inferir parâmetros da ✔️ ❌


DI

Precedência de variável de ambiente prefixada com ASPNET ✔️ ✔️

AuthenticateAsync para provedores de autenticação remota ✔️ ❌

Autenticação em aplicativos WebAssembly ❌ ✔️

Esquema de autenticação padrão ❌ ✔️

As IDs de evento de algumas mensagens de log do ❌ ✔️


Microsoft.AspNetCore.Mvc.Core foram alteradas

Pontos de extremidade de arquivo de fallback ❌ ✔️

IHubClients e IHubCallerClients ocultam membros ✔️ ❌

Kestrel: associação HTTPS padrão removida ❌ ✔️

O Microsoft.AspNetCore.Server.Kestrel.Transport.Libuv e o ❌ ❌
libuv.dll foram removidos

O Microsoft.Data.SqlClient foi atualizado para 4.0.1 ✔️ ❌


Título Compatível Compatível
com binários com a origem

O Middleware não é mais adiado para o ponto de ❌ ✔️


extremidade com o delegado de solicitação nula

A detecção do MVC de um corpo vazio no model binding foi ❌ ✔️


alterada

Alterações na API de cache de saída ❌ ❌

Os métodos do hub do SignalR tentam resolver parâmetros ✔️ ❌


da DI

Bibliotecas principais do .NET


Título Compatível com Compatível com a
binários origem

Obsolescências de API com IDs de diagnóstico não ✔️ ❌


padrão

Obsolescências de API com IDs de diagnóstico não ✔️ ❌


padrão

APIs de serialização BinaryFormatter produzem erros ✔️ ❌


de compilador

BrotliStream não permite mais valores de ❌ ✔️


CompressionLevel indefinidos

Projetos C++/CLI no Visual Studio ✔️ ❌

Alterações nas exceções de API de invocação de ❌ ✔️


reflexão

Assembly de coleção no AssemblyLoadContext não ❌ ✔️


colecionável

Alteração de precisão dos métodos de adição de ✔️ ✔️


DateTime

Igual à alteração de comportamento do método para ❌ ✔️


NaN

Comportamento de retorno de chamada do ✔️ ✔️


EventSource

Restrição de tipo genérico em PatternContext<T> ❌ ❌


Título Compatível com Compatível com a
binários origem

Estratégia de FileStream herdada removida ❌ ✔️

Suporte à biblioteca para estruturas mais antigas ❌ ❌

Precisão máxima para cadeias de caracteres de ❌ ✔️


formato numérico

O SerializationFormat.Binary está obsoleto ❌ ❌

A opção de configuração System.Drawing.Common foi ✔️ ✔️


removida

Pacote NuGet ✔️ ✔️
System.Runtime.CompilerServices.Unsafe

Campos de tempo em links simbólicos ❌ ✔️

Rastreamento de entradas de cache vinculadas ❌ ✔️

Validar o CompressionLevel para BrotliStream ❌ ✔️

Configuração
Título Compatível com Compatível com a
binários origem

Entrada System.diagnostics no ❌ ✔️
app.config

Criptografia
Título Compatível com Compatível com a
binários origem

Descriptografar EnvelopedCms não cancela quebra ❌ ✔️


de linha duas vezes

Hora de verificação do X509ChainPolicy dinâmico ❌ ✔️

Análise de nomes amigáveis de ❌ ✔️


X500DistinguishedName

Implantação
Título Compatível com Compatível com a
binários origem

Todos os assemblies cortados por padrão ✔️ ❌

A pesquisa em vários níveis está ❌ ✔️


desabilitada

Caminho do host x86 no Windows 64 bits ✔️ ✔️

TrimmerDefaultAction foi preterido ✔️ ❌

Entity Framework Core


Alterações interruptivas no EF Core 7

Extensões
Título Compatível com Compatível com a
binários origem

A configuração de associação ao dicionário ✔️ ✔️


estende valores

ContentRootPath para aplicativos iniciados pelo ❌ ✔️


Windows Shell

Prefixos de variável de ambiente ❌ ✔️

Globalização
Title Compatível com Compatível com a
binários origem

AS APIs de globalização usam bibliotecas de ICU ❌ ✔️


no Windows Server

Interoperabilidade
Title Compatível com Compatível com a
binários origem

RuntimeInformation.OSArchitecture em ❌ ✔️
Title Compatível com Compatível com a
binários origem

emulação

.NET MAUI
Título Compatível com Compatível com a
binários origem

Construtores aceitam interface base em vez de ❌ ✔️


tipo concreto

Métodos auxiliares de direção de fluxo ❌ ❌


removidos

Novo parâmetro UpdateBackground ❌ ✔️

Propriedade ScrollToRequest renomeada ❌ ❌

Algumas APIs do Windows foram removidas ❌ ❌

Rede
Título Compatível com Compatível com a
binários origem

O padrão de AllowRenegotiation é false ❌ ❌

Conteúdos de ping personalizados no Linux ❌ ✔️

Os métodos Socket.End não lançam ❌ ✔️


ObjectDisposedException

SDK e MSBuild
Título Compatível com Compatível com a
binários origem

RuntimeIdentifier automático para determinados ✔️ ❌


projetos

RuntimeIdentifier automático somente para ❌ ❌


publicação

A saída do console da CLI usa UTF-8 ❌ ❌


Título Compatível com Compatível com a
binários origem

Codificação de console não UTF-8 após a conclusão ❌ ✔️

Serialização do MSBuild de tipos personalizados no ❌ ❌


.NET 7

Instalações do SDK lado a lado ❌ ❌

A ferramenta se manifesta na pasta raiz ✔️ ✔️

Requisitos de versão do SDK do .NET 7 ✔️ ✔️

teste dotnet: alternar -a para alias --arch em vez de - ❌ ❌


-test-adapter-path

teste dotnet: alternar -r para alias --runtime em vez ❌ ❌


de --results-dir

A opção --output não é mais válida para comandos ❌ ❌


no nível da solução

O SDK não chama mais ✔️ ❌


ResolvePackageDependencies

Serialização
Título Compatível com Compatível com a
binários origem

DataContractSerializer retém o sinal ao desserializar ❌ ✔️


-0

Desserializar o tipo de versão com espaço em ❌ ✔️


branco à esquerda ou à direita

O construtor de cópia JsonSerializerOptions inclui ❌ ✔️


JsonSerializerContext

Serialização polimórfica para tipos de objeto ❌ ✔️

Fallback do gerador de origem System.Text.Json ❌ ✔️

Windows Forms
Título Compatível com Compatível com a
binários origem

Obsolescências e avisos ✔️ ❌

Algumas APIs geram ❌ ✔️


ArgumentNullException

XML e XSLT
Título Compatível com binários Compatível com a origem

XmlSecureResolver está obsoleto ❌ ❌

Confira também
Novidades do .NET 7

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Novidades no .NET 6
Artigo • 10/05/2023

O .NET 6 fornece as partes finais do plano de unificação do .NET que começou com o
.NET 5. O .NET 6 unifica o SDK, as bibliotecas base e o runtime em aplicativos móveis,
para desktop, IoT e na nuvem. Além dessa unificação, o ecossistema do .NET 6 oferece:

Desenvolvimento simplificado: começar é fácil. Novos recursos de linguagem no


C# 10 reduzem a quantidade de código que você precisa escrever. E os
investimentos na pilha da Web e as APIs mínimas facilitam a gravação rápida de
microsserviços menores e mais rápidos.

Melhor desempenho: o .NET 6 é a estrutura da Web de pilha completa e mais


rápida, o que reduz os custos de computação se você estiver executando na
nuvem.

Produtividade final: o .NET 6 e o Visual Studio 2022 fornecem recarregamento


frequente, novas ferramentas git, edição de código inteligente, ferramentas
robustas de diagnóstico e teste e melhor colaboração em equipe.

O .NET 6 terá suporte por três anos como uma versão LTS (suporte de longo prazo).

Os recursos de versão prévia estão desabilitados por padrão. Também não há suporte
para usá-los em produção, e podem ser removidos em uma versão futura. O novo
RequiresPreviewFeaturesAttribute é usado para anotar APIs de versão prévia e um
analisador correspondente alerta você se você estiver usando essas APIs de versão
prévia.

O .NET 6 tem suporte no Visual Studio 2022 e no Visual Studio 2022 para Mac (e versões
posteriores).

Este artigo não aborda todos os novos recursos do .NET 6. Para ver todos os novos
recursos e para obter mais informações sobre os recursos listados neste artigo, confira a
postagem no blog Anúncio do .NET 6 .

Desempenho
O .NET 6 inclui várias melhorias de desempenho. Esta seção lista algumas das melhorias
em FileStream, otimização guiada por perfil e compilação AOT. Para obter informações
detalhadas, confira a postagem no blog Aprimoramentos de desempenho no .NET 6 .
FileStream
O tipo System.IO.FileStream foi reescrito para o .NET 6 para fornecer melhor
desempenho e confiabilidade no Windows. Agora, o FileStream nunca é bloqueado
quando criado para E/S assíncrona no Windows. Para saber mais, confira a postagem no
blog Aprimoramentos de E/S de arquivo no .NET 6 .

Otimização guiada por perfil


É na PGO (otimização guiada por perfil) que o compilador JIT gera o código otimizado
em termos dos tipos e caminhos de código usados com mais frequência. O .NET 6
apresenta o PGO dinâmico. O PGO dinâmico funciona de forma estreita com a
compilação em camadas para otimizar ainda mais o código com base na
instrumentação adicional que é implementada durante a camada 0. O PGO dinâmico é
desabilitado por padrão, mas você pode habilitá-lo com a DOTNET_TieredPGO variável de
ambiente. Para saber mais, confira Aprimoramentos de desempenho do JIT .

Crossgen2
O .NET 6 apresenta o Crossgen2, o sucessor do Crossgen, que foi removido. Crossgen e
Crossgen2 são ferramentas que fornecem compilação AOT (antecipada) para melhorar o
tempo de inicialização de um aplicativo. Crossgen2 é escrito em C# em vez de C++, e
pode executar análise e otimização que não eram possíveis com a versão anterior. Para
saber mais, confira Conversa sobre Crossgen2 .

Suporte ao Arm64
A versão do .NET 6 inclui suporte para sistemas operacionais macOS Arm64 (ou "Apple
Silicon") e Windows Arm64, para execução nativa do Arm64 e emulação de x64. Além
disso, os instaladores do .NET x64 e Arm64 agora são instalados lado a lado. Para saber
mais, confira Suporte do .NET para macOS 11 e Windows 11 para Arm64 e x64 .

Recarga Dinâmica
Recarga frequente é um recurso que permite modificar o código-fonte do aplicativo e
aplicar instantaneamente essas alterações ao aplicativo em execução. A finalidade do
recurso é aumentar sua produtividade evitando reinicializações de aplicativo entre
edições. A Recarga frequente está disponível no Visual Studio 2022 e na ferramenta de
linha de comando dotnet watch . A Recarga frequente funciona com a maioria dos tipos
de aplicativos .NET e para código-fonte em Visual Basic, C# e C++. Para saber mais,
confira a postagem de blog sobre Recarga frequente .

.NET MAUI
A interface do usuário de aplicativo multiplataforma do .NET (.NET MAUI) ainda está em
versão prévia, com uma versão RC chegando no primeiro trimestre de 2022, e a de GA
(disponibilidade geral) no segundo trimestre de 2022. O .NET MAUI possibilita a criação
de aplicativos cliente nativos para sistemas operacionais móveis e para desktop com
uma única base de código. Para saber mais, confira a postagem no blog Atualização na
interface do usuário de aplicativo multiplataforma do .NET .

C# 10 e modelos
O C# 10 inclui inovações, como diretivas global using , declarações de namespace com
escopo de arquivo e structs de registro. Para saber mais, confira Novidades do C# 10.

Em conjunto com esse trabalho, os modelos de projeto do SDK do .NET para C# foram
modernizados para usar alguns dos novos recursos de linguagem:

Método async Main


Instruções de nível superior
Expressões new com tipo de destino
Diretivas implícitas global using
Namespaces com escopo de arquivo
Tipos de referência anuláveis

Ao adicionar esses novos recursos de linguagem aos modelos de projeto, o novo


código começa com os recursos habilitados. No entanto, o código existente não é
afetado quando você atualiza para o .NET 6. Para saber mais sobre essas alterações de
modelo, confira a postagem no blog SDK .NET: modelos de projeto em C#
modernizados .

F# e Visual Basic
O F# 6 adiciona várias melhorias à linguagem F# e ao F# Interativo. Para saber mais,
confira Novidades do F# 6.

O Visual Basic tem melhorias na experiência do Visual Studio e na inicialização do


projeto do Windows Forms.
Cargas de trabalho do SDK
Para manter o tamanho menor do SDK do .NET, alguns componentes foram colocados
em cargas de trabalho do SDK novas e opcionais. Esses componentes incluem o .NET
MAUI e o Blazor WebAssembly AOT. Se você usar o Visual Studio, ele cuidará da
instalação de todas as cargas de trabalho do SDK necessárias. Se você usar a CLI do
.NET, poderá gerenciar cargas de trabalho usando os novos comandos dotnet workload :

Comando Descrição

dotnet workload Pesquisa cargas de trabalho disponíveis.


search

dotnet workload Instala uma carga de trabalho especificada.


install

dotnet workload Remove uma carga de trabalho especificada.


uninstall

dotnet workload Atualiza cargas de trabalho instaladas.


update

dotnet workload Reinstala todas as cargas de trabalho instaladas para reparar uma
repair instalação interrompida.

dotnet workload list Lista as cargas de trabalho instaladas.

Para saber mais, confira Cargas de trabalho de SDK opcionais .

APIs System.Text.Json
Muitas melhorias foram feitas em System.Text.Json no .NET 6, de modo que, agora, ele é
uma solução de serialização de "força industrial".

Gerador de origem
O .NET 6 adiciona um novo gerador de origem para System.Text.Json. A geração de
origem funciona com JsonSerializer e pode ser configurada de várias maneiras. Ela pode
melhorar o desempenho, reduzir o uso de memória e facilitar o corte do assembly. Para
saber mais, confira Como escolher reflexão ou geração de origem em System.Text.Json e
Como usar a geração de origem no System.Text.Json.

DOM gravável
Um novo DOM (modelo de objeto de documento) gravável foi adicionado, o que
complementa o DOM somente leitura pré-existente. A nova API fornece uma alternativa
leve de serialização para casos em que o uso de tipos POCO (objeto CLR básico) não é
possível. Ela também permite que você navegue com eficiência até uma subseção de
uma árvore JSON grande e leia uma matriz ou desserialize um POCO a partir dessa
subseção. Os novos tipos a seguir foram adicionados para dar suporte ao DOM
gravável:

JsonNode
JsonArray
JsonObject
JsonValue

Para saber mais, confira Opções de DOM JSON.

Serialização IAsyncEnumerable
Agora, System.Text.Json dá suporte à serialização e à desserialização com instâncias
IAsyncEnumerable<T>. Os métodos de serialização assíncrona enumeram todas as
instâncias de IAsyncEnumerable<T> em um grafo de objetos e, em seguida, as
serializam como matrizes JSON. Para desserialização, o novo método
JsonSerializer.DeserializeAsyncEnumerable<TValue>(Stream, JsonSerializerOptions,
CancellationToken) foi adicionado. Para saber mais, confira Serialização
IAsyncEnumerable.

Outras APIs novas


Novas interfaces de serialização para validação e valores padrão:

IJsonOnDeserialized
IJsonOnDeserializing
IJsonOnSerialized
IJsonOnSerializing

Para saber mais, confira Retornos de chamada.

Novo atributo de ordenação de propriedade:

JsonPropertyOrderAttribute

Para saber mais, confira Configurar a ordem das propriedades serializadas.

Novo método para escrever JSON "bruto":


Utf8JsonWriter.WriteRawValue

Para saber mais, confira Escrever JSON bruto.

Serialização e desserialização síncrona para um fluxo:

JsonSerializer.Deserialize(Stream, Type, JsonSerializerOptions)


JsonSerializer.Deserialize(Stream, Type, JsonSerializerContext)
JsonSerializer.Deserialize<TValue>(Stream, JsonSerializerOptions)
JsonSerializer.Deserialize<TValue>(Stream, JsonTypeInfo<TValue>)
JsonSerializer.Serialize(Stream, Object, Type, JsonSerializerOptions)
JsonSerializer.Serialize(Stream, Object, Type, JsonSerializerContext)
JsonSerializer.Serialize<TValue>(Stream, TValue, JsonSerializerOptions)
JsonSerializer.Serialize<TValue>(Stream, TValue, JsonTypeInfo<TValue>)

Nova opção para ignorar um objeto quando um ciclo de referência é detectado durante
a serialização:

ReferenceHandler.IgnoreCycles

Para saber mais, confira Ignorar referências circulares.

Para saber mais sobre serialização e desserialização com System.Text.Json , confira


Serialização e desserialização JSON no .NET.

HTTP/3
O .NET 6 inclui suporte de versão prévia para HTTP/3, uma nova versão do HTTP. O
HTTP/3 resolve alguns desafios funcionais e de desempenho existentes usando um
novo protocolo de conexão subjacente chamado QUIC. O QUIC estabelece conexões
mais rapidamente, e essas conexões são independentes do endereço IP, permitindo que
os clientes móveis percorram redes Wi-fi e celular. Para saber mais, confira Usar HTTP/3
com HttpClient.

ASP.NET Core
O ASP.NET Core inclui melhorias em APIs mínimas, compilação AOT (antecipada) para
aplicativos Blazor WebAssembly e aplicativos de página única. Além disso, os
componentes do Blazor agora podem ser renderizados do JavaScript e integrados com
aplicativos baseados em JavaScript existentes. Para saber mais, confira Novidades no
ASP.NET Core 6.
OpenTelemetry
O .NET 6 traz suporte aprimorado para OpenTelemetry , que é uma coleção de
ferramentas, APIs e SDKs que ajudam você a analisar o desempenho e o
comportamento do software. As APIs no namespace System.Diagnostics.Metrics
implementam a especificação da API de Métricas do OpenTelemetry . Por exemplo, há
quatro classes de instrumento para dar suporte a diferentes cenários de métricas. As
classes de instrumento são:

Counter<T>
Histogram<T>
ObservableCounter<T>
ObservableGauge<T>

Segurança
O .NET 6 adiciona suporte de versão prévia para duas mitigações de segurança
principais: CET (Tecnologia de Imposição de fluxo de controle) e "execução exclusiva de
gravação" (W^X).

O CET é uma tecnologia Intel disponível em alguns processadores Intel e AMD mais
recentes. Ele adiciona funcionalidades ao hardware que protegem contra alguns ataques
de sequestro de fluxo de controle. O .NET 6 fornece suporte para CET para aplicativos
Windows x64 e você deve habilitá-lo explicitamente. Para saber mais, confira
Compatibilidade do .NET 6 com pilhas de sombra do Intel CET .

O W^X está disponível em todos os sistemas operacionais com o .NET 6, mas está
habilitado apenas por padrão no Apple Silicon. O W^X bloqueia o caminho de ataque
mais simples, não permitindo que páginas de memória sejam graváveis e executáveis ao
mesmo tempo.

Corte de IL
O corte de implantações independentes autocontidas foi aprimorado. No .NET 5, apenas
assemblies não utilizados foram cortados. O .NET 6 também adiciona o corte de tipos e
membros não utilizados. Além disso, os avisos de corte, que alertam você sobre os
locais onde o corte pode remover o código usado em tempo de execução, agora estão
habilitados por padrão. Para obter mais informações, confira Cortar implantações e
executáveis autocontidos.
Análise de código
O SDK do .NET 6 inclui um punhado de novos analisadores de código relacionados à
compatibilidade da API, compatibilidade da plataforma, segurança de corte, uso de
extensão na concatenação e divisão de cadeia de caracteres, APIs de cadeia de
caracteres mais rápidas e APIs de coleção mais rápidas. Para obter uma lista completa
de analisadores novos (e removidos), confira Versões do Analisador – .NET 6 .

Proteções de plataforma personalizadas


O Analisador de compatibilidade de plataforma reconhece os métodos Is<Platform> na
classe OperatingSystem, por exemplo, OperatingSystem.IsWindows()como proteções de
plataforma. Para permitir proteções de plataforma personalizadas, o .NET 6 introduz
dois novos atributos que você pode usar para anotar campos, propriedades ou métodos
com um nome de plataforma com ou sem suporte:

SupportedOSPlatformGuardAttribute
UnsupportedOSPlatformGuardAttribute

Windows Forms
Application.SetDefaultFont(Font) é um novo método no .NET 6 que define a fonte
padrão em seu aplicativo.

Os modelos para aplicativos de Windows Forms em C# foram atualizados para dar


suporte a diretivas global using , namespaces com escopo de arquivo e tipos de
referência anuláveis. Além disso, incluem o código de inicialização do aplicativo, que
reduz o código clichê e permite que o designer do Windows Forms renderize a
superfície de design na fonte preferencial. O código de inicialização é uma chamada
para ApplicationConfiguration.Initialize() , que é um método gerado pela origem
que emite chamadas para outros métodos de configuração, como
Application.EnableVisualStyles(). Além disso, se você definir uma fonte não padrão por
meio da propriedade ApplicationDefaultFont do MSBuild,
ApplicationConfiguration.Initialize() emitirá uma chamada para

SetDefaultFont(Font).

Para saber mais, confira a postagem no blog Novidades no Windows Forms .

Build de origem
O tarball de origem, que contém toda a origem do SDK do .NET, agora é um produto do
build do SDK do .NET. Outras organizações, como o Red Hat, podem criar sua própria
versão do SDK usando esse tarball de origem.

Monikers da Estrutura de Destino


Os TFMs (Monikers da Estrutura de Destino) adicionais específicos ao sistema
operacional foram adicionados para .NET 6, por exemplo, net6.0-android , net6.0-ios e
net6.0-macos . Para saber mais, confira TFMs específicos ao sistema operacional .NET 5+.

Matemática genérica
Está em versão prévia a capacidade de usar operadores em tipos genéricos no .NET 6. O
.NET 6 apresenta várias interfaces que usam a nova versão prévia do recurso do C# 10,
membros da interface static abstract . Essas interfaces correspondem a operadores
diferentes, por exemplo, IAdditionOperators representa o operador + . As interfaces
estão disponíveis no pacote NuGet System.Runtime.Experimental . Para saber mais,
confira a postagem no blog Matemática genérica .

Validação do pacote NuGet


Se você for um desenvolvedor de biblioteca do NuGet, a nova ferramenta de validação
de pacote permitirá que você valide se os pacotes são consistentes e bem formados.
Você pode determinar se:

Há alterações significativas nas versões do pacote.


O pacote tem o mesmo conjunto de APIs públicas para todas as implementações
específicas ao runtime.
Há lacunas para aplicabilidade de estrutura de destino ou runtime.

Para saber mais, confira a postagem no blog Validação do pacote .

APIs de reflexão
O .NET 6 apresenta as seguintes APIs novas que inspecionam o código e fornecem
informações de nulidade:

System.Reflection.NullabilityInfo
System.Reflection.NullabilityInfoContext
System.Reflection.NullabilityState
Essas APIs são úteis para ferramentas e serializadores baseados em reflexão.

APIs de Microsoft.Extensions
Vários namespaces de extensões receberam melhorias no .NET 6, como mostra a tabela
a seguir.

Namespace Aprimoramentos

Microsoft.Extensions.DependencyInjection CreateAsyncScope permite que você use com


segurança uma instrução using para um provedor
de serviços que registra um serviço
IAsyncDisposable.

Microsoft.Extensions.Hosting Novos métodos ConfigureHostOptions simplificam a


configuração do aplicativo.

Microsoft.Extensions.Logging Microsoft.Extensions.Logging tem um novo gerador


de origem para APIs de registro em log com
desempenho. O gerador de origem é disparado se
você adicionar o novo LoggerMessageAttribute a um
método de registro em log partial . No tempo de
compilação, o gerador gera a implementação do
método partial , que normalmente é mais rápido
em tempo de execução do que as soluções de log
existentes. Para saber mais, confira Geração de
origem de registro em log em tempo de compilação.

Novas APIs LINQ


Vários métodos LINQ foram adicionados ao .NET 6. A maioria dos métodos novos
listados na tabela a seguir tem métodos equivalentes ao tipo System.Linq.Queryable.

Método Descrição

Enumerable.TryGetNonEnumeratedCount<TSource> Tenta determinar o número de elementos


(IEnumerable<TSource>, Int32) em uma sequência sem forçar uma
enumeração.

Enumerable.Chunk<TSource> Divide os elementos de uma sequência em


(IEnumerable<TSource>, Int32) partes com tamanho especificado.

Enumerable.MaxBy e Enumerable.MinBy Localiza elementos máximos ou mínimos


usando um seletor de chave.
Método Descrição

Enumerable.DistinctBy, Enumerable.ExceptBy, Essas novas variações de métodos que


Enumerable.IntersectBy e Enumerable.UnionBy executam operações baseadas em
conjunto permitem que você especifique a
igualdade usando uma função seletora de
chave.

Enumerable.ElementAt<TSource> Aceita índices contados desde o início ou


(IEnumerable<TSource>, Index) e final da sequência, por exemplo,
Enumerable.ElementAtOrDefault<TSource> Enumerable.Range(1, 10).ElementAt(^2)
(IEnumerable<TSource>, Index) retorna 9 .

Enumerable.FirstOrDefault<TSource> Novas sobrecargas permitem que você


(IEnumerable<TSource>, TSource) e especifique um valor padrão a ser usado se
Enumerable.FirstOrDefault<TSource> a sequência estiver vazia.
(IEnumerable<TSource>, Func<TSource,Boolean>,
TSource)
Enumerable.LastOrDefault<TSource>
(IEnumerable<TSource>, TSource) e
Enumerable.LastOrDefault<TSource>
(IEnumerable<TSource>, Func<TSource,Boolean>,
TSource)
Enumerable.SingleOrDefault<TSource>
(IEnumerable<TSource>, TSource) e
Enumerable.SingleOrDefault<TSource>
(IEnumerable<TSource>, Func<TSource,Boolean>,
TSource)

Enumerable.Max<TSource> Novas sobrecargas permitem que você


(IEnumerable<TSource>, IComparer<TSource>) e especifique um comparador.
Enumerable.Min<TSource>(IEnumerable<TSource>,
IComparer<TSource>)

Enumerable.Take<TSource> Aceita um argumento Range para


(IEnumerable<TSource>, Range) simplificar a tomada de uma fatia de uma
sequência. Por exemplo, você pode usar
source.Take(2..7) em vez de
source.Take(7).Skip(2) .

Enumerable.Zip<TFirst,TSecond,TThird> Produz uma sequência de tuplas com


(IEnumerable<TFirst>, IEnumerable<TSecond>, elementos de três sequências
IEnumerable<TThird>) especificadas.

Melhorias de data, hora e fuso horário


Os dois structs a seguir foram adicionados ao .NET 6: System.DateOnly e
System.TimeOnly. Eles representam a parte de data e a parte de hora de um DateTime,
respectivamente. DateOnly é útil para aniversários e aniversários de casamento, e
TimeOnly é útil para alarmes diários e horário comercial semanal.

Agora, você pode usar as IDs de IANA (Autoridade de Números Atribuídos à Internet)
ou do fuso horário do Windows em qualquer sistema operacional que tenha dados de
fuso horário instalados. O método TimeZoneInfo.FindSystemTimeZoneById(String) foi
atualizado para converter automaticamente sua entrada de um fuso horário do
Windows em um fuso horário IANA (ou vice-versa), se o fuso horário solicitado não for
encontrado no sistema. Além disso, os novos métodos
TryConvertIanaIdToWindowsId(String, String) e TryConvertWindowsIdToIanaId foram
adicionados para cenários em que você ainda precisa converter manualmente de um
formato de fuso horário para outro.

Também há algumas outras melhorias de fuso horário. Para saber mais, confira
Aprimoramentos de data, hora e fuso horário no .NET 6 .

Classe PriorityQueue
A nova classe PriorityQueue<TElement,TPriority> representa uma coleção de itens que
têm um valor e uma prioridade. Os itens são removidos da fila em ordem de prioridade
crescente, ou seja, o item com o valor de prioridade mais baixo é removido da fila
primeiro. Essa classe implementa uma estrutura de dados de heap mínimo .

Confira também
Novidades no C# 10
Novidades no F# 6
Novidades no EF Core 6
Novidades no ASP.NET Core 6
Notas sobre a versão de .NET 6
Notas sobre a versão do Visual Studio 2022
Blog: anúncio do .NET 6
Blog: experimente o novo gerador de origem System.Text.Json
Alterações interruptivas no .NET 6
Artigo • 11/03/2024

Se você estiver migrando um aplicativo para o .NET 6, as alterações interruptivas listadas


aqui poderão afetar você. As alterações são agrupadas por área de tecnologia, como
ASP.NET Core ou Windows Forms.

Este artigo indica se cada alteração interruptiva é compatível com binários ou compatível
com o código-fonte:

Compatível com binários – Os binários existentes serão carregados e executados


com êxito sem recompilação e o comportamento em tempo de execução não será
alterado.
Compatível com o código-fonte – O código-fonte será compilado com êxito sem
alterações durante o redirecionamento ao novo runtime ou quando um novo SDK
ou componente for usado.

ASP.NET Core
ノ Expandir a tabela

Título Compatível com Compatível com


binários a origem

ActionResult<T> define StatusCode para 200 ✔️ ❌

O método AddDataAnnotationsValidation ficou obsoleto ✔️ ❌

Assemblies removidos da estrutura compartilhada do ❌ ✔️


Microsoft.AspNetCore.App

Blazor: nome do parâmetro alterado no método ✔️ ❌


RequestImageFileAsync

Blazor: WebEventDescriptor.EventArgsType property ❌ ❌


replaced

Blazor: Byte array interop ✔️ ❌

Changed MessagePack library in @microsoft/signalr- ❌ ✔️


protocol-msgpack

A propriedade ClientCertificate não dispara mais a ✔️ ❌


renegociação para HttpSys
Título Compatível com Compatível com
binários a origem

Os metadados de EndpointName não são definidos ✔️ ❌


automaticamente

Identidade: a versão de inicialização padrão da interface ❌ ❌


do usuário foi alterada

Kestrel: atributos de mensagem de log alterados ✔️ ❌

Microsoft.AspNetCore.Http.Features split ❌ ✔️

Middleware: middleware de redirecionamento HTTPS ✔️ ❌


gera exceção em portas HTTPS ambíguas

Middleware: sobrecarga de novo uso ✔️ ❌

Renomeações mínimas de API no RC 1 ❌ ❌

Renomeações mínimas de API no RC 2 ❌ ❌

O MVC não armazenará em buffer os tipos ✔️ ❌


IAsyncEnumerable ao usar System.Text.Json

As anotações de tipo de referência anuláveis foram ✔️ ❌


alteradas

APIs ficaram obsoletas e foram removidas ✔️ ❌

O PreserveCompilationContext não é configurado por ❌ ✔️


padrão

Razor: O compilador não produz mais um assembly ✔️ ❌


Views

Razor: alterações de ID de registro em log ❌ ✔️

Razor: RazorAPIs do mecanismo marcadas como ✔️ ❌


obsoletas

SignalR: cliente Java atualizado para o RxJava3 ❌ ✔️

Os métodos TryParse e BindAsync são validados ❌ ❌

Contêineres
ノ Expandir a tabela
Title Compatível com Compatível com a
binários origem

Formatação padrão do agente de console em ✔️ ❌


imagens de contêiner

Para obter informações sobre outras alterações significativas para contêineres no .NET 6,
consulte Notas de versão do contêiner do .NET 6 .

Bibliotecas principais do .NET


ノ Expandir a tabela

Título Compatível com Compatível com


binários a origem

Obsolescências de API com IDs de diagnóstico não ✔️ ❌


padrão

Alterações em anotações de tipo de referência ✔️ ❌


anuláveis

Avaliação de cadeia de caracteres condicional em ✔️ ❌


métodos de depuração

Comportamento de environment.ProcessorCount no ✔️ ❌
Windows

Comportamento de retorno de chamada do ✔️ ✔️


EventSource

File.Replace no Unix gera exceções para corresponder ✔️ ❌


ao Windows

FileStream bloqueia arquivos com bloqueio ❌ ✔️


compartilhado no Unix

FileStream não sincroniza mais o deslocamento de ❌ ❌


arquivo com o sistema operacional

Atualizações de FileStream.Position após a conclusão ❌ ❌


de ReadAsync ou WriteAsync

Novas IDs de diagnóstico para APIs obsoletas ✔️ ❌

Novas sobrecargas de método System.Linq.Queryable ✔️ ❌

Versões mais antigas da estrutura removidas do pacote ❌ ✔️


Título Compatível com Compatível com
binários a origem

Nomes de parâmetros alterados ✔️ ❌

Nomes de parâmetros em tipos derivados de fluxo ✔️ ❌

Leituras parciais e de bytes zero no DeflateStream, ✔️ ❌


GZipStream e CryptoStream

Definir o carimbo de data/hora no arquivo somente ❌ ✔️


leitura no Windows

Precisão da análise de formato numérico padrão ✔️ ❌

Membros abstratos estáticos em interfaces ❌ ✔️

Sobrecargas de StringBuilder.Append e ordem de ❌ ✔️


avaliação

APIs de nome forte geram ❌ ✔️


PlatformNotSupportedException

System.Drawing.Common só é compatível com o ❌ ❌


Windows

System.Security.SecurityContext está marcado como ✔️ ❌


obsoleto

Task.FromResult pode retornar singleton ❌ ✔️

Exceções sem tratamento de um BackgroundService ✔️ ❌

Criptografia
ノ Expandir a tabela

Título Compatível com Compatível com a


binários origem

Métodos CreateEncryptor geram exceção de ❌ ✔️


tamanho incorreto de comentários

Implantação
ノ Expandir a tabela
Título Compatível com Compatível com a
binários origem

Caminho do host x86 no Windows 64 ✔️ ✔️


bits

Entity Framework Core


Alterações interruptivas no EF Core 6

Extensões
ノ Expandir a tabela

Título Compatível com Compatível com a


binários origem

AddProvider verifica se há um provedor não ✔️ ❌


nulo

FileConfigurationProvider.Load gera ✔️ ❌
InvalidDataException

Elementos XML repetidos incluem índice ❌ ✔️

A resolução de ServiceProvider descartado gera ✔️ ❌


exceção

Globalização
ノ Expandir a tabela

Title Compatível com Compatível com a


binários origem

Criação de cultura e mapeamento de maiúsculas e


minúsculas no modo invariável de globalização

Interoperabilidade
ノ Expandir a tabela
Title Compatível com Compatível com a
binários origem

Membros abstratos estáticos em ❌ ✔️


interfaces

Compilador JIT
ノ Expandir a tabela

Title Compatível com Compatível com a


binários origem

CCoagir argumentos de chamada de acordo ✔️ ✔️


com o ECMA-335

Rede
ノ Expandir a tabela

Título Compatível com Compatível com a


binários origem

Porta removida do SPN para Kerberos e ❌ ✔️


Negotiate

WebRequest, WebClient e ServicePoint estão ✔️ ❌


obsoletos

.
ノ Expandir a tabela

Title Compatível com Compatível com


binários a origem

-p opção para dotnet run preterida ✔️ ❌

O código C# em modelos sem suporte em versões ✔️ ✔️


anteriores

Arquivos EditorConfig incluídos implicitamente ✔️ ❌

Gerar apphost para macOS ✔️ ❌


Title Compatível com Compatível com
binários a origem

Gerar erro para arquivos duplicados na saída de ❌ ✔️


publicação

GetTargetFrameworkProperties e ❌ ✔️
GetNearestTargetFramework removidos do protocolo
ProjectReference

Instalar o local para x64 emulado no ARM64 ✔️ ❌

O MSBuild não dá mais suporte à chamada de GetType()

.NET não pode ser instalado em um local personalizado ✔️ ✔️

OutputType não é definido automaticamente como ✔️ ❌


WinExe

A publicação de ReadyToRun com --no-restore requer ✔️ ❌


alterações

Arquivo runtimeconfig.dev.json não gerado ❌ ✔️

Aviso de RuntimeIdentifier quando autossuficiente não é ✔️ ❌


especificado

A ferramenta se manifesta na pasta raiz ✔️ ✔️

Requisitos de versão do SDK do .NET 6 ✔️ ✔️

O arquivo .version inclui a versão de build ✔️ ✔️

Gravar assemblies de referência em ❌ ✔️


IntermediateOutputPath

Serialização
ノ Expandir a tabela

Título Compatível com Compatível com a


binários origem

DataContractSerializer retém o sinal ao ❌ ✔️


desserializar -0

Formato de serialização padrão para TimeSpan ❌ ✔️

Serialização IAsyncEnumerable ✔️ ❌
Título Compatível com Compatível com a
binários origem

Refatoração da API de geração de origem JSON ❌ ✔️

JsonNumberHandlingAttribute nas propriedades ❌ ✔️


da coleção

Novas sobrecargas do gerador de origem ❌ ✔️


JsonSerializer

Windows Forms
ノ Expandir a tabela

Título Compatível com Compatível com


binários a origem

Os modelos C# usam inicialização de aplicativo ✔️ ❌

As propriedades TableLayoutSettings selecionadas ❌ ✔️


geram InvalidEnumArgumentException

APIs relacionadas a DataGridView agora geram ❌ ✔️


InvalidOperationException

Os métodos ListViewGroupCollection geram uma nova ❌ ✔️


InvalidOperationException

Tamanho máximo de texto aumentado para ❌ ✔️


NotifyIcon.Text

ScaleControl chamado somente quando necessário ✔️ ❌

Algumas APIs geram ArgumentNullException ❌ ✔️

O TreeNodeCollection.Item gera uma exceção quando o ❌ ✔️


nó é atribuído em outro lugar

XML e XSLT
ノ Expandir a tabela
Título Compatível com Compatível com a
binários origem

Comportamento de XNodeReader.GetAttribute ✔️ ❌
para índice inválido

Confira também
Alterações interruptivas do compilador C# após C# 9
Alterações interruptivas do compilador C# no C# 10
Novidades no .NET 6

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Novidades do .NET 5
Artigo • 10/05/2023

O .NET 5 é a próxima versão principal do .NET Core a seguir 3,1. Nomeamos essa nova
versão do .NET 5 em vez do .NET Core 4 por dois motivos:

Pulamos os números de versão 4.x para evitar confusão com .NET Framework 4.x.
Tiramos "Core" do nome para enfatizar que essa é a implementação principal do
.NET daqui para frente. O .NET 5 dá suporte a mais tipos de aplicativos e mais
plataformas do que .NET Core ou .NET Framework.

ASP.NET Core 5.0 é baseado no .NET 5, mas mantém o nome "Core" para evitar
confundi-lo com ASP.NET MVC 5. Da mesma forma, o Entity Framework Core 5.0
mantém o nome "Core" para evitar confundi-lo com o Entity Framework 5 e 6.

O .NET 5 inclui os seguintes aprimoramentos e novos recursos em comparação com o


.NET Core 3.1:

Atualizações de C#
Atualizações de F#
Atualizações de Visual Basic
Novos recursos de System.Text.Json
Aplicativos de arquivo único
Corte de aplicativo
Intrínsecos do Windows Arm64 e Arm64
Suporte a ferramentas de depuração de despejo
As bibliotecas de runtime são 80% anotadas para tipos de referência anuláveis
Aprimoramentos de desempenho:
Coleta de lixo (GC)
System.Text.Json
System.Text.RegularExpressions
Pooling de ValueTask assíncrono
Otimizações de tamanho de contêiner
Muito mais áreas

O .NET 5 não substitui .NET Framework


O .NET 5 e versões posteriores são a implementação principal do .NET daqui para frente,
mas .NET Framework 4.x ainda tem suporte. Não há planos para portar as seguintes
tecnologias de .NET Framework para o .NET 5, mas há alternativas no .NET:
Tecnologia Alternativa recomendada

Web Forms ASP.NET Core Blazor ou Razor Pages

Fluxo de trabalho do Windows (WF) Elsa-Workflows

Windows Communication Foundation


A implementação original do WCF (Windows Communication Foundation) só tinha
suporte no Windows. No entanto, há uma porta de cliente disponível no .NET
Foundation. Ele é totalmente de código aberto , multiplataforma e compatível com a
Microsoft. Os principais pacotes NuGet estão listados abaixo:

System.ServiceModel.Duplex
System.ServiceModel.Federation
System.ServiceModel.Http
System.ServiceModel.NetTcp
System.ServiceModel.Primitives
{1&gt;System.ServiceModel.Security&lt;1}

Os componentes do servidor que complementam as bibliotecas de clientes


mencionadas acima estão disponíveis por meio do CoreWCF . A partir de abril de
2022, o CoreWCF tem suporte oficial da Microsoft. No entanto, para uma alternativa ao
WCF, considere gRPC.

O .NET 5 não substitui .NET Standard


O novo desenvolvimento de aplicativos pode especificar o TFM (Moniker da Estrutura
de Destino) net5.0 para todos os tipos de projeto, incluindo bibliotecas de classes. O
compartilhamento de código entre cargas de trabalho do .NET 5 é simplificado porque
tudo o que você precisa é do net5.0 TFM.

Para aplicativos e bibliotecas do .NET 5, o net5.0 TFM combina e substitui os TFMs


netcoreapp e netstandard . No entanto, se você planeja compartilhar código entre
cargas de trabalho de .NET Framework, .NET Core e .NET 5, poderá fazer isso
especificando netstandard2.0 como seu TFM. Para obter mais informações, confira .NET
Standard.

Atualizações de C#
Os desenvolvedores de aplicativos .NET 5 terão acesso à versão e aos recursos mais
recentes do C#. O .NET 5 é combinado com o C# 9, o que traz muitos novos recursos
para a linguagem. Veja aqui alguns destaques:

Registros: tipos de referência com semântica de igualdade baseada em valor e


mutação não destrutiva suportada por uma nova expressão with .

Correspondência de padrões relacionais: estende os recursos de correspondência


de padrões para operadores relacionais para avaliações e expressões
comparativas, incluindo padrões lógicos - novas palavras-chave and , or e not .

Instruções de nível superior: como um meio de acelerar a adoção e o aprendizado


de C#, o método Main pode ser omitido e o aplicativo tão simples quanto o
seguinte é válido:

C#

System.Console.Write("Hello world!");

Ponteiros de função: construções de linguagem que expõem os seguintes


opcodes de IL (linguagem intermediária): ldftn e calli .

Para obter mais informações sobre os recursos disponíveis do C# 9, consulte Novidades


no C# 9.

Geradores de origem
Além de alguns dos novos recursos em C# realçados, os geradores de origem estão
entrando em projetos de desenvolvedor. Os geradores de origem permitem que o
código executado durante a compilação inspecione seu programa e produza arquivos
adicionais que são compilados junto com o restante do código.

Para obter mais informações sobre geradores de origem, consulte Introdução a


geradores de origem C# e Amostras de gerador de origem C# .

Atualizações de F#
F# é a linguagem de programação funcional .NET e, com o .NET 5, os desenvolvedores
têm acesso ao F# 5. Um dos novos recursos são cadeias de caracteres interpoladas,
semelhantes à cadeia de caracteres interpolada em C# e até mesmo JavaScript.

F#
let name = "David"
let age = 36
let message = $"{name} is {age} years old."

Além da interpolação de cadeia de caracteres básica, há interpolação por tipo. Com a


interpolação por tipo, um determinado tipo deve corresponder ao especificador de
formato.

F#

let name = "David"


let age = 36
let message = $"%s{name} is %d{age} years old."

Esse formato é semelhante à função sprintf que formata uma cadeia de caracteres
com base em entradas fortemente tipadas.

Para saber mais, consulte Novidades do F# 5.

Atualizações de Visual Basic


Não há novos recursos de linguagem para o Visual Basic no .NET 5. No entanto, com o
.NET 5, o suporte ao Visual Basic é estendido para:

Descrição Parâmetro dotnet new

Aplicativo do Console console

Biblioteca de classes classlib

Aplicativo WPF wpf

Biblioteca de classes do WPF wpflib

Biblioteca de Controles Personalizados do WPF wpfcustomcontrollib

Biblioteca de controle de usuário WPF wpfusercontrollib

Aplicativo Windows Forms (WinForms) winforms

Biblioteca de Classes Windows Forms (WinForms) winformslib

Projeto de Teste de Unidade mstest

Projeto de Teste do NUnit 3 nunit

Item de Teste do NUnit 3 nunit-test


Descrição Parâmetro dotnet new

Projeto de Teste xUnit xunit

Para obter mais informações sobre modelos de projeto da .NET CLI, consulte dotnet
new.

Novos recursos de System.Text.Json


Há novos recursos dentro e para System.Text.Json:

Preservar referências e manipular referências circulares


Métodos de extensão HttpClient e HttpContent
Permitir ou gravar números entre aspas
Suporte a tipos imutáveis e registros C# 9
Suporte a acessadores de propriedade não pública
Campos de suporte
Ignorar propriedades condicionalmente
Dar suporte a dicionários sem chave de cadeia de caracteres
Permitir que conversores personalizados manipulem nulo
Copiar JsonSerializerOptions
Criar JsonSerializerOptions com padrões da Web

Confira também
A jornada para um .NET
Aprimoramentos de desempenho no .NET 5
Baixar o SDK do .NET
Alterações interruptivas no .NET 5
Artigo • 18/03/2023

Se você estiver migrando um aplicativo para o .NET 5, as alterações interruptivas listadas


aqui poderão afetar você. As alterações são agrupadas por área de tecnologia, como
ASP.NET Core ou criptografia.

Este artigo indica se cada alteração interruptiva é compatível com binários ou compatível
com o código-fonte:

Compatível com binários – Os binários existentes serão carregados e executados


com êxito sem recompilação e o comportamento em tempo de execução não será
alterado.
Compatível com o código-fonte – O código-fonte será compilado com êxito sem
alterações durante o redirecionamento ao novo runtime ou quando um novo SDK
ou componente for usado.

ASP.NET Core
Title Compatível Compatível
com binários com a origem

Os aplicativos ASP.NET Core desserializam números entre ✔️ ❌


aspas

APIs do AzureAD.UI e do AzureADB2C.UI obsoletas ✔️ ❌

Os métodos de serialização BinaryFormatter estão obsoletos ✔️ ❌

O recurso no roteamento de ponto de extremidade é ✔️ ❌


HttpContext

Pacotes de integração do Azure prefixados com Microsoft ❌ ✔️


removidos

Blazor: Lógica de precedência de rota alterada em ✔️ ❌


aplicativos Blazor

Blazor: suporte a navegadores atualizado ✔️ ✔️

Blazor: espaço em branco não significativo cortado pelo ✔️ ❌


compilador

Blazor: os tipos JSObjectReference e ✔️ ❌


JSInProcessObjectReference são internos
Title Compatível Compatível
com binários com a origem

Blazor: estrutura de destino de pacotes NuGet alterada ❌ ✔️

Blazor: o recurso ProtectedBrowserStorage foi movido para a ✔️ ❌


estrutura compartilhada

Blazor: os campos públicos somente leitura ❌ ✔️


RenderTreeFrame agora são propriedades

Blazor: lógica de validação atualizada para ativos da Web ❌ ✔️


estáticos

APIs de criptografia sem suporte no navegador ❌ ✔️

Extensões: alterações de referência de pacote ❌ ✔️

Os tipos Kestrel e IIS BadHttpRequestException estão ✔️ ❌


obsoletos

Instâncias de HttpClient criadas por códigos de status de ✔️ ❌


inteiro de log IHttpClientFactory

HttpSys: renegociação de certificado do cliente desabilitada ✔️ ❌


por padrão

IIS: as cadeias de caracteres de consulta de middleware ✔️ ❌


UrlRewrite são preservadas

Kestrel: alterações de configuração detectadas por padrão ✔️ ❌

Kestrel: versões do protocolo TLS com suporte padrão ✔️ ❌


alteradas

Kestrel: HTTP/2 desabilitado via TLS em versões ✔️ ✔️


incompatíveis do Windows

Kestrel: transporte do Libuv marcado como obsoleto ✔️ ❌

Propriedades obsoletas em ConsoleLoggerOptions ✔️ ❌

Classe ResourceManagerWithCultureStringLocalizer e ✔️ ❌
membro de interface WithCulture removidos

APIs do Pubternal removidas ✔️ ❌

Construtor obsoleto removido no middleware de localização ✔️ ❌


de solicitação

Middleware: página de erro do banco de dados marcada ✔️ ❌


como obsoleta
Title Compatível Compatível
com binários com a origem

O middleware do manipulador de exceção gera uma ✔️ ✔️


exceção original

ObjectModelValidator chama uma nova sobrecarga de ✔️ ❌


Validate

Codificação de nome de cookie removida ✔️ ❌

Versões de pacote NuGet IdentityModel atualizadas ❌ ✔️

SignalR: tipo de opções de protocolo do hub MessagePack ✔️ ❌


alterado

SignalR: protocolo do hub MessagePack removido ✔️ ❌

Métodos UseSignalR e UseConnections removidos ✔️ ❌

Tipo de conteúdo CSV alterado para em conformidade com ✔️ ❌


padrões

Análise de código
Title Compatível com binários Compatível com a origem

Aviso CA1416 ✔️ ❌

Aviso CA1417 ✔️ ❌

Aviso CA1831 ✔️ ❌

Aviso CA2013 ✔️ ❌

Aviso CA2014 ✔️ ❌

Aviso CA2015 ✔️ ❌

Aviso CA2200 ✔️ ❌

Aviso CA2247 ✔️ ❌

Bibliotecas principais do .NET


Title Compatível com Compatível com a
binários origem
Title Compatível com Compatível com a
binários origem

Alterações de APIs relacionadas a assembly para ❌ ✔️


publicação de arquivo único

Os métodos de serialização BinaryFormatter estão ✔️ ❌


obsoletos

As APIs de segurança de acesso do código estão ✔️ ❌


obsoletas

CreateCounterSetInstance gera ✔️ ❌
InvalidOperationException

O ActivityIdFormat padrão é W3C ❌ ✔️

Environment.OSVersion retorna a versão correta ❌ ✔️

O valor de FrameworkDescription é .NET, não .NET ✔️ ❌


Core

As APIs do GAC estão obsoletas ✔️ ❌

Verificações de IsSupported de intrínsecos de ❌ ✔️


hardware

IntPtr e UIntPtr implementam IFormattable ✔️ ❌

LastIndexOf lida com cadeias de caracteres de ❌ ✔️


pesquisa vazias

Caminhos de URI com caracteres não ASCII no UNIX ❌ ✔️

Obsolescências de API com IDs de diagnóstico não ✔️ ❌


padrão

Propriedades obsoletas em ConsoleLoggerOptions ✔️ ❌

Complexidade de OrderBy.First da LINQ ❌ ✔️

Atributos OSPlatform renomeados ou removidos ✔️ ❌

Pacote Microsoft.DotNet.PlatformAbstractions ❌ ✔️
removido

PrincipalPermissionAttribute está obsoleto ✔️ ❌

Alterações de nome de parâmetro de versões prévias ✔️ ❌

Alterações de nome de parâmetro em assemblies de ✔️ ❌


referência
Title Compatível com Compatível com a
binários origem

As APIs de comunicação remota estão obsoletas ❌ ✔️

A ordem da lista de Activity.Tags está invertida ✔️ ❌

Os métodos de comparação SSE e SSE2 manipulam ✔️ ❌


NaN

Thread.Abort está obsoleto ✔️ ❌

Reconhecimento de URI de caminhos UNC no UNIX ❌ ✔️

Os caminhos do código UTF-7 estão obsoletos ✔️ ❌

Alteração de comportamento para Vector2.Lerp e ✔️ ❌


Vector4.Lerp

Vector<T> lança NotSupportedException ❌ ✔️

Criptografia
Title Compatível com Compatível com a
binários origem

APIs de criptografia sem suporte no navegador ❌ ✔️

Cryptography.OID é somente inicialização ✔️ ❌

Conjuntos de criptografia TLS padrão no Linux ❌ ✔️

As sobrecargas Create() em abstrações ✔️ ❌


criptográficas estão obsoletas

Valor de FeedbackSize padrão alterado ✔️ ❌

Entity Framework Core


Alterações interruptivas no EF Core 5.0

Globalização
Title Compatível com Compatível com a
binários origem
Title Compatível com Compatível com a
binários origem

Usar bibliotecas ICU no Windows ❌ ✔️

StringInfo e TextElementEnumerator são ❌ ✔️


compatíveis com UAX29

Categoria Unicode alterada para caracteres Latin-1 ✔️ ❌

Valores TextInfo.ListSeparator alterados ✔️ ❌

Interoperabilidade
Title Compatível com Compatível com a
binários origem

O suporte para WinRT foi removido ❌ ✔️

A conversão de RCW em InterfaceIsIInspectable ❌ ✔️


gera exceção

Nenhuma investigação de sufixo A/W em ❌ ✔️


plataformas não Windows

Rede
Title Compatível com Compatível com a
binários origem

A manipulação de caminho de cookie está em ✔️ ❌


conformidade com a RFC 6265

LocalEndPoint é atualizado após chamar SendToAsync ✔️ ❌

MulticastOption.Group não aceita nulo ✔️ ❌

Fluxos permitem operações Begin sucessivas ❌ ✔️

WinHttpHandler removido do runtime do .NET ❌ ✔️

.
Title Compatível Compatível com
com binários a origem
Title Compatível Compatível com
com binários a origem

Arquivos Directory.Packages.prop importados por padrão ❌ ✔️

Erro gerado quando o projeto executável referencia ✔️


executável incompatível

FrameworkReference substituído pelo ✔️ ❌


WindowsSdkPackageVersion para o SDK do Windows

Símbolo de pré-processador de NETCOREAPP3_1 não ✔️ ❌


definido

OutputType definido como WinExe ❌ ✔️

Alteração de comportamento de PublishDepsFilePath ❌ ✔️

Alteração de TargetFramework de netcoreapp para net ❌ ✔️

Os aplicativos WinForms e WPF usam Microsoft.NET.Sdk ❌ ✔️

Segurança
Título Compatível com Compatível com a
binários origem

As APIs de segurança de acesso do código estão ✔️ ❌


obsoletas

PrincipalPermissionAttribute está obsoleto ✔️ ❌

Os caminhos do código UTF-7 estão obsoletos ✔️ ❌

Serialização
Title Compatível com Compatível com a
binários origem

BinaryFormatter.Deserialize reencapsula exceções ✔️ ❌

JsonSerializer.Desserialize requer cadeia de caracteres ✔️ ❌


de caractere único

Os aplicativos ASP.NET Core desserializam números ✔️ ❌


entre aspas

JsonSerializer.Serialize gera ArgumentNullException ✔️ ❌


Title Compatível com Compatível com a
binários origem

Construtores não públicos sem parâmetros não ✔️ ❌


usados para desserialização

As opções são respeitadas ao serializar pares chave- ✔️ ❌


valor

Windows Forms
Title Compatível com Compatível com a
binários origem

O código nativo não pode acessar objetos ✔️ ❌


Windows Forms

OutputType definido como WinExe ❌ ✔️

DataGridView não redefine fontes ✔️ ❌


personalizadas

Os métodos geram ArgumentException ✔️ ❌

Os métodos geram ArgumentNullException ✔️ ❌

As propriedades geram ✔️ ❌
ArgumentOutOfRangeException

TextFormatFlags.ModifyString está obsoleto ✔️ ❌

As APIs do DataGridView geram ✔️ ❌


InvalidOperationException

Os aplicativos WinForms usam ❌ ✔️


Microsoft.NET.Sdk

Controles de barra de status removidos ✔️ ❌

WPF
Title Compatível com Compatível com a
binários origem

OutputType definido como WinExe ❌ ✔️


Title Compatível com Compatível com a
binários origem

Aplicativos WPF usam ❌ ✔️


Microsoft.NET.Sdk

Confira também
Novidades do .NET 5
Novidades do .NET Core 3.1
Artigo • 10/05/2023

Este artigo descreve as novidades do .NET Core 3.1. Esta versão contém pequenas
melhorias no .NET Core 3.0, com foco em correções pequenas, mas importantes. O
recurso mais importante sobre o .NET Core 3.1 é que ele é uma versão LTS (suporte de
longo prazo).

Se você estiver usando o Visual Studio 2019, precisará atualizar para o Visual Studio
2019 versão 16.4 ou posterior para trabalhar com projetos do .NET Core 3.1. Para
saber informações sobre as novidades no Visual Studio versão 16.4, confira Novidades
no Visual Studio 2019 versão 16.4.

O Visual Studio para Mac também é compatível e inclui o .NET Core 3.1 no Visual Studio
para Mac 8.4.

Para obter mais informações sobre a versão, consulte o comunicado do .NET Core 3.1 .

Baixe e comece a usar o .NET Core 3.1 no Windows, macOS ou Linux.

Suporte de longo prazo


O .NET Core 3.1 é uma versão LTS com suporte da Microsoft por três anos após seu
lançamento. Recomendamos que seus aplicativos sejam transferidos para a versão mais
recente do LTS. Confira a página de política de suporte do .NET e do .NET Core para
obter uma lista de versões com suporte.

Versão Data de fim da vida útil

.NET Core 3.1 Fim da vida útil em 13 de dezembro de 2022.

.NET Core 3.0 Fim da vida útil em 3 de março de 2020.

.NET Core 2.2 Fim da vida útil em 23 de dezembro de 2019.

.NET Core 2.1 Fim da vida útil em 21 de agosto de 2021.

Para obter mais informações, consulte a política de suporte do .NET e do .NET Core .

appHost e notarização no macOS


Somente macOS
A partir do SDK do .NET Core 3.1 notorizado para macOS, a configuração appHost é
desabilitada por padrão. Para obter mais informações, consulte Notarização do macOS
Catalina e o impacto nos downloads e projetos do .NET Core.

Quando a configuração appHost está habilitada, o .NET Core gera um executável Mach-
O nativo quando você cria ou publica. Seu aplicativo é executado no contexto do
appHost quando ele é executado do código-fonte com o comando dotnet run ou
iniciando o executável Mach-O diretamente.

Sem o appHost, a única maneira de um usuário iniciar um aplicativo dependente de


estrutura é com o comando dotnet <filename.dll> . Um appHost sempre é criado
quando você publica seu aplicativo autocontido.

Você pode configurar o appHost no nível do projeto ou alternar o appHost para um


comando dotnet específico com o parâmetro -p:UseAppHost :

Arquivo de projeto

XML

<PropertyGroup>
<UseAppHost>true</UseAppHost>
</PropertyGroup>

Parâmetro de linha de comando

CLI do .NET

dotnet run -p:UseAppHost=true

Para obter mais informações sobre a configuração UseAppHost , consulte as propriedades


do MSBuild para Microsoft.NET.Sdk.

Windows Forms
Somente Windows

2 Aviso

Há alterações interruptivas no Windows Forms.


Controles herdados foram incluídos em Windows Forms indisponíveis na Caixa de
Ferramentas do Visual Studio Designer há algum tempo. Eles foram substituídos por
novos controles novamente no .NET Framework 2.0. Eles foram removidos do SDK da
Área de Trabalho para o .NET Core 3.1.

Controle removido Substituição recomendada APIs associadas removidas

DataGrid DataGridView DataGridCell


DataGridRow
DataGridTableCollection
DataGridColumnCollection
DataGridTableStyle
DataGridColumnStyle
DataGridLineStyle
DataGridParentRowsLabel
DataGridParentRowsLabelStyle
DataGridBoolColumn
DataGridTextBox
GridColumnStylesCollection
GridTableStylesCollection
HitTestType

ToolBar ToolStrip ToolBarAppearance

ToolBarButton ToolStripButton ToolBarButtonClickEventArgs


ToolBarButtonClickEventHandler
ToolBarButtonStyle
ToolBarTextAlign

ContextMenu ContextMenuStrip

Menu ToolStripDropDown MenuItemCollection


ToolStripDropDownMenu

MainMenu MenuStrip

MenuItem ToolStripMenuItem

Recomendamos atualizar os aplicativos para o .NET Core 3.1 e passar para os controles
de substituição. Substituir os controles é um processo simples, em que basta “encontrar
e substituir” no tipo.

C++/CLI
Somente Windows
Adicionamos a possibilidade de criação de projetos C++/CLI (também conhecidos como
"C++ gerenciados"). Os binários produzidos a partir desses projetos são compatíveis
com o .NET Core 3.0 e versões posteriores.

Para adicionar suporte para C++/CLI no Visual Studio 2019 versão 16.4, instale a carga
de trabalho de desenvolvimento da área de trabalho com C++. Essa carga de trabalho
adiciona dois modelos ao Visual Studio:

Biblioteca de Classes (.NET Core)


Projeto CLR Vazio (.NET Core)

Próximas etapas
Examinar as alterações interruptivas entre o .NET Core 3.0 e o 3.1.
Examinar as alterações interruptivas no .NET Core 3.1 para aplicativos do Windows
Forms.
Alterações interruptivas no .NET Core
3.1
Artigo • 28/07/2023

Se você estiver migrando para a versão 3.1 do .NET Core ou do ASP.NET Core, as
alterações interruptivas listadas neste artigo poderão afetar seu aplicativo.

ASP.NET Core

HTTP: as alterações do SameSite do navegador afetam a


autenticação
Alguns navegadores, como Chrome e Firefox, fizeram alterações interruptivas nas
respectivas implementações de SameSite para cookies. As alterações afetam cenários de
autenticação remota, como OpenID Connect e Web Services Federation, que precisam
fazer a recusa enviando SameSite=None . No entanto, SameSite=None falha no iOS 12 e em
algumas versões mais antigas de outros navegadores. O aplicativo precisa detectar
essas versões e omitir SameSite .

Para conferir a discussão sobre esse problema, veja dotnet/aspnetcore#14996 .

Versão introduzida

3.1 versão prévia 1

Comportamento antigo
SameSite é uma extensão padrão de rascunho de 2016 para cookies HTTP. Seu objetivo

é atenuar a CSRF (solicitação intersite forjada). Ela foi projetada originalmente como um
recurso que os servidores aceitariam por meio da adição de novos parâmetros. No
ASP.NET Core 2.0 foi adicionado o suporte inicial para SameSite .

Novo comportamento

O Google propôs um novo padrão de rascunho que não é compatível com versões
anteriores. O padrão altera o modo padrão para Lax e adiciona uma nova entrada None
para a recusa. Lax é suficiente para a maioria dos cookies de aplicativo, no entanto, ele
interrompe cenários entre sites, como o logon do OpenID Connect e do Web Services
Federation. A maioria dos logons do OAuth não é afetada devido a diferenças na forma
como a solicitação flui. O novo parâmetro None causa problemas de compatibilidade
com clientes que implementaram o padrão de rascunho anterior (por exemplo, o iOS
12). O Chrome 80 incluirá as alterações. Confira Atualizações do SameSite para a linha
do tempo de lançamento do produto Chrome.

O ASP.NET Core 3.1 foi atualizado para implementar o novo comportamento de


SameSite . A atualização redefine o comportamento de SameSiteMode.None para emitir
SameSite=None e adiciona o novo valor SameSiteMode.Unspecified para omitir o atributo

SameSite . Todas as APIs de cookie agora usam Unspecified como padrão, embora

alguns componentes que usam cookies definam valores mais específicos para seus
cenários, como a correlação do OpenID Connect e de cookies nonce.

Para outras alterações recentes nessa área, confira HTTP: alguns padrões de cookie do
SameSite foram alterados para None. No ASP.NET Core 3.0, a maioria dos padrões foi
alterada de SameSiteMode.Lax para SameSiteMode.None (mas ainda usando o padrão
anterior).

Motivo da alteração
Alterações de navegador e de especificação conforme a descrição no texto anterior.

Ação recomendada
Os aplicativos que interagem com sites remotos, como por meio de logon de terceiros,
precisam:

Testar esses cenários em vários navegadores.


Aplicar a mitigação de detecção de navegador de política de cookie discutida em
Suporte a navegadores mais antigos.

Para obter instruções de teste e de detecção de navegador, confira a seção a seguir.

Determinar se você foi afetado

Teste o aplicativo Web usando uma versão do cliente que pode aceitar o novo
comportamento. O Chrome, o Firefox e o Microsoft Edge Chromium têm novos
sinalizadores de recursos de aceitação que podem ser usados para teste. Verifique se o
aplicativo é compatível com versões de cliente mais antigas depois de aplicar os
patches, principalmente o Safari. Para obter mais informações, confira Suporte a
navegadores mais antigos.
Chrome

O Chrome 78 e posterior geram resultados de teste enganosos. Essas versões têm uma
mitigação temporária em vigor e permitem cookies com menos de dois minutos de
duração. Com os sinalizadores de teste apropriados habilitados, o Chrome 76 e 77
geram resultados mais precisos. Para testar o novo comportamento, habilite
chrome://flags/#same-site-by-default-cookies . O Chrome 75 e as versões anteriores

são relatados como falhando com a nova configuração None . Para obter mais
informações, confira Suporte a navegadores mais antigos.

O Google não disponibiliza versões mais antigas do Chrome. No entanto, você pode
baixar versões mais antigas do Chromium, o que será suficiente para o teste. Siga as
instruções em Baixar Chromium .

Chromium 76 Win64
Chromium 74 Win64

Safari

O Safari 12 implementou estritamente o rascunho anterior e falhará se o novo valor


None estiver em cookies. Isso precisa ser evitado por meio do código de detecção de

navegador mostrado em Suporte a navegadores mais antigos. Teste logons no estilo do


sistema operacional Safari 12 e 13, bem como baseados em WebKit usando a MSAL
(Biblioteca de Autenticação da Microsoft), a ADAL (Biblioteca de Autenticação do Active
Directory) ou qualquer biblioteca que você esteja usando. O problema depende da
versão subjacente do sistema operacional. Sabe-se que o OSX Mojave 10.14 e o iOS 12
têm problemas de compatibilidade com o novo comportamento. A atualização para o
OSX Catalina 10.15 ou iOS 13 corrige o problema. No momento, o Safari não tem um
sinalizador de aceitação para testar o novo comportamento de especificação.

Firefox

O suporte do Firefox ao novo padrão pode ser testado na versão 68 e posterior


aceitando-o na página about:config com o sinalizador de recurso
network.cookie.sameSite.laxByDefault . Nenhum problema de compatibilidade foi

relatado em versões mais antigas do Firefox.

Microsoft Edge

Embora o Microsoft Edge dê suporte ao padrão antigo SameSite , da versão 44 em


diante, ele não teve problemas de compatibilidade com o novo padrão.
Microsoft Edge Chromium

O sinalizador de recurso é edge://flags/#same-site-by-default-cookies . Nenhum


problema de compatibilidade foi observado em testes com o Microsoft Edge Chromium
78.

Electron

As versões do Electron incluem versões mais antigas do Chromium. Por exemplo, a


versão do Electron usada pelo Microsoft Teams é a Chromium 66, que exibe o
comportamento mais antigo. Execute seu próprio teste de compatibilidade com a
versão do Electron que o seu produto usa. Para obter mais informações, confira Suporte
a navegadores mais antigos.

Suporte a navegadores mais antigos

O padrão SameSite de 2016 determina que valores desconhecidos sejam tratados como
valores SameSite=Strict . Consequentemente, todos os navegadores mais antigos que
dão suporte ao padrão original podem ser interrompidos quando veem uma
propriedade SameSite com o valor None . A detecção de navegador precisa ser
implementada nos aplicativos Web quando o objetivo é dar suporte a esses
navegadores antigos. O ASP.NET Core não implementa a detecção de navegador, pois
os valores de cabeçalho de solicitação User-Agent são altamente instáveis e são
alterados semanalmente. Nesse caso, um ponto de extensão na política de cookie
permite adicionar uma lógica específica do User-Agent .

No Startup.cs, adicione as seguintes instruções:

C#

private void CheckSameSite(HttpContext httpContext, CookieOptions options)


{
if (options.SameSite == SameSiteMode.None)
{
var userAgent = httpContext.Request.Headers["User-
Agent"].ToString();
// TODO: Use your User Agent library of choice here.
if (/* UserAgent doesn't support new behavior */)
{
options.SameSite = SameSiteMode.Unspecified;
}
}
}

public void ConfigureServices(IServiceCollection services)


{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = SameSiteMode.Unspecified;
options.OnAppendCookie = cookieContext =>
CheckSameSite(cookieContext.Context,
cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext =>
CheckSameSite(cookieContext.Context,
cookieContext.CookieOptions);
});
}

public void Configure(IApplicationBuilder app)


{
// Before UseAuthentication or anything else that writes cookies.
app.UseCookiePolicy();

app.UseAuthentication();
// code omitted for brevity
}

Opções de recusa

A opção de compatibilidade Microsoft.AspNetCore.SuppressSameSiteNone permite que


você recuse temporariamente o novo comportamento de cookie do ASP.NET Core.
Adicione o seguinte JSON a um arquivo runtimeconfig.template.json no projeto:

JSON

{
"configProperties": {
"Microsoft.AspNetCore.SuppressSameSiteNone": "true"
}
}

Outras versões

Em breve haverá patches relacionados ao SameSite :

ASP.NET Core 2.1, 2.2 e 3.0


Microsoft.Owin 4.1

System.Web (para .NET Framework 4.7.2 e versões posteriores)

Categoria
ASP.NET

APIs afetadas
Microsoft.AspNetCore.Builder.CookiePolicyOptions.MinimumSameSitePolicy
Microsoft.AspNetCore.Http.CookieBuilder.SameSite
Microsoft.AspNetCore.Http.CookieOptions.SameSite
Microsoft.AspNetCore.Http.SameSiteMode
Microsoft.Net.Http.Headers.SameSiteMode
Microsoft.Net.Http.Headers.SetCookieHeaderValue.SameSite

Implantação
Caminho do host x86 no Windows 64 bits

MSBuild

Builds de tempo de design retornam apenas referências


de pacote de nível superior
Do SDK do .NET Core 3.1.400 em diante, somente as referências de pacote de nível
superior são retornadas pelo destino RunResolvePackageDependencies .

Versão introduzida

SDK do .NET Core 3.1.400

Descrição das alterações

Em versões anteriores do SDK do .NET Core, o destino RunResolvePackageDependencies


criava os seguintes itens do MSBuild que continham informações do arquivo de ativos
NuGet:

PackageDefinitions

PackageDependencies
TargetDefinitions

FileDefinitions

FileDependencies
Esses dados eram usados pelo Visual Studio para popular o nó Dependências no
Gerenciador de Soluções. Mas isso pode representar uma grande quantidade de dados
que não serão necessários, a menos que o nó Dependências seja expandido.

Do SDK do .NET Core versão 3.1.400 em diante, a maioria desses itens não é gerada por
padrão. Somente itens do tipo Package são retornados. Se o Visual Studio precisar dos
itens para popular o nó Dependências, ele lerá as informações diretamente do arquivo
de ativos.

Motivo da alteração

Essa alteração foi introduzida para aprimorar o desempenho do carregamento de


solução dentro do Visual Studio. Antes, todas as referências de pacote eram carregadas,
o que envolvia o carregamento de muitas referências que a maioria dos usuários nunca
visualizava.

Ação recomendada

Se você tiver uma lógica do MSBuild que dependa da criação desses itens, defina a
propriedade EmitLegacyAssetsFileItems como true no arquivo de projeto. Essa
configuração habilita o comportamento anterior em que todos os itens eram criados.

Categoria

MSBuild

APIs afetadas

N/D

SDK
Manifestos da ferramenta na pasta raiz

Windows Forms

Controles removidos
A partir do .NET Core 3.1, alguns controles do Windows Forms não estão mais
disponíveis.

Descrição das alterações

A partir do .NET Core 3.1, vários controles do Windows Forms não estão mais
disponíveis. Controles de substituição que têm design e suporte aprimorados foram
introduzidos no .NET Framework 2.0. Os controles preteridos já foram removidos das
caixas de ferramentas do designer, mas ainda estavam disponíveis para serem usados.

Os seguintes tipos não estão mais disponíveis:

ContextMenu
DataGrid
DataGrid.HitTestType
DataGridBoolColumn
DataGridCell
DataGridColumnStyle
DataGridLineStyle
DataGridParentRowsLabelStyle
DataGridPreferredColumnWidthTypeConverter
DataGridTableStyle
DataGridTextBox
DataGridTextBoxColumn
GridColumnStylesCollection
GridTablesFactory
GridTableStylesCollection
IDataGridEditingService
IMenuEditorService
MainMenu
Menu
Menu.MenuItemCollection
MenuItem
ToolBar
ToolBarAppearance
ToolBarButton
ToolBar.ToolBarButtonCollection
ToolBarButtonClickEventArgs
ToolBarButtonStyle
ToolBarTextAlign
Versão introduzida
3.1

Ação recomendada

Cada controle removido tem um controle de substituição recomendado. Consulte a


tabela a seguir:

Controle Substituição APIs associadas que foram removidas


removido recomendada
(API)

ContextMenu ContextMenuStrip

DataGrid DataGridView DataGridCell, DataGridRow,


DataGridTableCollection,
DataGridColumnCollection, DataGridTableStyle,
DataGridColumnStyle, DataGridLineStyle,
DataGridParentRowsLabel,
DataGridParentRowsLabelStyle,
DataGridBoolColumn, DataGridTextBox,
GridColumnStylesCollection,
GridTableStylesCollection, HitTestType

MainMenu MenuStrip

Menu ToolStripDropDown, MenuItemCollection


ToolStripDropDownMenu

MenuItem ToolStripMenuItem

ToolBar ToolStrip ToolBarAppearance

ToolBarButton ToolStripButton ToolBarButtonClickEventArgs,


ToolBarButtonClickEventHandler,
ToolBarButtonStyle, ToolBarTextAlign

Categoria
Windows Forms

APIs afetadas
System.Windows.Forms.ContextMenu
System.Windows.Forms.GridColumnStylesCollection
System.Windows.Forms.GridTablesFactory
System.Windows.Forms.GridTableStylesCollection
System.Windows.Forms.IDataGridEditingService
System.Windows.Forms.MainMenu
System.Windows.Forms.Menu
System.Windows.Forms.Menu.MenuItemCollection
System.Windows.Forms.MenuItem
System.Windows.Forms.ToolBar
System.Windows.Forms.ToolBar.ToolBarButtonCollection
System.Windows.Forms.ToolBarAppearance
System.Windows.Forms.ToolBarButton
System.Windows.Forms.ToolBarButtonClickEventArgs
System.Windows.Forms.ToolBarButtonStyle
System.Windows.Forms.ToolBarTextAlign
System.Windows.Forms.DataGrid
System.Windows.Forms.DataGrid.HitTestType
System.Windows.Forms.DataGridBoolColumn
System.Windows.Forms.DataGridCell
System.Windows.Forms.DataGridColumnStyle
System.Windows.Forms.DataGridLineStyle
System.Windows.Forms.DataGridParentRowsLabelStyle
System.Windows.Forms.DataGridPreferredColumnWidthTypeConverter
System.Windows.Forms.DataGridTableStyle
System.Windows.Forms.DataGridTextBox
System.Windows.Forms.DataGridTextBoxColumn
System.Windows.Forms.Design.IMenuEditorService

O evento CellFormatting não é gerado quando a dica de


ferramenta é mostrada
Um DataGridView agora mostra as dicas de texto e de erro de uma célula quando
focalizado por um mouse e quando selecionado por meio do teclado. Se uma dica de
ferramenta for mostrada, o evento DataGridView.CellFormatting não será gerado.

Descrição das alterações


Antes do .NET Core 3.1, um DataGridView que tinha a propriedade ShowCellToolTips
definida como true mostrava uma dica de ferramenta para o texto e os erros da célula
quando ela era focalizada por um mouse. As dicas de ferramenta não eram mostradas
quando uma célula era selecionada por meio do teclado (por exemplo, usando a tecla
Tab, teclas de atalho ou a navegação com as setas). Se o usuário editava uma célula e,
enquanto DataGridView ainda estava no modo de edição, passava sobre uma célula que
não tinha a propriedade ToolTipText definida, um evento CellFormatting era gerado para
formatar o texto da célula da exibição na célula.

Para atender aos padrões de acessibilidade, começando no .NET Core 3.1, um


DataGridView que tem a propriedade ShowCellToolTips definida como true mostra as
dicas de ferramentas do texto e os erros de uma célula não apenas quando a célula é
focalizada, mas também quando ela é selecionada por meio do teclado. Como
consequência dessa alteração, o evento CellFormattingnão é gerado quando células que
não têm o conjunto de propriedades ToolTipText são focalizadas enquanto
DataGridView está no modo de edição. O evento não é gerado porque o conteúdo da
célula focalizada é mostrado como uma dica de ferramenta em vez de ser exibido na
célula.

Versão introduzida

3.1

Ação recomendada

Refatorar códigos que dependem do evento CellFormatting enquanto DataGridView


está no modo de edição.

Categoria

Windows Forms

APIs afetadas

Nenhum

Confira também
Novidades do .NET Core 3.1
Novidades do .NET Core 3.0
Artigo • 10/05/2023

Este artigo descreve as novidades do .NET Core 3.0. Um dos maiores avanços é o
suporte para aplicativos Windows de área de trabalho (somente Windows). Usando a
Área de Trabalho do Windows do componente de SDK do .NET Core 3.0, você pode
portar seus aplicativos Windows Forms e WPF (Windows Presentation Foundation). Para
deixar claro, o componente Windows Desktop só é compatível com o Windows e só é
incluído nele. Para obter mais informações, consulte a seção Área de Trabalho do
Windows mais adiante neste artigo.

O .NET Core 3.0 adiciona suporte para C# 8.0. É altamente recomendável que você use o
Visual Studio 2019 versão 16.3 ou mais recente, Visual Studio para Mac 8.3 ou mais
recente ou Visual Studio Code com a extensão C# mais recente.

Baixe e comece a usar o .NET Core 3.0 versão agora no Windows, macOS ou Linux.

Para obter mais informações sobre a versão, consulte o comunicado do .NET Core 3.0 .

O .NET Core 3.0 RC é considerado uma produção pronta pela Microsoft e tem suporte
total. Se você estiver usando uma versão prévia, deverá passar para a versão RTM para
dar suporte contínuo.

Aprimoramentos de linguagem C# 8.0


O C# 8.0 também faz parte dessa versão, que inclui o recurso de tipos de referência
anuláveis, fluxos assíncronos e mais padrões. Para obter mais informações sobre
recursos do C# 8.0, consulte Novidades do C# 8.0.

Tutoriais relacionados aos recursos de linguagem C# 8.0:

Tutorial: Expressar sua intenção de design mais claramente com tipos de referência
que permitem valor nulo e tipos que não permitem valor nulo
Tutprial: Gerar e consumir fluxos assíncronos usando o C# 8.0 e .NET Core 3.0
Tutorial: Usar padrões correspondentes para criar algoritmos controlados por tipo
e controlados por dados

Foram adicionados aprimoramentos de linguagem para dar suporte aos seguintes


recursos de API detalhados abaixo:

Intervalos e índices
Fluxos assíncronos
.NET Standard 2.1
O .NET Core 3.0 implementa o .NET Standard 2.1. No entanto, o modelo dotnet new
classlib padrão gera um projeto que ainda tem como destino .NET Standard 2.0. Para

direcionar ao .NET Standard 2.1, edite seu arquivo de projeto e altere a propriedade
TargetFramework para netstandard2.1 :

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>

</Project>

Se você estiver usando o Visual Studio, precisará do Visual Studio 2019, já que o Visual
Studio 2017 não dá suporte ao .NET Standard 2.1 nem ao .NET Core 3.0.

Compilar/Implantar

Executáveis por padrão


O .NET Core agora compila executáveis dependentes de estrutura por padrão. Esse
comportamento é novo para aplicativos que usam uma versão do .NET Core instalada
globalmente. Anteriormente, apenas implantações autocontidas produziam um
executável.

Durante dotnet build ou dotnet publish , é criado um executável (conhecido como o


appHost) que corresponde ao ambiente e à plataforma do SDK que você está usando.
Você pode esperar desses executáveis o mesmo que de outros executáveis nativos,
como:

Você pode clicar duas vezes no arquivo executável.


Você pode iniciar o aplicativo diretamente de um prompt de comando, como
myapp.exe no Windows e ./myapp no Linux e macOS.

appHost e notarização no macOS


Somente macOS
A partir do SDK 3.0 do .NET Core em cartório para macOS, a configuração para produzir
um executável padrão (conhecido como appHost) é desabilitada por padrão. Para obter
mais informações, consulte Notarização do macOS Catalina e o impacto nos downloads
e projetos do .NET Core.

Quando a configuração appHost está habilitada, o .NET Core gera um executável Mach-
O nativo quando você cria ou publica. Seu aplicativo é executado no contexto do
appHost quando ele é executado do código-fonte com o comando dotnet run ou
iniciando o executável Mach-O diretamente.

Sem o appHost, a única maneira de um usuário iniciar um aplicativo dependente de


estrutura é com o comando dotnet <filename.dll> . Um appHost sempre é criado
quando você publica seu aplicativo autocontido.

Você pode configurar o appHost no nível do projeto ou alternar o appHost para um


comando dotnet específico com o parâmetro -p:UseAppHost :

Arquivo de projeto

XML

<PropertyGroup>
<UseAppHost>true</UseAppHost>
</PropertyGroup>

Parâmetro de linha de comando

CLI do .NET

dotnet run -p:UseAppHost=true

Para obter mais informações sobre a configuração UseAppHost , consulte Propriedades


do MSBuild para Microsoft.NET.Sdk.

Executáveis de arquivo único


O comando dotnet publish dá suporte ao empacotamento de seu aplicativo em um
executável de arquivo único específico da plataforma. O executável é autoextraível e
contém todas as dependências (incluindo nativas) necessárias para a execução do
aplicativo. Quando o aplicativo é executado pela primeira vez, o aplicativo é extraído
para um diretório com base no nome do aplicativo e no identificador do build. A
inicialização é mais rápida quando o aplicativo é executado novamente. O aplicativo não
precisará autoextrair uma segunda vez, a menos que uma versão nova tenha sido usada.
Para publicar um único arquivo executável, defina o PublishSingleFile em seu projeto
ou na linha de comando com o comando dotnet publish :

XML

<PropertyGroup>
<RuntimeIdentifier>win10-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
</PropertyGroup>

- ou -

CLI do .NET

dotnet publish -r win10-x64 -p:PublishSingleFile=true

Para obter mais informações sobre a publicação de arquivo único, consulte o


documento de design de empacotador de arquivo único .

Corte de assembly
O SDK do .NET Core 3.0 vem com uma ferramenta que pode reduzir o tamanho dos
aplicativos analisando a IL e cortando assemblies não utilizados.

Os aplicativos autossuficientes incluem todos os componentes necessários para


executar seu código, sem exigir que o .NET seja instalado no computador host. No
entanto, muitas vezes o aplicativo requer apenas um pequeno subconjunto da estrutura
para funcionar, e outras bibliotecas não utilizadas podem ser removidas.

O .NET Core agora inclui uma configuração que usará a ferramentaVinculador de IL


para verificar a IL do seu aplicativo. Essa ferramenta detecta qual código é necessário e,
em seguida, corta bibliotecas não usadas. Ela pode reduzir significativamente o
tamanho da implantação de alguns aplicativos.

Para habilitá-la, adicione a configuração <PublishTrimmed> ao seu projeto e publique um


aplicativo autossuficiente:

XML

<PropertyGroup>
<PublishTrimmed>true</PublishTrimmed>
</PropertyGroup>

CLI do .NET
dotnet publish -r <rid> -c Release

Por exemplo, o modelo básico de novo projeto de console "hello world" incluído,
quando publicado, atinge cerca de 70 MB de tamanho. Usando <PublishTrimmed> , esse
tamanho é reduzido para cerca de 30 MB.

É importante considerar que os aplicativos ou estruturas (incluindo ASP.NET Core e


WPF) que usam reflexão ou recursos dinâmicos relacionados, geralmente são
interrompidos quando cortados. Essa interrupção ocorre porque o cortador não tem
ciência desse comportamento dinâmico e não pode determinar quais tipos de estrutura
são necessários para reflexão. A ferramenta Cortador de IL pode ser configurada para
estar ciente deste cenário.

Antes de mais nada, teste seu aplicativo depois de cortar.

Para saber mais sobre a ferramenta Cortador de IL, confira a documentação ou visite o
repositório mono/linker .

Compilação em camadas
A TC (compilação em camadas ) está ativa por padrão com o .NET Core 3.0. Esse
recurso permite que o runtime use de modo mais adaptável o compilador JIT (just-in-
time) para conseguir um melhor desempenho.

O principal benefício da compilação em camadas é fornecer duas maneiras de jitting de


métodos: em uma camada de baixa qualidade, mas mais rápida, ou em uma camada de
maior qualidade, mas mais lenta. A qualidade refere-se ao quão bem o método é
otimizado. O TC ajuda a aumentar o desempenho de um aplicativo quando ele passa
por vários estágios da execução, desde a inicialização até o estado estável. Quando a
compilação em camadas é desabilitada, cada método é compilado de uma única
maneira que é tendenciosa para o desempenho de estado estável em relação ao
desempenho de inicialização.

Quando o TC está habilitado, o seguinte comportamento se aplica à compilação de


método quando um aplicativo é iniciado:

Se o método tiver um código compilado antecipadamente ou ReadyToRun, o


código pré-gerado será usado.
Caso contrário, o método será jitted. Normalmente, esses métodos são genéricos
sobre tipos de valor.
O JIT rápido produz o código de baixa qualidade (ou menos otimizado) mais
rapidamente. No .NET Core 3.0, o JIT Rápido é habilitado por padrão para
métodos que não contêm loops e é preferencial durante a inicialização.
O JIT de otimização completa produz código de maior qualidade (ou mais
otimizado) mais lentamente. Para métodos em que o Quick JIT não seria usado
(por exemplo, se o método for atribuído com
MethodImplOptions.AggressiveOptimization), o JIT de otimização completa é
usado.

Para métodos chamados com frequência, o compilador just-in-time eventualmente cria


um código totalmente otimizado em segundo plano. Em seguida, o código otimizado
substitui o código pré-compilado para esse método.

O código gerado pelo Quick JIT pode ser executado mais lento, alocar mais memória ou
usar mais espaço de pilha. Se houver problemas, você poderá desabilitar o JIT Rápido
usando essa propriedade MSBuild no arquivo de projeto:

XML

<PropertyGroup>
<TieredCompilationQuickJit>false</TieredCompilationQuickJit>
</PropertyGroup>

Para desabilitar completamente a TC, use esta configuração em seu arquivo de projeto:

XML

<PropertyGroup>
<TieredCompilation>false</TieredCompilation>
</PropertyGroup>

 Dica

Se você alterar essas configurações no arquivo de projeto, talvez seja necessário


executar um build limpo para que as novas configurações sejam refletidas (exclua
os diretórios obj e bin e recompile).

Para obter mais informações sobre como configurar a compilação em tempo de


execução, confira Opções de configuração do Runtime para compilação.

Imagens ReadyToRun
Você pode melhorar o tempo de inicialização do seu aplicativo .NET Core compilando
seus assemblies de aplicativos como o formato ReadyToRun (R2R). R2R é uma forma de
compilação antecipada (AOT).

Os binários R2R melhoram o desempenho de inicialização reduzindo a quantidade de


trabalho que o compilador just-in-time (JIT) precisa fazer à medida que seu aplicativo é
carregado. Os binários contêm código nativo similar comparado ao que o JIT produziria.
Entretanto, os binários R2R são maiores porque contêm código de IL (linguagem
intermediária), que ainda é necessário para alguns cenários, e a versão nativa do mesmo
código. O R2R só está disponível quando você publica um aplicativo autocontido que
tenha como alvo um RID (Runtime Environment) específico, como o Linux x64 ou o
Windows x64.

Para compilar seu projeto como ReadyToRun, faça o seguinte:

1. Adicione a configuração <PublishReadyToRun> ao seu projeto:

XML

<PropertyGroup>
<PublishReadyToRun>true</PublishReadyToRun>
</PropertyGroup>

2. Publique um aplicativo autossuficiente. Por exemplo, esse comando cria um


aplicativo autossuficiente para a versão de 64 bits do Windows:

CLI do .NET

dotnet publish -c Release -r win-x64 --self-contained

Restrições de plataforma cruzada/arquitetura


O compilador ReadyToRun atualmente não tem suporte para o direcionamento cruzado.
Você precisa compilar em determinado destino. Por exemplo, se você quiser imagens
R2R para Windows x64, será necessário executar o comando Publicar nesse ambiente.

Exceções ao direcionamento cruzado:

O Windows x64 pode ser usado para compilar imagens do Windows ARM32,
ARM64 e x86.
O Windows x86 pode ser usado para compilar imagens do Windows ARM32.
O Linux x64 pode ser usado para compilar imagens do Linux Arm32 e Arm64.

Para obter mais informações, consulte Pronto para execução.


Runtime/SDK

Roll forward de runtime de versão principal


O .NET Core 3.0 introduz um recurso opcional que permite que seu aplicativo efetue roll
forward para a versão principal mais recente do .NET Core. Adicionalmente, foi
adicionada uma nova configuração para controlar como o roll forward é aplicado ao seu
aplicativo. Isso pode ser configurado das seguintes maneiras:

Propriedade do arquivo de projeto: RollForward


Propriedade do arquivo de configuração de runtime: rollForward
Variável de ambiente: DOTNET_ROLL_FORWARD
Argumento de linha de comando: --roll-forward

Um dos valores a seguir precisa ser especificado. Se a configuração for omitida,


Secundária será o padrão.

LatestPatch
Efetuar roll forward para a versão de patch mais recente. Isso desabilita o roll
forward da versão secundária.
Secundária
Se a versão secundária solicitada estiver ausente, efetue roll forward para a menor
versão secundária mais alta. Se a versão secundária solicitada estiver presente, a
política LatestPatch será usada.
Principal
Se a versão principal solicitada estiver ausente, efetuar roll forward para a versão
principal mais alta e a versão secundária mais baixa. Se a versão principal solicitada
está presente, a política Secundária é usada.
LatestMinor
Efetuar roll forward para a versão secundária mais recente, mesmo se a versão
secundária solicitada estiver presente. Destinado a cenários de hospedagem de
componente.
LatestMajor
Efetuar roll forward para a versão principal e a secundária mais altas, mesmo se a
principal solicitada estiver presente. Destinado a cenários de hospedagem de
componente.
Desabilitar
Não efetuar roll forward. Associar somente à versão especificada. Essa política não
é recomendada para uso geral, pois ela desabilita a capacidade de efetuar roll
forward para os patches mais recentes. Esse valor só é recomendado para teste.
Com a exceção da configuração Desabilitar, todas as configurações usarão a versão de
patch mais recente disponível.

Por padrão, se a versão solicitada (conforme especificado em .runtimeconfig.json para


o aplicativo) for uma versão de versão, somente as versões de versão serão
consideradas para roll forward. Todas as versões de pré-lançamento são ignoradas. Se
não houver nenhuma versão de versão correspondente, as versões de pré-lançamento
serão levadas em conta. Esse comportamento pode ser alterado pela configuração
DOTNET_ROLL_FORWARD_TO_PRERELEASE=1 , nesse caso, todas as versões são sempre

consideradas.

O build copia dependências


O comando dotnet build agora copia as dependências do NuGet para seu aplicativo do
cache NuGet para a pasta de saída de build. Anteriormente, as dependências eram
copiadas apenas como parte de dotnet publish .

Há algumas operações, como corte e publicação de página do razor, que ainda exigem
publicação.

Ferramentas locais
O .NET Core 3.0 apresenta ferramentas locais. Ferramentas locais são semelhantes às
ferramentas globais, mas estão associadas a um local específico no disco. Ferramentas
locais não estão disponíveis globalmente e são distribuídas como pacotes NuGet.

As ferramentas locais dependem de um nome de arquivo de manifesto dotnet-


tools.json no seu diretório atual. Esse arquivo de manifesto define as ferramentas que

estarão disponíveis nessa pasta e abaixo. Você pode distribuir o arquivo de manifesto
com o seu código para garantir que qualquer pessoa que trabalha com o seu código
possa restaurar e usar as mesmas ferramentas.

Para ferramentas globais e locais, é necessária uma versão compatível do runtime.


Muitas ferramentas que estão atualmente em NuGet.org direcionam para o runtime do
.NET Core 2.1. Para instalar essas ferramentas, de forma global ou local, você ainda
precisará instalar o Runtime do NET Core 2.1 .

Novas opções global.json


O arquivo global.json tem novas opções que fornecem mais flexibilidade quando você
está tentando definir qual versão do SDK do .NET Core é usada. As novas opções são:
allowPrerelease : indica se o resolvedor do SDK deve considerar versões de pré-

lançamento ao selecionar a versão do SDK a ser usada.


rollForward : indica a política de roll-forward a ser usada ao selecionar uma versão

do SDK, seja como um fallback quando uma versão específica do SDK está ausente
ou como uma diretiva para usar uma versão mais alta.

Para obter mais informações sobre as alterações, incluindo valores padrão, valores com
suporte e novas regras de correspondência, consulte Visão geral do global.json.

Tamanhos menores de heap de coleta de lixo


O tamanho do heap do coletor de lixo padrão foi reduzido, resultando em menor uso
de memória pelo .NET Core. Essa alteração se alinha melhor com o orçamento de
alocação de geração 0 com os tamanhos de cache de processadores modernos.

Suporte de página grande de coleta de lixo


Páginas grandes (também conhecidas como páginas enormes no Linux) é um recurso
em que o sistema operacional é capaz de estabelecer regiões de memória maiores do
que o tamanho da página nativo (geralmente 4K) para melhorar o desempenho do
aplicativo que está solicitando essas páginas grandes.

O coletor de lixo agora pode ser configurado com a configuração GCLargePages como
um recurso opcional a ser escolhido para alocar páginas grandes no Windows.

Windows Desktop & COM

Windows Installer do SDK do .NET Core


O instalador MSI para Windows foi alterado do .NET Core 3.0 em diante. Os instaladores
de SDK agora atualizarão versões de faixa de recurso do SDK no local. Faixas de recurso
são definidas nos grupos de centenas na seção patch do número de versão. Por
exemplo, 3.0.101 e 3.0.201 são versões em duas faixas de recurso diferentes, enquanto
3.0.101 e 3.0.199 estão na mesma faixa de recurso. Além disso, quando o SDK do .NET
Core 3.0.101 for instalado, o SDK do .NET Core 3.0.100 será removido do computador se
ele existir. Quando o SDK do .NET Core 3.0.200 for instalado no mesmo computador, o
SDK do .NET Core 3.0.101 não será removido.

Para obter mais informações sobre controle de versão, consulte Visão geral de como é o
controle de versão no .NET Core.
Área de trabalho do Windows
O .NET Core 3.0 dá suporte a aplicativos da Área de Trabalho do Windows usando o
Windows Forms e o WPF (Windows Presentation Foundation). Essas estruturas também
oferecem suporte ao uso de controles modernos e no estilo Fluent da biblioteca XAML
da interface do usuário do Windows (WinUI) por meio de Ilhas XAML.

O componente Windows Desktop faz parte do SDK do .NET Core 3.0 do Windows.

É possível criar um novo aplicativo de WPF ou Windows Forms com os seguintes


comandos dotnet :

CLI do .NET

dotnet new wpf


dotnet new winforms

O Visual Studio 2019 adiciona modelos de Novo Projeto ao Windows Forms e WPF no
.NET Core 3.0.

Para obter mais informações sobre como portar um aplicativo existente do .NET
Framework, consulte Portar projetos do WPF e Portar projetos do Windows Forms.

WinForms com DPI alto

Aplicativos do Windows Forms do .NET Core podem definir o modo de DPI alto com
Application.SetHighDpiMode(HighDpiMode). O método SetHighDpiMode define o modo
de DPI alto correspondente, a menos que a configuração tenha sido definida por outros
meios, tais como App.Manifest ou P/Invoke antes de Application.Run .

Os valores highDpiMode possíveis, conforme expressos pelo enum


System.Windows.Forms.HighDpiMode, são:

DpiUnaware

SystemAware

PerMonitor
PerMonitorV2

DpiUnawareGdiScaled

Para obter mais informações sobre os modos de DPI alto, confira Desenvolvimento de
aplicativos de área de trabalho de DPI alto no Windows.
Criar componentes COM
No Windows, agora você pode criar componentes gerenciados que podem ser
chamados por COM. Essa funcionalidade é fundamental para usar o .NET Core com
modelos de suplemento do COM e também para fornecer paridade com o .NET
Framework.

Ao contrário do .NET Framework em que o mscoree. dll foi usado como o servidor COM,
o .NET Core adicionará uma dll de inicializador nativa ao diretório bin quando você
compilar o componente COM.

Para obter um exemplo de como criar um componente COM e consumi-lo, veja a


demonstração de COM .

Interoperabilidade nativa do Windows


O Windows oferece uma API nativa rica na forma de APIs C simples, COM e WinRT.
Embora o .NET Core dê suporte a P/Invoke, o .NET Core 3.0 adiciona a capacidade de
criar conjuntamente APIs COM e ativar APIs WinRT. Para obter um exemplo de código,
consulte a demonstração do Excel .

Implantação de MSIX
MSIX é um novo formato de pacote de aplicativos do Windows. Ele pode ser usado para
implantar aplicativos da área de trabalho do .NET Core 3.0 no Windows 10.

O Projeto de Empacotamento de Aplicativos do Windows, disponível no Visual Studio


2019, permite criar pacotes MSIX com aplicativos .NET Core autossuficientes.

O arquivo de projeto do .NET Core precisa especificar os runtimes compatíveis na


propriedade <RuntimeIdentifiers> :

XML

<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>

Aprimoramentos do Linux

SerialPort para Linux


O .NET Core 3.0 fornece suporte básico para System.IO.Ports.SerialPort no Linux.
Anteriormente, o .NET Core só dava suporte ao uso de SerialPort no Windows.

Para saber mais sobre o suporte limitado para a porta serial no Linux, confira o
Problema do GitHub nº 33146 .

Limites de memória do Docker e cgroup


A execução do .NET Core 3.0 no Linux com o Docker funciona melhor com limites de
memória cgroup. Executar um contêiner do Docker com limites de memória, tais como
docker run -m , altera o comportamento do .NET Core.

Tamanho de heap do GC (coletor de lixo) padrão: máximo de 20 MB ou 75% do


limite de memória no contêiner.
O tamanho explícito pode ser definido como um número absoluto ou um
percentual do limite de cgroup.
O tamanho mínimo do segmento reservado por heap de GC é de 16 MB. Esse
tamanho reduz o número de heaps que são criados em computadores.

Suporte de GPIO para o Raspberry Pi


Foram lançados dois pacotes para o NuGet que você pode usar para programação de
GPIO:

System.Device.Gpio
Iot.Device.Bindings

Os pacotes GPIO incluem as APIs para dispositivos GPIO, SPI, I2C e PWM. O pacote de
associações de IoT inclui associações de dispositivo. Para obter mais informações, veja o
repositório GitHub de dispositivos .

Suporte a Arm64 no Linux


O .NET Core 3.0 adiciona suporte para Arm64 para Linux. O principal caso de uso para
Arm64 atualmente é em cenários de IoT. Para obter mais informações, consulte Status
do .NET Core Arm64 .

Imagens do Docker para o .NET Core Arm64 estão disponíveis para Alpine, Debian e
Ubuntu.

7 Observação
O suporte para os sistemas operacionais macOS Arm64 (ou "Apple Silicon") e
Windows Arm64 foi adicionado posteriormente ao .NET 6.

Segurança

TLS 1.3 & OpenSSL 1.1.1 no Linux


O .NET Core agora faz proveito do suporte a protocolo TLS 1.3 no OpenSSL 1.1.1
quando esse protocolo está disponível em um determinado ambiente. Com o TLS 1.3:

Tempos de conexão são aprimorados com um menor número de viagens de ida e


volta necessárias entre o cliente e o servidor.
Segurança aprimorada devido à remoção de vários algoritmos criptográficos
obsoletos e não seguros.

Quando disponíveis, o .NET Core 3.0 usa OpenSSL 1.1.1, 1.1.0 ou 1.0.2 em um sistema
Linux. Quando o OpenSSL 1.1.1 está disponível, ambos os tipos
System.Net.Security.SslStream e System.Net.Http.HttpClient usarão o protocolo TLS 1.3
(supondo que o cliente e o servidor deem suporte ao protocolo TLS 1.3).

) Importante

Windows e macOS ainda não oferecem suporte a TLS 1.3.

O exemplo de C# 8.0 a seguir demonstra o .NET Core 3.0 no Ubuntu 18.10 conectando-
se a https://www.cloudflare.com :

C#

using System;
using System.Net.Security;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace whats_new
{
public static class TLS
{
public static async Task ConnectCloudFlare()
{
var targetHost = "www.cloudflare.com";

using TcpClient tcpClient = new TcpClient();


await tcpClient.ConnectAsync(targetHost, 443);

using SslStream sslStream = new


SslStream(tcpClient.GetStream());

await sslStream.AuthenticateAsClientAsync(targetHost);
await Console.Out.WriteLineAsync($"Connected to {targetHost}
with {sslStream.SslProtocol}");
}
}
}

Cifras de criptografia
O .NET Core 3.0 adiciona o suporte para as criptografias AES-GCM e AES-CCM,
implementadas com System.Security.Cryptography.AesGcm e
System.Security.Cryptography.AesCcm, respectivamente. Esses algoritmos são ambos do
tipo AEAD (criptografia autenticada com os dados de associação) .

O código a seguir demonstra como usar criptografia AesGcm para criptografar e


descriptografar dados aleatórios.

C#

using System;
using System.Linq;
using System.Security.Cryptography;

namespace whats_new
{
public static class Cipher
{
public static void Run()
{
// key should be: pre-known, derived, or transported via another
channel, such as RSA encryption
byte[] key = new byte[16];
RandomNumberGenerator.Fill(key);

byte[] nonce = new byte[12];


RandomNumberGenerator.Fill(nonce);

// normally this would be your data


byte[] dataToEncrypt = new byte[1234];
byte[] associatedData = new byte[333];
RandomNumberGenerator.Fill(dataToEncrypt);
RandomNumberGenerator.Fill(associatedData);

// these will be filled during the encryption


byte[] tag = new byte[16];
byte[] ciphertext = new byte[dataToEncrypt.Length];

using (AesGcm aesGcm = new AesGcm(key))


{
aesGcm.Encrypt(nonce, dataToEncrypt, ciphertext, tag,
associatedData);
}

// tag, nonce, ciphertext, associatedData should be sent to the


other part

byte[] decryptedData = new byte[ciphertext.Length];

using (AesGcm aesGcm = new AesGcm(key))


{
aesGcm.Decrypt(nonce, ciphertext, tag, decryptedData,
associatedData);
}

// do something with the data


// this should always print that data is the same
Console.WriteLine($"AES-GCM: Decrypted data is
{(dataToEncrypt.SequenceEqual(decryptedData) ? "the same as" : "different
than")} original data.");
}
}
}

Importar/exportar chave de criptografia


O .NET Core 3.0 dá suporte à importação e exportação de chaves públicas e privadas
assimétricas de formatos padrão. Você não precisa usar um certificado X.509.

Todos os tipos principais, tais como RSA, DSA, ECDsa e ECDiffieHellman, dão suporte
aos seguintes formatos:

Chave pública
X.509 SubjectPublicKeyInfo

Chave privada
PKCS nº 8 PrivateKeyInfo
PKCS nº 8 EncryptedPrivateKeyInfo

Chaves RSA também dão suporte a:

Chave pública
PKCS nº 1 RSAPublicKey

Chave privada
PKCS nº 1 RSAPrivateKey

Os métodos de exportação produzem dados binários codificados em DER e os métodos


de importação esperam o mesmo. Se uma chave for armazenada no formato PEM
compatível com texto, o chamador precisará decodificar o conteúdo em Base64 antes
de chamar um método de importação.

C#

using System;
using System.Security.Cryptography;

namespace whats_new
{
public static class RSATest
{
public static void Run(string keyFile)
{
using var rsa = RSA.Create();

byte[] keyBytes = System.IO.File.ReadAllBytes(keyFile);


rsa.ImportRSAPrivateKey(keyBytes, out int bytesRead);

Console.WriteLine($"Read {bytesRead} bytes, {keyBytes.Length -


bytesRead} extra byte(s) in file.");
RSAParameters rsaParameters = rsa.ExportParameters(true);
Console.WriteLine(BitConverter.ToString(rsaParameters.D));
}
}
}

Arquivos PKCS nº 8 podem ser inspecionados com


System.Security.Cryptography.Pkcs.Pkcs8PrivateKeyInfo e arquivos PFX/PKCS nº 12
podem ser inspecionados com System.Security.Cryptography.Pkcs.Pkcs12Info. Arquivos
PFX/PKCS nº 12 podem ser manipulados com
System.Security.Cryptography.Pkcs.Pkcs12Builder.

Alterações na API do .NET Core 3.0

Intervalos e índices
O novo tipo System.Index pode ser usado para indexação. É possível criar um a partir de
um int que conta desde o início, ou com um operador ^ de prefixo (C#) que conta do
final:

C#
Index i1 = 3; // number 3 from beginning
Index i2 = ^4; // number 4 from end
int[] a = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
Console.WriteLine($"{a[i1]}, {a[i2]}"); // "3, 6"

Há também o tipo System.Range, que consiste em dois valores Index (um para o início
e outro para o final) e pode ser escrito com uma expressão de intervalo x..y (C#). Em
seguida, você pode indexar com um Range , o que produz uma fatia:

C#

var slice = a[i1..i2]; // { 3, 4, 5 }

Para obter mais informações, consulte o tutorial de intervalos e índices.

Fluxos assíncronos
O tipo IAsyncEnumerable<T> é uma nova versão assíncrona de IEnumerable<T>. A
linguagem permite o uso de await foreach em IAsyncEnumerable<T> para consumir
seus elementos e o uso de yield return neles para produzir elementos.

O exemplo a seguir demonstra a produção e o consumo de fluxos assíncronos. A


instrução foreach é assíncrona e usa yield return para produzir um fluxo assíncrono
para chamadores. Esse padrão (usar yield return ) é o modelo recomendado para a
produção de fluxos assíncronos.

C#

async IAsyncEnumerable<int> GetBigResultsAsync()


{
await foreach (var result in GetResultsAsync())
{
if (result > 20) yield return result;
}
}

Além de poder await foreach , você também pode criar iteradores assíncronos, por
exemplo, um iterador que retorne um IAsyncEnumerable/IAsyncEnumerator em que é
possível aplicar await e yield . Para objetos que precisam ser descartados, você pode
usar IAsyncDisposable , que vários tipos BCL implementam, como Stream e Timer .

Para obter mais informações, consulte o tutorial de fluxos assíncronos.


Ponto flutuante IEEE
APIs de ponto flutuante estão sendo atualizadas para entrar em conformidade com a
revisão IEEE 754-2008 . O objetivo dessas alterações é expor todas as operações
necessárias e garantir que elas estejam em conformidade comportamental com a
especificação IEEE. Para obter mais informações sobre melhorias de ponto flutuante,
consulte Melhorias de análise e formatação de ponto flutuante na postagem no blog do
.NET Core 3.0 .

As correções de análise e formatação incluem:

Analisar e arredondar corretamente entradas de qualquer tamanho.


Analisar e formatar corretamente o zero negativo.
Analisar corretamente Infinity e NaN , fazendo uma verificação sem diferenciação
de maiúsculas e minúsculas e permitindo um + precedente opcional onde
aplicável.

Novas APIs System.Math incluem:

BitIncrement(Double) e BitDecrement(Double)
Correspondem às operações nextUp e nextDown do IEEE. Retornam o menor
número de ponto flutuante que compara o valor maior ou menor que a entrada
(respectivamente). Por exemplo, Math.BitIncrement(0.0) retorna double.Epsilon .

MaxMagnitude(Double, Double) e MinMagnitude(Double, Double)


Correspondem às operações maxNumMag e minNumMag do IEEE; retornam o valor
maior ou menor em magnitude das duas entradas (respectivamente). Por exemplo,
Math.MaxMagnitude(2.0, -3.0) retorna -3.0 .

ILogB(Double)
Corresponde à operação logB IEEE que retorna um valor integral, ele retorna o log
de base 2 integral do parâmetro de entrada. Esse método é praticamente o
mesmo que floor(log2(x)) , mas feito com o mínimo de erro de arredondamento.

ScaleB(Double, Int32)
Corresponde à operação IEEE scaleB que usa um valor integral, ele retorna
efetivamente x * pow(2, n) , mas é feito com o mínimo de erro de
arredondamento.

Log2(Double)
Corresponde à operação log2 do IEEE; retorna o logaritmo de base 2. Minimiza o
erro de arredondamento.
FusedMultiplyAdd(Double, Double, Double)
Corresponde à operação fma do IEEE; executa uma adição e multiplicação fundida.
Em outras palavras, realiza (x * y) + z como uma única operação, minimizando o
erro de arredondamento. Um exemplo é FusedMultiplyAdd(1e308, 2.0, -1e308) ,
que retorna 1e308 . O (1e308 * 2.0) - 1e308 regular retorna
double.PositiveInfinity .

CopySign(Double, Double)
Corresponde à operação copySign do IEEE; retorna o valor de x , mas com o sinal
de y .

Intrínsecos dependentes da plataforma .NET


Foram adicionadas APIs que permitem acesso a determinadas instruções da CPU
orientadas a desempenho, como o SIMD ou conjuntos de instruções de manipulação
de bits. Essas instruções podem ajudar a obter melhorias significativas de desempenho
em determinados cenários, tais como processamento de dados eficiente em paralelo.

Quando apropriado, as bibliotecas .NET começaram usando estas instruções para


melhorar o desempenho.

Para obter mais informações, consulte Intrínsecos dependentes da plataforma .NET .

APIs de versão aprimoradas do .NET Core


Começando com o .NET Core 3.0, as APIs de versão fornecidas com o .NET Core agora
retornam as informações que você espera. Por exemplo:

C#

System.Console.WriteLine($"Environment.Version:
{System.Environment.Version}");

// Old result
// Environment.Version: 4.0.30319.42000
//
// New result
// Environment.Version: 3.0.0

C#

System.Console.WriteLine($"RuntimeInformation.FrameworkDescription:
{System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");
// Old result
// RuntimeInformation.FrameworkDescription: .NET Core 4.6.27415.71
//
// New result (notice the value includes any preview release information)
// RuntimeInformation.FrameworkDescription: .NET Core 3.0.0-preview4-
27615-11

2 Aviso

Alteração da falha. Isso é tecnicamente uma alteração da falha, porque o esquema


de controle de versão foi alterado.

Suporte interno rápido a JSON


Usuários do .NET têm dependido basicamente de Newtonsoft.Json e outras
bibliotecas JSON populares, que continuam a ser boas opções. O Newtonsoft.Json usa
cadeias de caracteres do .NET como seu tipo de dados base, o qual subjacentemente é
UTF-16.

O novo suporte JSON interno é de alto desempenho, baixa alocação e funciona com
texto JSON codificado em UTF-8. Para obter mais informações sobre o namespace e os
tipos System.Text.Json, consulte os seguintes artigos:

Serialização do JSON no .NET - Visão geral


Como serializar e desserializar JSON em .NET.
Como migrar de Newtonsoft.Json para System.Text.Json

Suporte do HTTP/2
O tipo System.Net.Http.HttpClient dá suporte ao protocolo HTTP/2. Se o HTTP/2 estiver
habilitado, a versão do protocolo HTTP é negociada via TLS/ALPN, e o HTTP/2 é usado
apenas se o servidor selecionar seu uso.

O protocolo padrão permanece HTTP/1.1, mas o HTTP/2 pode ser ativado de duas
maneiras diferentes. Primeiro, você pode definir a mensagem de solicitação HTTP para
usar HTTP/2:

C#

var client = new HttpClient() { BaseAddress = new


Uri("https://localhost:5001") };

// HTTP/1.1 request
using (var response = await client.GetAsync("/"))
Console.WriteLine(response.Content);

// HTTP/2 request
using (var request = new HttpRequestMessage(HttpMethod.Get, "/") { Version =
new Version(2, 0) })
using (var response = await client.SendAsync(request))
Console.WriteLine(response.Content);

Segundo, você pode alterar HttpClient para usar HTTP/2 por padrão:

C#

var client = new HttpClient()


{
BaseAddress = new Uri("https://localhost:5001"),
DefaultRequestVersion = new Version(2, 0)
};

// HTTP/2 is default
using (var response = await client.GetAsync("/"))
Console.WriteLine(response.Content);

Muitas vezes, quando você está desenvolvendo um aplicativo, quer usar uma conexão
não criptografada. Se você souber que o ponto de extremidade estará usando HTTP/2,
poderá ativar conexões não criptografadas para HTTP/2. Você pode ativá-lo definindo a
variável de ambiente
DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP2UNENCRYPTEDSUPPORT como 1 ou
ativando-a no contexto do aplicativo:

C#

AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSup
port", true);

Próximas etapas
Examinar as alterações interruptivas entre o .NET Core 2.2 e o 3.0.
Examinar as alterações interruptivas no .NET Core 3.0 para aplicativos do Windows
Forms.
Alterações interruptivas no .NET Core
3.0
Artigo • 18/03/2023

Se você estiver migrando para a versão 3.0 do .NET Core, do ASP.NET Core ou do EF
Core, as alterações interruptivas listadas neste artigo poderão afetar seu aplicativo.

ASP.NET Core
APIs obsoletas antifalsificação, CORS, de diagnóstico, MVC e de roteamento
removidas
APIs do "Pubternal" removidas
Autenticação: preterimento do Google+
Autenticação: propriedade HttpContext.Authentication removida
Autenticação: tipos Newtonsoft.Json substituídos
Autenticação: assinatura do ExchangeCodeAsync do OAuthHandler alterada
Autorização: sobrecarga de AddAuthorization movida para um assembly diferente
Autorização: IAllowAnonymous removido de AuthorizationFilterContext.Filters
Autorização: implementações IAuthorizationPolicyProvider exigem novo método
Cache: propriedade CompactOnMemoryPressure removida
Cache: Microsoft.Extensions.Caching.SqlServer usa um novo pacote SqlClient
Cache: tipos "pubternal" ResponseCaching alterados para internos
Proteção de dados: DataProtection.Blobs usa novas APIs do Armazenamento do
Azure
Hospedagem: AspNetCoreModule V1 removido do Pacote de Hospedagem do
Windows
Hospedagem: host genérico restringe a injeção de construtor de inicialização
Hospedagem: redirecionamento de HTTPS habilitado para aplicativos fora de
processo do IIS
Hospedagem: tipos IHostingEnvironment e IApplicationLifetime substituídos
Hosting: ObjectPoolProvider removido das dependências do WebHostBuilder
HTTP: extensibilidade do DefaultHttpContext removida
HTTP: campos HeaderNames alterados para estáticos somente leitura
HTTP: alterações na infraestrutura do corpo da resposta
HTTP: alguns valores padrão do SameSite de cookie alterados
HTTP: E/S síncrona desabilitada por padrão
Identity: sobrecarga do método AddDefaultUI removida
Identity: alteração da versão do Bootstrap da interface do usuário
Identity: SignInAsync gera exceção para identidade não autenticada
Identity: construtor SignInManager aceita novo parâmetro
Identity: interface do usuário usa o recurso de ativos Web estáticos
Kestrel: adaptadores de conexão removidos
Kestrel: assembly HTTPS vazio removido
Kestrel: cabeçalhos de trailer de solicitação movidos para a nova coleção
Kestrel: alterações na camada de abstração de transporte
Localização: APIs marcadas como obsoletas
Log: classe DebugLogger alterada para interna
MVC: sufixo assíncrono de ação do controlador removido
MVC: JsonResult mudou para Microsoft.AspNetCore.Mvc.Core
MVC: ferramenta de pré-compilação preterida
MVC: tipos alterados para internos
MVC: shim de compatibilidade da Web API removido
Razor: API RazorTemplateEngine removida
Razor: compilação de runtime movida para um pacote
Estado de sessão: APIs obsoletas removidas
Estrutura compartilhada: remoção do assembly de Microsoft.AspNetCore.App
Estrutura compartilhada: Microsoft.AspNetCore.All removido
SignalR: HandshakeProtocol.SuccessHandshakeData substituído
SignalR: métodos HubConnection removidos
SignalR: construtores HubConnectionContext alterados
SignalR: alteração do nome do pacote do cliente JavaScript
SignalR: APIs obsoletas
SPAs: SpaServices e NodeServices marcados como obsoletos
SPAs: alteração do padrão de fallback do agente de console de SpaServices e
NodeServices
Estrutura de destino: .NET Framework sem suporte

APIs obsoletas antifalsificação, CORS, de diagnóstico,


MVC e de roteamento removidas
Os membros obsoletos e os comutadores de compatibilidade no ASP.NET Core 2.2
foram removidos.

Versão introduzida
3.0

Motivo da alteração
Aprimoramento da superfície da API ao longo do tempo.

Ação recomendada
Ao direcionar projetos ao .NET Core 2.2, siga as diretrizes nas mensagens de build sobre
obsolescência para adotar novas APIs.

Categoria

ASP.NET Core

APIs afetadas

Os seguintes tipos e membros foram marcados como obsoletos no ASP.NET Core 2.1 e
2.2:

Types

Microsoft.AspNetCore.Diagnostics.Views.WelcomePage
Microsoft.AspNetCore.DiagnosticsViewPage.Views.AttributeValue

Microsoft.AspNetCore.DiagnosticsViewPage.Views.BaseView
Microsoft.AspNetCore.DiagnosticsViewPage.Views.HelperResult

Microsoft.AspNetCore.Mvc.Formatters.Xml.ProblemDetails21Wrapper
Microsoft.AspNetCore.Mvc.Formatters.Xml.ValidationProblemDetails21Wrapper

Microsoft.AspNetCore.Mvc.Razor.Compilation.ViewsFeatureProvider

Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageArgumentBinder
Microsoft.AspNetCore.Routing.IRouteValuesAddressMetadata

Microsoft.AspNetCore.Routing.RouteValuesAddressMetadata

Construtores

Microsoft.AspNetCore.Cors.Infrastructure.CorsService(IOptions{CorsOptions})

Microsoft.AspNetCore.Routing.Tree.TreeRouteBuilder(ILoggerFactory,UrlEncoder,O
bjectPool{UriBuildingContext},IInlineConstraintResolver)

Microsoft.AspNetCore.Mvc.Formatters.OutputFormatterCanWriteContext
Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider(IOptions{Mv

cOptions},IInlineConstraintResolver,IModelMetadataProvider)

Microsoft.AspNetCore.Mvc.ApiExplorer.DefaultApiDescriptionProvider(IOptions{Mv
cOptions},IInlineConstraintResolver,IModelMetadataProvider,IActionResultTypeMa

pper)
Microsoft.AspNetCore.Mvc.Formatters.FormatFilter(IOptions{MvcOptions})

Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ArrayModelBinder`1(IModelBinder
)

Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ByteArrayModelBinder
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinder`1(IModel
Binder)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder(IDicti
onary`2)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinder`2(IModelBi
nder,IModelBinder)

Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DoubleModelBinder(System.Globali

zation.NumberStyles)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FloatModelBinder(System.Globaliz

ation.NumberStyles)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FormCollectionModelBinder

Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FormFileModelBinder

Microsoft.AspNetCore.Mvc.ModelBinding.Binders.HeaderModelBinder
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.KeyValuePairModelBinder`2(IModel

Binder,IModelBinder)
Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinder(System.Ty

pe)
Microsoft.AspNetCore.Mvc.ModelBinding.ModelAttributes(IEnumerable{System.Obje

ct})

Microsoft.AspNetCore.Mvc.ModelBinding.ModelAttributes(IEnumerable{System.Objec
t},IEnumerable{System.Object})

Microsoft.AspNetCore.Mvc.ModelBinding.ModelBinderFactory(IModelMetadataProvide
r,IOptions{MvcOptions})

Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder(IModelMetadataProvider,I

ModelBinderFactory,IObjectModelValidator)
Microsoft.AspNetCore.Mvc.Routing.KnownRouteValueConstraint()
Microsoft.AspNetCore.Mvc.Formatters.XmlDataContractSerializerInputFormatter
Microsoft.AspNetCore.Mvc.Formatters.XmlDataContractSerializerInputFormatter(Sy

stem.Boolean)

Microsoft.AspNetCore.Mvc.Formatters.XmlDataContractSerializerInputFormatter(Mv
cOptions)

Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter
Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter(System.Boolea

n)
Microsoft.AspNetCore.Mvc.Formatters.XmlSerializerInputFormatter(MvcOptions)

Microsoft.AspNetCore.Mvc.TagHelpers.ImageTagHelper(IHostingEnvironment,IMe
moryCache,HtmlEncoder,IUrlHelperFactory)
Microsoft.AspNetCore.Mvc.TagHelpers.LinkTagHelper(IHostingEnvironment,IMemoryC
ache,HtmlEncoder,JavaScriptEncoder,IUrlHelperFactory)

Microsoft.AspNetCore.Mvc.TagHelpers.ScriptTagHelper(IHostingEnvironment,IMemor

yCache,HtmlEncoder,JavaScriptEncoder,IUrlHelperFactory)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.RazorPageAdapter(RazorPage

Base)

Propriedades

Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.CookieDomain

Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.CookieName
Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.CookiePath

Microsoft.AspNetCore.Antiforgery.AntiforgeryOptions.RequireSsl
Microsoft.AspNetCore.Mvc.ApiBehaviorOptions.AllowInferringBindingSourceForColl

ectionTypesAsFromQuery

Microsoft.AspNetCore.Mvc.ApiBehaviorOptions.SuppressUseValidationProblemDetail
sForInvalidModelStateResponses

Microsoft.AspNetCore.Mvc.CookieTempDataProviderOptions.CookieName
Microsoft.AspNetCore.Mvc.CookieTempDataProviderOptions.Domain

Microsoft.AspNetCore.Mvc.CookieTempDataProviderOptions.Path
Microsoft.AspNetCore.Mvc.DataAnnotations.MvcDataAnnotationsLocalizationOptions

.AllowDataAnnotationsLocalizationForEnumDisplayAttributes

Microsoft.AspNetCore.Mvc.Formatters.Xml.MvcXmlOptions.AllowRfc7807CompliantPro
blemDetailsFormat

Microsoft.AspNetCore.Mvc.MvcOptions.AllowBindingHeaderValuesToNonStringModelT
ypes

Microsoft.AspNetCore.Mvc.MvcOptions.AllowCombiningAuthorizeFilters

Microsoft.AspNetCore.Mvc.MvcOptions.AllowShortCircuitingValidationWhenNoValida
torsArePresent

Microsoft.AspNetCore.Mvc.MvcOptions.AllowValidatingTopLevelNodes
Microsoft.AspNetCore.Mvc.MvcOptions.InputFormatterExceptionPolicy

Microsoft.AspNetCore.Mvc.MvcOptions.SuppressBindingUndefinedValueToEnumType

Microsoft.AspNetCore.Mvc.MvcViewOptions.AllowRenderingMaxLengthAttribute
Microsoft.AspNetCore.Mvc.MvcViewOptions.SuppressTempDataAttributePrefix

Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions.AllowAreas
Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions.AllowDefaultHandlingForO

ptionsRequests
Microsoft.AspNetCore.Mvc.RazorPages.RazorPagesOptions.AllowMappingHeadRequests

ToGetHandler

Métodos

Microsoft.AspNetCore.Mvc.LocalRedirectResult.ExecuteResult(ActionContext)

Microsoft.AspNetCore.Mvc.RedirectResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.RedirectToActionResult.ExecuteResult(ActionContext)

Microsoft.AspNetCore.Mvc.RedirectToPageResult.ExecuteResult(ActionContext)
Microsoft.AspNetCore.Mvc.RedirectToRouteResult.ExecuteResult(ActionContext)

Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionCon

text,IValueProvider,ParameterDescriptor)
Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(Action
Context,IValueProvider,ParameterDescriptor,Object)

APIs do "Pubternal" removidas


Para manter melhor a superfície de API pública do ASP.NET Core, a maioria dos tipos
nos namespaces *.Internal (conhecidos como APIs "pubternal") tornaram-se
verdadeiramente internos. Os membros desses namespaces não foram projetados para
ter suporte como APIs voltadas ao público. As APIs podem ser interrompidas em
versões secundárias (e muitas vezes foram). Códigos que dependem dessas APIs são
interrompidos ao atualizar para o ASP.NET Core 3.0.

Para obter mais informações, confira dotnet/aspnetcore#4932 e


dotnet/aspnetcore#11312 .

Versão introduzida
3.0

Comportamento antigo
As APIs afetadas são marcadas com o public modificador de acesso e existem nos
namespaces *.Internal .

Novo comportamento
As APIs afetadas são marcadas com o modificador de acesso internal e não podem mais
ser usadas pelo código fora desse assembly.

Motivo da alteração

A orientação para essas APIs "pubternal" era que elas:

Podem ser alteradas sem aviso prévio.


Não estavam sujeitas às políticas do .NET para evitar alterações interruptivas.

Deixar as APIs public (mesmo nos namespaces *.Internal ) era confuso para os
clientes.

Ação recomendada
Pare de usar essas APIs "pubternal". Se você tiver dúvidas sobre APIs alternativas, abra
um problema no repositório dotnet/aspnetcore .

Por exemplo, considere o código de buffer de solicitação HTTP a seguir em um projeto


do ASP.NET Core 2.2. O método de extensão EnableRewind existe no namespace
Microsoft.AspNetCore.Http.Internal .

C#

HttpContext.Request.EnableRewind();

Em um projeto ASP.NET Core 3.0, substitua a chamada EnableRewind por uma chamada
ao método de extensão EnableBuffering . O recurso de buffer de solicitação funciona
como no passado. EnableBuffering chama agora a API internal .

C#

HttpContext.Request.EnableBuffering();

Categoria
ASP.NET Core

APIs afetadas
Todas as APIs nos namespaces Microsoft.AspNetCore.* e Microsoft.Extensions.* que
têm um segmento Internal no nome. Por exemplo:

Microsoft.AspNetCore.Authentication.Internal

Microsoft.AspNetCore.Builder.Internal
Microsoft.AspNetCore.DataProtection.Cng.Internal

Microsoft.AspNetCore.DataProtection.Internal

Microsoft.AspNetCore.Hosting.Internal
Microsoft.AspNetCore.Http.Internal

Microsoft.AspNetCore.Mvc.Core.Infrastructure
Microsoft.AspNetCore.Mvc.Core.Internal

Microsoft.AspNetCore.Mvc.Cors.Internal

Microsoft.AspNetCore.Mvc.DataAnnotations.Internal
Microsoft.AspNetCore.Mvc.Formatters.Internal

Microsoft.AspNetCore.Mvc.Formatters.Json.Internal
Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal

Microsoft.AspNetCore.Mvc.Internal

Microsoft.AspNetCore.Mvc.ModelBinding.Internal
Microsoft.AspNetCore.Mvc.Razor.Internal

Microsoft.AspNetCore.Mvc.RazorPages.Internal
Microsoft.AspNetCore.Mvc.TagHelpers.Internal

Microsoft.AspNetCore.Mvc.ViewFeatures.Internal
Microsoft.AspNetCore.Rewrite.Internal

Microsoft.AspNetCore.Routing.Internal

Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal
Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http

Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
Microsoft.AspNetCore.Server.Kestrel.Https.Internal

Autenticação: Google+ preterido e substituído


O Google começou a encerrar a entrada do Google+ para aplicativos em 28 de
janeiro de 2019.

Descrição das alterações

O ASP.NET 4.x e o ASP.NET Core têm usado as APIs de entrada do Google+ para
autenticar usuários de conta do Google em aplicativos Web. Os pacotes NuGet afetados
são Microsoft.AspNetCore.Authentication.Google para ASP.NET Core e
Microsoft.Owin.Security.Google para Microsoft.Owin com ASP.NET Web Forms e
MVC.

As APIs de substituição do Google usam uma fonte de dados e um formato diferentes.


As mitigações e soluções fornecidas abaixo explicam as alterações estruturais. Os
aplicativos devem verificar se os dados em si ainda atendem aos requisitos. Por
exemplo, nomes, endereços de email, links de perfil e fotos de perfil podem ter valores
um pouco diferentes dos anteriores.

Versão introduzida
Todas as versões. Essa alteração é externa ao ASP.NET Core.

Ação recomendada

Owin com ASP.NET Web Forms e MVC

Para Microsoft.Owin 3.1.0 e posteriores, uma mitigação temporária está descrita aqui .
Os aplicativos devem concluir o teste com a mitigação para verificar se há alterações no
formato de dados. Há planos para lançar o Microsoft.Owin 4.0.1 com uma correção. Os
aplicativos que usam qualquer versão anterior devem ser atualizados para a versão
4.0.1.

ASP.NET Core 1.x

A mitigação em Owin com ASP.NET Web Forms e MVC pode ser adaptada para o
ASP.NET Core 1.x. Não há planos de patch para o pacote NuGet porque o 1.x atingiu o
status de fim de vida útil .

ASP.NET Core 2.x

Para o Microsoft.AspNetCore.Authentication.Google a versão 2.x, substitua a chamada


existente para AddGoogle em Startup.ConfigureServices pelo seguinte código:

C#

.AddGoogle(o =>
{
o.ClientId = Configuration["Authentication:Google:ClientId"];
o.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
o.UserInformationEndpoint =
"https://www.googleapis.com/oauth2/v2/userinfo";
o.ClaimActions.Clear();
o.ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
o.ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
o.ClaimActions.MapJsonKey(ClaimTypes.GivenName, "given_name");
o.ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
o.ClaimActions.MapJsonKey("urn:google:profile", "link");
o.ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
});

Os patches de fevereiro das versões 2.1 e 2.2 incorporaram a reconfiguração anterior


como o novo padrão. Nenhum patch está planejado para o ASP.NET Core 2.0, pois ele
chegou ao fim da vida útil .

ASP.NET Core 3.0

A mitigação fornecida para o ASP.NET Core 2.x também pode ser usada para o ASP.NET
Core 3.0. Em versões prévias futuras do 3.0, o pacote
Microsoft.AspNetCore.Authentication.Google poderá ser removido. Nesse caso, os

usuários serão direcionados ao Microsoft.AspNetCore.Authentication.OpenIdConnect . O


código a seguir mostra como substituir AddGoogle por AddOpenIdConnect em
Startup.ConfigureServices . Essa substituição pode ser usada com o ASP.NET Core 2.0 e
posteriores e pode ser adaptada para o ASP.NET Core 1.x, conforme o necessário.

C#

.AddOpenIdConnect("Google", o =>
{
o.ClientId = Configuration["Authentication:Google:ClientId"];
o.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
o.Authority = "https://accounts.google.com";
o.ResponseType = OpenIdConnectResponseType.Code;
o.CallbackPath = "/signin-google"; // Or register the default "/signin-
oidc"
o.Scope.Add("email");
});
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Authentication.Google
Autenticação: propriedade HttpContext.Authentication
removida
A propriedade preterida Authentication em HttpContext foi removida.

Descrição das alterações

Como parte do dotnet/aspnetcore#6504 , a propriedade Authentication preterida em


HttpContext foi removida. A propriedade Authentication foi preterida desde a versão

2.0. Um guia de migração foi publicado para migrar o código usando essa propriedade
preterida para as novas APIs de substituição. As classes/APIs não utilizados restantes
relacionadas à pilha de autenticação antiga do ASP.NET Core 1.x foram removidas no
commit dotnet/aspnetcore@d7a7c65 .

Para ver a discussão, confira dotnet/aspnetcore#6533 .

Versão introduzida
3.0

Motivo da alteração
As APIs do ASP.NET Core 1.0 foram substituídas por métodos de extensão no
Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions.

Ação recomendada
Confira o Guia de migração.

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Http.Authentication.AuthenticateInfo
Microsoft.AspNetCore.Http.Authentication.AuthenticationManager
Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties
Microsoft.AspNetCore.Http.Features.Authentication.AuthenticateContext
Microsoft.AspNetCore.Http.Features.Authentication.ChallengeBehavior
Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext
Microsoft.AspNetCore.Http.Features.Authentication.DescribeSchemesContext
Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler
Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature.Ha
ndler
Microsoft.AspNetCore.Http.Features.Authentication.SignInContext
Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext
Microsoft.AspNetCore.Http.HttpContext.Authentication

Autenticação: tipos Newtonsoft.Json substituídos


No ASP.NET Core 3.0, os tipos Newtonsoft.Json usados em APIs de Autenticação foram
substituídos por tipos System.Text.Json . Exceto nos seguintes casos, o uso básico dos
pacotes de autenticação permanece inalterado:

Classes derivadas dos provedores OAuth, como as de aspnet-contrib .


Implementações avançadas de manipulação de declarações.

Para obter mais informações, confira dotnet/aspnetcore#7105 . Para ver a discussão,


confira dotnet/aspnetcore#7289 .

Versão introduzida
3.0

Ação recomendada
Para implementações do OAuth derivadas, a alteração mais comum é substituir
JObject.Parse por JsonDocument.Parse no CreateTicketAsync , como é mostrado aqui .
JsonDocument implementa IDisposable .

A seguinte lista descreve as alterações conhecidas:

ClaimAction.Run(JObject, ClaimsIdentity, String) se torna


ClaimAction.Run(JsonElement userData, ClaimsIdentity identity, string issuer) .

Todas as implementações derivadas de ClaimAction são afetadas da mesma forma.


ClaimActionCollectionMapExtensions.MapCustomJson(ClaimActionCollection,
String, Func<JObject,String>) torna-se MapCustomJson(this ClaimActionCollection
collection, string claimType, Func<JsonElement, string> resolver)
ClaimActionCollectionMapExtensions.MapCustomJson(ClaimActionCollection,
String, String, Func<JObject,String>) torna-se MapCustomJson(this
ClaimActionCollection collection, string claimType, string valueType,

Func<JsonElement, string> resolver)


No, OAuthCreatingTicketContext um construtor antigo foi removido e JObject foi
substituído por JsonElement . A propriedade User e o método RunClaimActions
foram atualizados para corresponder.
Success(JObject) agora aceita um parâmetro de tipo JsonDocument em vez de
JObject . A propriedade Response foi atualizada para corresponder.
OAuthTokenResponse agora é descartável e será descartado por OAuthHandler . As

implementações do OAuth derivadas que substituem ExchangeCodeAsync não


precisam descartar o JsonDocument ou o OAuthTokenResponse .
UserInformationReceivedContext.User foi alterado de JObject para JsonDocument .
TwitterCreatingTicketContext.User foi alterado de JObject para JsonElement .
O último parâmetro de
TwitterHandler.CreateTicketAsync(ClaimsIdentity,AuthenticationProperties,AccessTo
ken,JObject) foi alterado de JObject para JsonElement . O método de substituição
é TwitterHandler.CreateTicketAsync(ClaimsIdentity, AuthenticationProperties,
AccessToken, JsonElement).

Categoria

ASP.NET Core

APIs afetadas

Microsoft.AspNetCore.Authentication.Facebook
Microsoft.AspNetCore.Authentication.Google
Microsoft.AspNetCore.Authentication.MicrosoftAccount
Microsoft.AspNetCore.Authentication.OAuth
Microsoft.AspNetCore.Authentication.OpenIdConnect
Microsoft.AspNetCore.Authentication.Twitter

Autenticação: assinatura do ExchangeCodeAsync do


OAuthHandler alterada
No ASP.NET Core 3.0, a assinatura de OAuthHandler.ExchangeCodeAsync foi alterada de:

C#
protected virtual
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.OAuth.OAuthT
okenResponse> ExchangeCodeAsync(string code, string redirectUri) { throw
null; }

Para:

C#

protected virtual
System.Threading.Tasks.Task<Microsoft.AspNetCore.Authentication.OAuth.OAuthT
okenResponse>
ExchangeCodeAsync(Microsoft.AspNetCore.Authentication.OAuth.OAuthCodeExchang
eContext context) { throw null; }

Versão introduzida

3.0

Comportamento antigo

As cadeias de caracteres code e redirectUri foram passadas como argumentos


separados.

Novo comportamento
Code e RedirectUri são propriedades em OAuthCodeExchangeContext que podem ser

definidas por meio do construtor OAuthCodeExchangeContext . O novo tipo


OAuthCodeExchangeContext é o único argumento passado para

OAuthHandler.ExchangeCodeAsync .

Motivo da alteração
Essa alteração permite que parâmetros adicionais sejam fornecidos de maneira não
interruptiva. Não é necessário criar sobrecargas de ExchangeCodeAsync .

Ação recomendada

Construa um OAuthCodeExchangeContext com os valores apropriados de code e


redirectUri . Uma instância de AuthenticationProperties precisa ser fornecida. Essa
única instância de OAuthCodeExchangeContext pode ser passada para
OAuthHandler.ExchangeCodeAsync em vez de vários argumentos.

Categoria

ASP.NET Core

APIs afetadas

OAuthHandler<TOptions>.ExchangeCodeAsync(String, String)

Autorização: sobrecarga de AddAuthorization movida


para um assembly diferente
Os métodos AddAuthorization principais que costumavam residir em
Microsoft.AspNetCore.Authorization foram renomeado para AddAuthorizationCore . Os
métodos AddAuthorization antigos ainda existem, mas estão no assembly
Microsoft.AspNetCore.Authorization.Policy . Nos aplicativos que usam os dois
métodos, não deve haver nenhum impacto. Observe que
Microsoft.AspNetCore.Authorization.Policy agora é fornecido na estrutura

compartilhada em vez de em um pacote autônomo, conforme discutido em Estrutura


compartilhada: assemblies removidos de Microsoft.AspNetCore.App.

Versão introduzida
3.0

Comportamento antigo
Os métodos AddAuthorization existiam em Microsoft.AspNetCore.Authorization .

Novo comportamento
Os métodos AddAuthorization existem em Microsoft.AspNetCore.Authorization.Policy .
AddAuthorizationCore é o novo nome dos métodos antigos.

Motivo da alteração
AddAuthorization é um nome de método melhor para adicionar todos os serviços

comuns necessários para autorização.

Ação recomendada

Nesse caso, adicione uma referência a Microsoft.AspNetCore.Authorization.Policy ou


use AddAuthorizationCore .

Categoria
ASP.NET Core

APIs afetadas
Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions.Add
Authorization(IServiceCollection, Action<AuthorizationOptions>)

Autorização: IAllowAnonymous removido de


AuthorizationFilterContext.Filters
Do ASP.NET Core 3.0 em diante, o MVC não adiciona AllowAnonymousFilters a atributos
[AllowAnonymous] descobertos em controladores e métodos de ação. Essa alteração é
tratada localmente para derivativos de AuthorizeAttribute, mas é uma alteração
interruptiva e para implementações de IAsyncAuthorizationFilter e IAuthorizationFilter.
Essas implementações encapsuladas em um atributo [TypeFilter] são uma forma
popular e com suporte de obter autorização baseada em atributo fortemente tipada
quando a configuração e a injeção de dependência são necessárias.

Versão introduzida

3.0

Comportamento antigo

IAllowAnonymous aparecia na coleção AuthorizationFilterContext.Filters. O teste de


presença da interface foi era abordagem válida para substituir ou desabilitar o filtro em
métodos de controlador individuais.

Novo comportamento
IAllowAnonymous não aparece mais na coleção AuthorizationFilterContext.Filters . As

implementações de IAsyncAuthorizationFilter que dependem do comportamento


antigo normalmente causam as respostas intermitentes HTTP 401 Não Autorizado ou
HTTP 403 Proibido.

Motivo da alteração

Uma nova estratégia de roteamento de ponto de extremidade foi introduzida no


ASP.NET Core 3.0.

Ação recomendada
Pesquise os metadados de ponto de extremidade para IAllowAnonymous . Por exemplo:

C#

var endpoint = context.HttpContext.GetEndpoint();


if (endpoint?.Metadata?.GetMetadata<IAllowAnonymous>() != null)
{
}

Um exemplo dessa técnica é visto neste método HasAllowAnonymous .

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Autorização: implementações
IAuthorizationPolicyProvider exigem novo método
No ASP.NET Core 3.0, um novo método GetFallbackPolicyAsync foi adicionado a
IAuthorizationPolicyProvider . Essa política de fallback é usada pelo middleware de

autorização quando nenhuma política é especificada.

Para obter mais informações, confira dotnet/aspnetcore#9759 .

Versão introduzida
3.0

Comportamento antigo
As implementações de IAuthorizationPolicyProvider não exigiam um método
GetFallbackPolicyAsync .

Novo comportamento

As implementações de IAuthorizationPolicyProvider exigem um método


GetFallbackPolicyAsync .

Motivo da alteração
Um novo método era necessário para que o novo AuthorizationMiddleware fosse usado
quando nenhuma política era especificada.

Ação recomendada
Adicione o método GetFallbackPolicyAsync às implementações de
IAuthorizationPolicyProvider .

Categoria

ASP.NET Core

APIs afetadas

Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider

Cache: propriedade CompactOnMemoryPressure


removida
A versão ASP.NET Core 3.0 removeu as APIs obsoletas de MemoryCacheOptions .

Descrição das alterações


Essa alteração é um acompanhamento do aspnet/Caching#221 . Para ver a discussão,
confira dotnet/aspnetcore#1062 .
Versão introduzida
3.0

Comportamento antigo

A propriedade MemoryCacheOptions.CompactOnMemoryPressure estava disponível.

Novo comportamento

A propriedade MemoryCacheOptions.CompactOnMemoryPressure foi removida.

Motivo da alteração

A compactação automática do cache causava problemas. Para evitar um


comportamento inesperado, o cache só deve ser compactado quando necessário.

Ação recomendada
Para compactar o cache, faça downcast para MemoryCache e chame Compact quando
necessário.

Categoria

ASP.NET Core

APIs afetadas

MemoryCacheOptions.CompactOnMemoryPressure

Cache: Microsoft.Extensions.Caching.SqlServer usa um


novo pacote SqlClient
O pacote Microsoft.Extensions.Caching.SqlServer usará o novo pacote
Microsoft.Data.SqlClient em vez do pacote System.Data.SqlClient . Essa alteração
pode causar pequenas alterações interruptivas no comportamento. Para obter mais
informações, confira Introdução ao novo Microsoft.Data.SqlClient .

Versão introduzida
3.0

Comportamento antigo
O pacote Microsoft.Extensions.Caching.SqlServer usava o pacote
System.Data.SqlClient .

Novo comportamento

Agora, Microsoft.Extensions.Caching.SqlServer está usando o pacote


Microsoft.Data.SqlClient .

Motivo da alteração
Microsoft.Data.SqlClient é um novo pacote que é criado com base em

System.Data.SqlClient . É nele que todo o novo trabalho de recursos será feito a partir

de agora.

Ação recomendada

Os clientes não devem precisar se preocupar com essa alteração interruptiva, a menos
que estejam usando tipos retornados pelo pacote
Microsoft.Extensions.Caching.SqlServer e convertendo-os em tipos
System.Data.SqlClient . Por exemplo, se alguém estiver convertendo um DbConnection

no tipo SqlConnection antigo, será necessário alterar a conversão para o novo tipo
Microsoft.Data.SqlClient.SqlConnection .

Categoria

ASP.NET Core

APIs afetadas

Nenhum

Cache: tipos "pubterais" ResponseCaching alterados para


internos
No ASP.NET Core 3.0, os tipos "pubternal" em ResponseCaching foram alterados para
internal .

Além disso, as implementações padrão de IResponseCachingPolicyProvider e


IResponseCachingKeyProvider não são mais adicionadas aos serviços como parte do
método AddResponseCaching .

Descrição das alterações


No ASP.NET Core, os tipos "pubternal" são declarados como public , mas residem em
um namespace com o sufixo .Internal . Embora esses tipos sejam públicos, eles não
têm nenhuma política de suporte e estão sujeitos a alterações interruptivas. O uso
acidental desses tipos tem sido comum, resultando em alterações interruptivas nesses
projetos e limitando a capacidade de manutenção da estrutura.

Versão introduzida

3.0

Comportamento antigo

Esses tipos eram visíveis publicamente, mas não tinham suporte.

Novo comportamento

Esses tipos são internal agora.

Motivo da alteração
O escopo de internal reflete melhor a política sem suporte.

Ação recomendada
Copie os tipos que são usados pelo aplicativo ou a biblioteca.

Categoria
ASP.NET Core
APIs afetadas
Microsoft.AspNetCore.ResponseCaching.Internal.CachedResponse

Microsoft.AspNetCore.ResponseCaching.Internal.CachedVaryByRules

Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCache
Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCacheEntry

Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCachingKeyProvider
Microsoft.AspNetCore.ResponseCaching.Internal.IResponseCachingPolicyProvider

Microsoft.AspNetCore.ResponseCaching.Internal.MemoryResponseCache

Microsoft.AspNetCore.ResponseCaching.Internal.ResponseCachingContext
Microsoft.AspNetCore.ResponseCaching.Internal.ResponseCachingKeyProvider

Microsoft.AspNetCore.ResponseCaching.Internal.ResponseCachingPolicyProvider
Microsoft.AspNetCore.ResponseCaching.ResponseCachingMiddleware.ResponseCa
chingMiddleware(RequestDelegate, IOptions<ResponseCachingOptions>,
ILoggerFactory, IResponseCachingPolicyProvider, IResponseCache,
IResponseCachingKeyProvider)

Proteção de dados: DataProtection.Blobs usa novas APIs


do Armazenamento do Azure
Azure.Extensions.AspNetCore.DataProtection.Blobs depende das bibliotecas do
Armazenamento do Azure . Nessas bibliotecas, foram renomeados os assemblies, os
pacotes e os namespaces. Do ASP.NET Core 3.0 em diante,
Azure.Extensions.AspNetCore.DataProtection.Blobs usa as novas APIs e pacotes

prefixados com Azure.Storage. .

Para perguntas sobre as APIs do Armazenamento do Azure, use


https://github.com/Azure/azure-storage-net . Para conferir a discussão sobre esse
problema, veja dotnet/aspnetcore#19570 .

Versão introduzida

3.0

Comportamento antigo

O pacote referenciava o pacote NuGet WindowsAzure.Storage . O pacote referencia o


pacote NuGet Microsoft.Azure.Storage.Blob .
Novo comportamento
O pacote referencia o pacote NuGet Azure.Storage.Blob .

Motivo da alteração

Essa alteração permite que Azure.Extensions.AspNetCore.DataProtection.Blobs seja


migrado para os pacotes recomendados do Armazenamento do Azure.

Ação recomendada
Se você ainda precisar usar as APIs do Armazenamento do Azure mais antigas com o
ASP.NET Core 3.0, adicione uma dependência direta ao pacote WindowsAzure.Storage
ou Microsoft.Azure.Storage . Esse pacote pode ser instalado junto com as novas APIs
do Azure.Storage .

Em muitos casos, a atualização envolve apenas a alteração das instruções using para
usar os novos namespaces:

diff

- using Microsoft.WindowsAzure.Storage;
- using Microsoft.WindowsAzure.Storage.Blob;
- using Microsoft.Azure.Storage;
- using Microsoft.Azure.Storage.Blob;
+ using Azure.Storage;
+ using Azure.Storage.Blobs;

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Hospedagem: AspNetCoreModule V1 removido do Pacote


de Hospedagem do Windows
Do ASP.NET Core 3.0 em diante, o Pacote de Hospedagem do Windows não conterá o
AspNetCoreModule (ANCM) V1.
O ANCM V2 é compatível com a versões anteriores ANCM OutOfProcess e é
recomendado para uso com aplicativos ASP.NET Core 3.0.

Para ver a discussão, confira dotnet/aspnetcore#7095 .

Versão introduzida
3.0

Comportamento antigo
O ANCM V1 está incluído no Pacote de Hospedagem do Windows.

Novo comportamento
O ANCM V1 não está incluído no Pacote de Hospedagem do Windows.

Motivo da alteração
O ANCM V2 é compatível com a versões anteriores ANCM OutOfProcess e é
recomendado para uso com aplicativos ASP.NET Core 3.0.

Ação recomendada
Use o ANCM V2 com os aplicativos ASP.NET Core 3.0.

Se o ANCM V1 for necessário, ele poderá ser instalado usando o Pacote de


Hospedagem do Windows do ASP.NET Core 2.1 ou 2.2.

Essa alteração interromperá os aplicativos ASP.NET Core 3.0 que:

Optaram explicitamente por usar o ANCM V1 com


<AspNetCoreModuleName>AspNetCoreModule</AspNetCoreModuleName> .

Têm um arquivo web.config personalizado com <add name="aspNetCore" path="*"


verb="*" modules="AspNetCoreModule" resourceType="Unspecified" /> .

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Hospedagem: o host genérico restringe a injeção de


construtor de inicialização
Os únicos tipos aos quais o host genérico dá suporte para injeção de construtor de
classe Startup são IHostEnvironment , IWebHostEnvironment e IConfiguration . Os
aplicativos que usam WebHost não foram afetados.

Descrição das alterações

Antes do ASP.NET Core 3.0, a injeção de construtor podia ser usada para tipos
arbitrários no construtor da classe Startup . No ASP.NET Core 3.0, a pilha da Web foi
mudou para uma plataforma de biblioteca de host genérica. Você pode ver a alteração
no arquivo Program.cs dos modelos:

ASP.NET Core 2.x:

https://github.com/dotnet/aspnetcore/blob/5cb615fcbe8559e49042e93394008077e304
54c0/src/Templating/src/Microsoft.DotNet.Web.ProjectTemplates/content/EmptyWeb-
CSharp/Program.cs#L20-L22

ASP.NET Core 3.0:

https://github.com/dotnet/aspnetcore/blob/b1ca2c1155da3920f0df5108b9fedbe82efaa
11c/src/ProjectTemplates/Web.ProjectTemplates/content/EmptyWeb-
CSharp/Program.cs#L19-L24

O Host usa um contêiner de DI (injeção de dependência) para compilar o aplicativo. O


WebHost usa dois contêineres: um para o host e outro para o aplicativo. Como resultado,

o construtor Startup não dá mais suporte à injeção de serviço personalizada. Somente


IHostEnvironment , IWebHostEnvironment e IConfiguration pode ser injetado. Essa

alteração impede problemas de DI, como a criação duplicada de um serviço singleton.

Versão introduzida
3.0

Motivo da alteração
Essa alteração é consequência da mudança de plataforma da pilha da Web na biblioteca
de host genérica.

Ação recomendada

Injete serviços na assinatura do método Startup.Configure . Por exemplo:

C#

public void Configure(IApplicationBuilder app, IOptions<MyOptions> options)

Categoria

ASP.NET Core

APIs afetadas

Nenhum

Hospedagem: redirecionamento de HTTPS habilitado


para aplicativos fora de processo do IIS
A versão 13.0.19218.0 do ANCM (ASP.NET Core Module) para hospedagem por meio do
IIS fora do processo permite um recurso de redirecionamento HTTPS existente para
ASP.NET Core aplicativos 3.0 e 2.2 existentes.

Para ver a discussão, confira dotnet/AspNetCore#15243 .

Versão introduzida

3.0

Comportamento antigo

O modelo de projeto do ASP.NET Core 2.1 introduziu pela primeira vez o suporte para
métodos de middleware HTTPS, como UseHttpsRedirection e UseHsts. A habilitação do
redirecionamento HTTPS exigia a adição da configuração, já que os aplicativos em
desenvolvimento não usam a porta padrão 443. HSTS (HTTP Strict Transport Security)
só estará ativo se a solicitação já estiver usando HTTPS. Localhost é ignorado por
padrão.
Novo comportamento
No ASP.NET Core 3.0, o cenário HTTPS do IIS foi aprimorado . Com o aprimoramento,
um aplicativo pode descobrir as portas HTTPS do servidor e fazer UseHttpsRedirection
funcionar por padrão. O componente em processo realiza a descoberta da porta com o
recurso IServerAddresses , o que afeta apenas aplicativos ASP.NET Core 3.0 porque a
biblioteca em processo é usada com a estrutura. O componente fora do processo foi
alterado para adicionar automaticamente a variável de ambiente
ASPNETCORE_HTTPS_PORT . Essa alteração afetou os aplicativos ASP.NET Core 2.2 e 3.0

porque o componente fora de processo é compartilhado globalmente. Os aplicativos


ASP.NET Core 2.1 não são afetados porque usam uma versão anterior do ANCM por
padrão.

O comportamento anterior foi modificado no ASP.NET Core 3.0.1 e 3.1.0 versão prévia 3
para reverter as alterações de comportamento no ASP.NET Core 2.x. Essas alterações
afetam apenas os aplicativos fora de processo do IIS.

Conforme detalhado acima, a instalação do ASP.NET Core 3.0.0 teve o efeito colateral de
também ativar o middleware UseHttpsRedirection nos aplicativos ASP.NET Core 2.x. Foi
feita uma alteração no ANCM no ASP.NET Core 3.0.1 e 3.1.0 versão prévia 3, de modo
que a instalação deles não tem mais esse efeito em aplicativos ASP.NET Core 2.x. A
variável de ambiente ASPNETCORE_HTTPS_PORT que o ANCM populava no ASP.NET Core
3.0.0 foi alterada para ASPNETCORE_ANCM_HTTPS_PORT no ASP.NET Core 3.0.1 e 3.1.0 versão
prévia 3. UseHttpsRedirection também foi atualizado nessas versões para entender as
variáveis novas e antigas. O ASP.NET Core 2.x não será atualizado. Como resultado, ele
será revertido para o comportamento anterior de ser desabilitado por padrão.

Motivo da alteração
Funcionalidade aprimorada do ASP.NET Core 3.0.

Ação recomendada
Nenhuma ação será necessária se você quiser que todos os clientes usem HTTPS. Para
permitir que alguns clientes usem HTTP, execute uma das seguintes etapas:

Remova as chamadas para UseHttpsRedirection e UseHsts do método do projeto


Startup.Configure e reimplante o aplicativo.

No arquivo web.config, defina a variável de ambiente ASPNETCORE_HTTPS_PORT como


uma cadeia de caracteres vazia. Essa alteração pode ocorrer diretamente no
servidor sem reimplantar o aplicativo. Por exemplo:

XML

<aspNetCore processPath="dotnet" arguments=".\WebApplication3.dll"


stdoutLogEnabled="false" stdoutLogFile="\\?\%home%\LogFiles\stdout" >
<environmentVariables>
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="" />
</environmentVariables>
</aspNetCore>

UseHttpsRedirection ainda pode ser:

Ativado manualmente no ASP.NET Core 2.x definindo a variável de ambiente


ASPNETCORE_HTTPS_PORT como o número da porta apropriado (443 na maioria dos
cenários de produção).
Desativado no ASP.NET Core 3.x definindo ASPNETCORE_ANCM_HTTPS_PORT com um
valor de cadeia de caracteres vazio. Esse valor é definido da mesma forma que o
exemplo anterior ASPNETCORE_HTTPS_PORT .

Os computadores que executam aplicativos ASP.NET Core 3.0.0 devem instalar o


runtime do ASP.NET Core 3.0.1 antes de instalar o ANCM do ASP.NET Core 3.1.0 versão
prévia 3. Isso garante que UseHttpsRedirection continue operando conforme o
esperado para os aplicativos ASP.NET Core 3.0.

No Serviço de Aplicativo do Azure, o ANCM é implantado em um agendamento


separado do runtime devido à sua natureza global. O ANCM foi implantado no Azure
com essas alterações após a implantação do ASP.NET Core 3.0.1 e 3.1.0.

Categoria
ASP.NET Core

APIs afetadas
HttpsPolicyBuilderExtensions.UseHttpsRedirection(IApplicationBuilder)

Hospedagem: tipos IHostingEnvironment e


IApplicationLifetime marcados como obsoletos e
substituídos
Novos tipos foram introduzidos para substituir os tipos existentes IHostingEnvironment
e IApplicationLifetime .

Versão introduzida

3.0

Comportamento antigo

Havia dois tipos diferentes IHostingEnvironment e IApplicationLifetime de


Microsoft.Extensions.Hosting e Microsoft.AspNetCore.Hosting .

Novo comportamento
Os tipos antigos foram marcados como obsoletos e substituídos por novos tipos.

Motivo da alteração
Quando Microsoft.Extensions.Hosting foi introduzido no ASP.NET Core 2.1, alguns
tipos como IHostingEnvironment e IApplicationLifetime foram copiados de
Microsoft.AspNetCore.Hosting . Algumas alterações do ASP.NET Core 3.0 fazem com que
os aplicativos incluam os namespaces Microsoft.Extensions.Hosting e
Microsoft.AspNetCore.Hosting . Qualquer uso desses tipos duplicados causa um erro de
compilador de "referência ambígua" quando os dois namespaces são referenciados.

Ação recomendada
Substitua todos os usos dos tipos antigos pelos tipos recém-introduzidos, desta forma:

Tipos obsoletos (aviso):

Microsoft.Extensions.Hosting.IHostingEnvironment
Microsoft.AspNetCore.Hosting.IHostingEnvironment
Microsoft.Extensions.Hosting.IApplicationLifetime
Microsoft.AspNetCore.Hosting.IApplicationLifetime
Microsoft.Extensions.Hosting.EnvironmentName
Microsoft.AspNetCore.Hosting.EnvironmentName

Novos tipos:

Microsoft.Extensions.Hosting.IHostEnvironment
Microsoft.AspNetCore.Hosting.IWebHostEnvironment : IHostEnvironment

Microsoft.Extensions.Hosting.IHostApplicationLifetime
Microsoft.Extensions.Hosting.Environments

Os métodos novos IHostEnvironment IsDevelopment e IsProduction de extensão estão


no namespace Microsoft.Extensions.Hosting . Talvez esse namespace precise ser
adicionado ao projeto.

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Hosting.EnvironmentName
Microsoft.AspNetCore.Hosting.IApplicationLifetime
Microsoft.AspNetCore.Hosting.IHostingEnvironment
Microsoft.Extensions.Hosting.EnvironmentName
Microsoft.Extensions.Hosting.IApplicationLifetime
Microsoft.Extensions.Hosting.IHostingEnvironment

Hospedagem: ObjectPoolProvider removido das


dependências do WebHostBuilder
Como parte do aprimoramento do ASP.NET Core, o ObjectPoolProvider foi removido
do conjunto principal de dependências. Os componentes específicos que dependem do
ObjectPoolProvider agora o adicionam por conta própria.

Para ver a discussão, confira dotnet/aspnetcore#5944 .

Versão introduzida
3.0

Comportamento antigo
WebHostBuilder fornece ObjectPoolProvider por padrão no contêiner de DI.

Novo comportamento
WebHostBuilder não fornece mais ObjectPoolProvider por padrão no contêiner de DI.

Motivo da alteração
Essa mudança foi feita para fazer o ASP.NET Core valer mais a pena.

Ação recomendada
Se o componente exige o ObjectPoolProvider , ele precisará ser adicionado às
dependências por meio do IServiceCollection .

Categoria

ASP.NET Core

APIs afetadas

Nenhum

HTTP: extensibilidade de DefaultHttpContext removida


Como parte dos aprimoramentos de desempenho do ASP.NET Core 3.0, a
extensibilidade DefaultHttpContext foi removida. A classe agora é sealed . Para obter
mais informações, confira dotnet/aspnetcore#6504 .

Se os testes de unidade usarem Mock<DefaultHttpContext> , use Mock<HttpContext> ou


new DefaultHttpContext() .

Para ver a discussão, confira dotnet/aspnetcore#6534 .

Versão introduzida
3.0

Comportamento antigo
As classes podem ser derivadas de DefaultHttpContext .

Novo comportamento
As classes não podem ser derivadas de DefaultHttpContext .

Motivo da alteração
A extensibilidade foi fornecida inicialmente para permitir o agrupamento do
HttpContext , mas gerou uma complexidade desnecessária e impediu outras
otimizações.

Ação recomendada
Se você estiver usando o Mock<DefaultHttpContext> em testes de unidade, comece a
usar o Mock<HttpContext> .

Categoria

ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Http.DefaultHttpContext

HTTP: constantes HeaderNames alteradas para estáticas


somente leitura
Começando no ASP.NET Core 3.0 versão prévia 5, os campos em
Microsoft.Net.Http.Headers.HeaderNames foram alterados de const para static
readonly .

Para ver a discussão, confira dotnet/aspnetcore#9514 .

Versão introduzida
3.0

Comportamento antigo
Esses campos eram const .

Novo comportamento
Esses campos agora são static readonly .

Motivo da alteração
A alteração:

Impede que os valores sejam inseridos entre limites de assembly, permitindo


correções de valor conforme o necessário.
Acelera as verificações de igualdade de referência.

Ação recomendada

Recompilar em relação ao 3.0. O código-fonte que usa esses campos das seguintes
maneiras não pode mais usá-lo:

Como um argumento de atributo


Como um case em uma instrução switch
Ao definir outro const

Para contornar a alteração interruptiva, passe a usar constantes de nome de cabeçalho


autodefinidas ou literais de cadeia de caracteres.

Categoria
ASP.NET Core

APIs afetadas

Microsoft.Net.Http.Headers.HeaderNames

HTTP: alterações na infraestrutura do corpo da resposta


A infraestrutura que dá suporte a um corpo da resposta HTTP foi alterada. Se você
estiver usando o HttpResponse diretamente, não precisará fazer nenhuma alteração de
código. Leia mais se você estiver encapsulando ou substituindo o HttpResponse.Body ou
acessando o HttpContext.Features .

Versão introduzida

3.0
Comportamento antigo
Havia três APIs associadas ao corpo da resposta HTTP:

IHttpResponseFeature.Body

IHttpSendFileFeature.SendFileAsync
IHttpBufferingFeature.DisableResponseBuffering

Novo comportamento
Se você substituir o HttpResponse.Body , ele substituirá todo o IHttpResponseBodyFeature
por um wrapper no fluxo fornecido usando StreamResponseBodyFeature para fornecer as
implementações padrão para todas as APIs esperadas. A configuração do fluxo original
reverte essa alteração.

Motivo da alteração
A motivação é combinar as APIs do corpo da resposta em uma só nova interface de
recurso.

Ação recomendada

Use IHttpResponseBodyFeature no lugar em que você usava IHttpResponseFeature.Body ,


IHttpSendFileFeature ou IHttpBufferingFeature .

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Http.Features.IHttpBufferingFeature
Microsoft.AspNetCore.Http.Features.IHttpResponseFeature.Body
Microsoft.AspNetCore.Http.Features.IHttpSendFileFeature

HTTP: alguns padrões do SameSite de cookie foram


alterados para None
SameSite é uma opção para cookies que pode ajudar a mitigar alguns ataques de CSRF

(solicitação intersite forjada). Quando essa opção foi introduzida inicialmente, padrões
inconsistentes foram usados em várias APIs do ASP.NET Core. A inconsistência levou a
resultados confusos. Do ASP.NET Core 3.0 em diante, esses padrões estão mais
alinhados. Você precisa aceitar esse recurso em cada componente.

Versão introduzida
3.0

Comportamento antigo
APIs do ASP.NET Core semelhantes usavam valores padrão de SameSiteMode
diferentes. Um exemplo da inconsistência é visto em
HttpResponse.Cookies.Append(String, String) e HttpResponse.Cookies.Append(String,
String, CookieOptions) , que usavam como padrão SameSiteMode.None e

SameSiteMode.Lax , respectivamente.

Novo comportamento

Todas as APIs afetadas usam como padrão SameSiteMode.None .

Motivo da alteração

O valor padrão foi alterado para que SameSite seja um recurso de aceitação.

Ação recomendada
Cada componente que emite cookies precisa decidir se SameSite é apropriado para os
respectivos cenários. Examine o uso das APIs afetadas e reconfigure o SameSite
conforme o necessário.

Categoria
ASP.NET Core

APIs afetadas
IResponseCookies.Append(String, String, CookieOptions)
CookiePolicyOptions.MinimumSameSitePolicy

HTTP: E/S síncrona desabilitada em todos os servidores


Do ASP.NET Core 3.0 em diante, as operações de servidor síncronas são desabilitadas
por padrão.

Descrição das alterações


AllowSynchronousIO é uma opção em cada servidor que habilita ou desabilita APIs de

E/S síncronas como HttpRequest.Body.Read , HttpResponse.Body.Write e Stream.Flush .


Essas APIs têm sido uma fonte de privação de threads e travamentos de aplicativos. Do
ASP.NET Core 3.0 versão prévia 3 em diante, essas operações síncronas são
desabilitadas por padrão.

Servidores afetados:

Kestrel
HttpSys
IIS em processo
TestServer

Espera-se erros semelhantes a:

Synchronous operations are disallowed. Call ReadAsync or set


AllowSynchronousIO to true instead.

Synchronous operations are disallowed. Call WriteAsync or set


AllowSynchronousIO to true instead.

Synchronous operations are disallowed. Call FlushAsync or set

AllowSynchronousIO to true instead.

Cada servidor tem uma opção AllowSynchronousIO que controla esse comportamento e
o padrão para todos eles agora é false .

O comportamento também pode ser substituído por solicitação como uma mitigação
temporária. Por exemplo:

C#

var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();


if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}

Se você tiver problemas com um TextWriter ou outro fluxo chamando uma API
síncrona em Dispose , chame a nova API DisposeAsync .

Para ver a discussão, confira dotnet/aspnetcore#7644 .

Versão introduzida
3.0

Comportamento antigo
HttpRequest.Body.Read , HttpResponse.Body.Write e Stream.Flush eram permitidos por

padrão.

Novo comportamento

Estas APIs síncronas são não permitidas por padrão:

Espera-se erros semelhantes a:

Synchronous operations are disallowed. Call ReadAsync or set

AllowSynchronousIO to true instead.


Synchronous operations are disallowed. Call WriteAsync or set

AllowSynchronousIO to true instead.


Synchronous operations are disallowed. Call FlushAsync or set

AllowSynchronousIO to true instead.

Motivo da alteração
Essas APIs síncronas têm sido uma fonte de privação de threads e travamentos de
aplicativos. Do ASP.NET Core 3.0 versão prévia 3 em diante, as operações síncronas são
desabilitadas por padrão.

Ação recomendada
Use as versões assíncronas dos métodos. O comportamento também pode ser
substituído por solicitação como uma mitigação temporária.
C#

var syncIOFeature = HttpContext.Features.Get<IHttpBodyControlFeature>();


if (syncIOFeature != null)
{
syncIOFeature.AllowSynchronousIO = true;
}

Categoria
ASP.NET Core

APIs afetadas
Stream.Flush
Stream.Read
Stream.Write

Identity: sobrecarga do método AddDefaultUI removida


Do ASP.NET Core 3.0 em diante, a sobrecarga do método
IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder, UIFramework) não existe mais.

Versão introduzida
3.0

Motivo da alteração
Essa alteração foi resultado da adoção do recurso de ativos Web estáticos.

Ação recomendada
Chame IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder) em vez da sobrecarga
que usa dois argumentos. Se você estiver usando o Bootstrap 3, adicione também a
seguinte linha a um elemento <PropertyGroup> no arquivo de projeto:

XML

<IdentityUIFrameworkVersion>Bootstrap3</IdentityUIFrameworkVersion>
Categoria
ASP.NET Core

APIs afetadas

IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder,UIFramework)

Identity: a versão padrão da interface do usuário do


Bootstrap foi alterada
Do ASP.NET Core 3.0 em diante, a interface do usuário do Identity usa a versão 4 do
Bootstrap.

Versão introduzida
3.0

Comportamento antigo

A chamada de método services.AddDefaultIdentity<IdentityUser>().AddDefaultUI();


era a mesma que services.AddDefaultIdentity<IdentityUser>
().AddDefaultUI(UIFramework.Bootstrap3);

Novo comportamento

A chamada de método services.AddDefaultIdentity<IdentityUser>().AddDefaultUI();


é a mesma que services.AddDefaultIdentity<IdentityUser>
().AddDefaultUI(UIFramework.Bootstrap4);

Motivo da alteração
O Bootstrap 4 foi lançado durante o período do ASP.NET Core 3.0.

Ação recomendada
Você será afetado por essa alteração se usar a interface do usuário padrão do Identity e
a tiver adicionado em Startup.ConfigureServices , conforme é mostrado no seguinte
exemplo:
C#

services.AddDefaultIdentity<IdentityUser>().AddDefaultUI();

Execute uma das seguintes ações:

Migre o aplicativo para usar o Bootstrap 4 seguindo o respectivo guia de


migração .

Atualize o Startup.ConfigureServices para impor o uso do Bootstrap 3. Por


exemplo:

C#

services.AddDefaultIdentity<IdentityUser>
().AddDefaultUI(UIFramework.Bootstrap3);

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Identity: SignInAsync gera exceção para identidade não


autenticada
Por padrão, SignInAsync gera uma exceção para entidades de segurança/identidades
em que IsAuthenticated é false .

Versão introduzida
3.0

Comportamento antigo
SignInAsync aceita quaisquer entidades de segurança/identidades, incluindo

identidades nas quais IsAuthenticated é false .


Novo comportamento
Por padrão, SignInAsync gera uma exceção para entidades de segurança/identidades
em que IsAuthenticated é false . Há um novo sinalizador para suprimir esse
comportamento, mas o comportamento padrão mudou.

Motivo da alteração

O comportamento antigo era problemático porque, por padrão, essas entidades de


segurança eram rejeitadas por [Authorize] / RequireAuthenticatedUser() .

Ação recomendada
No ASP.NET Core 3.0 versão prévia 6, há um sinalizador RequireAuthenticatedSignIn em
AuthenticationOptions que é true por padrão. Defina esse sinalizador como false

para restaurar o comportamento antigo.

Categoria

ASP.NET Core

APIs afetadas

Nenhum

Identity: construtor SignInManager aceita novo


parâmetro
Do ASP.NET Core 3.0 em diante, um novo parâmetro IUserConfirmation<TUser> foi
adicionado ao construtor SignInManager . Para obter mais informações, confira
dotnet/aspnetcore#8356 .

Versão introduzida
3.0

Motivo da alteração
A motivação para a alteração foi adicionar suporte para novos fluxos de
email/confirmação no Identity.
Ação recomendada
Se estiver construindo um SignInManager manualmente, forneça uma implementação de
IUserConfirmation ou pegue uma da injeção de dependência a ser fornecida.

Categoria
ASP.NET Core

APIs afetadas
SignInManager<TUser>

Identity: interface do usuário usa o recurso de ativos Web


estáticos
O ASP.NET Core 3.0 introduziu um recurso de ativos Web estáticos e a interface do
usuário do Identity o adotou.

Descrição das alterações


Como resultado da adoção do recurso de ativos Web estáticos pela interface do usuário
do Identity:

A seleção da estrutura é realizada usando a propriedade


IdentityUIFrameworkVersion no arquivo de projeto.

O Bootstrap 4 é a estrutura de interface do usuário padrão para a interface do


usuário do Identity. O Bootstrap 3 atingiu o fim da vida útil e você deve considerar
a migração para uma versão com suporte.

Versão introduzida

3.0

Comportamento antigo

A estrutura de interface do usuário padrão do Identity era o Bootstrap 3. A estrutura da


interface do usuário podia ser configurada usando um parâmetro para a chamada de
método AddDefaultUI em Startup.ConfigureServices .
Novo comportamento
A estrutura de interface do usuário padrão do Identity é o Bootstrap 4. A estrutura da
interface do usuário precisa ser configurada no arquivo de projeto, não na chamada do
método AddDefaultUI .

Motivo da alteração

A adoção do recurso de ativos Web estáticos exigiu que a configuração da estrutura de


interface do usuário mudasse para o MSBuild. A decisão sobre qual estrutura inserir é
uma decisão de tempo de compilação, não uma decisão de tempo de execução.

Ação recomendada

Examine a interface do usuário do site para garantir que os novos componentes do


Bootstrap 4 sejam compatíveis. Se necessário, use a propriedade
IdentityUIFrameworkVersion do MSBuild para reverter para o Bootstrap 3. Adicione a

propriedade a um elemento <PropertyGroup> no arquivo de projeto:

XML

<IdentityUIFrameworkVersion>Bootstrap3</IdentityUIFrameworkVersion>

Categoria

ASP.NET Core

APIs afetadas

IdentityBuilderUIExtensions.AddDefaultUI(IdentityBuilder, UIFramework)

Kestrel: adaptadores de conexão removidos


Como parte da mudança para mover as APIs "pubterais" para public , o conceito de um
IConnectionAdapter foi removido do Kestrel. Os adaptadores de conexão estão sendo
substituídos pelo middleware de conexão (semelhante ao middleware HTTP no pipeline
do ASP.NET Core, mas para conexões de nível inferior). Os logs de conexões e HTTPS
foram movidos dos adaptadores de conexão para o middleware de conexão. Esses
métodos de extensão devem continuar funcionando perfeitamente, mas os detalhes da
implementação foram alterados.
Para obter mais informações, confira dotnet/aspnetcore#11412 . Para ver a discussão,
confira dotnet/aspnetcore#11475 .

Versão introduzida

3.0

Comportamento antigo

Os componentes de extensibilidade do Kestrel eram criados usando


IConnectionAdapter .

Novo comportamento
Os componentes de extensibilidade do Kestrel são criados como middleware .

Motivo da alteração
Essa alteração se destina a fornecer uma arquitetura de extensibilidade mais flexível.

Ação recomendada
Converta todas as implementações de IConnectionAdapter para usar o novo padrão de
middleware, conforme mostrado aqui .

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Server.Kestrel.Core.Adapter.Internal.IConnectionAdapter

Kestrel: assembly HTTPS vazio removido


O assembly Microsoft.AspNetCore.Server.Kestrel.Https foi removido.

Versão introduzida

3.0
Motivo da alteração
No ASP.NET Core 2.1, o conteúdo de Microsoft.AspNetCore.Server.Kestrel.Https foi
movido para Microsoft.AspNetCore.Server.Kestrel.Core. Essa alteração foi feita de
maneira não interruptiva usando atributos [TypeForwardedTo] .

Ação recomendada

As bibliotecas que fazem referência ao


Microsoft.AspNetCore.Server.Kestrel.Https 2.0 devem atualizar todas as

dependências do ASP.NET Core para a versão 2.1 ou posterior. Caso contrário, elas
poderão ser interrompidas quando carregadas em um aplicativo ASP.NET Core 3.0.
Os aplicativos e as bibliotecas direcionados ao ASP.NET Core 2.1 e posterior
devem remover todas as referências diretas ao pacote NuGet
Microsoft.AspNetCore.Server.Kestrel.Https .

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Kestrel: cabeçalhos de trailer de solicitação movidos para


a nova coleção
Em versões anteriores, o Kestrel adicionava cabeçalhos de trailer em partes HTTP/1.1 à
coleção de cabeçalhos de solicitação quando o corpo da solicitação era lido até o final.
Esse comportamento causava preocupações sobre a ambiguidade entre cabeçalhos e
trailers. A decisão foi tomada para mover os trailers para uma nova coleção.

Os trailers de solicitação HTTP/2 não estavam disponíveis no ASP.NET Core 2.2, mas
agora também estão disponíveis nessa nova coleção no ASP.NET Core 3.0.

Novos métodos de extensão de solicitação foram adicionados para acessar esses


trailers.

Os trailers HTTP/1.1 ficam disponíveis depois que todo o corpo da solicitação é lido.
Os trailers HTTP/2 ficam disponíveis quando são recebidos do cliente. O cliente não
enviará os trailers até que todo o corpo da solicitação tenha sido pelo menos
armazenado em buffer pelo servidor. Talvez seja necessário ler o corpo da solicitação
para liberar espaço no buffer. Os trailers estarão sempre disponíveis quando você lê o
corpo da solicitação até o final. Os trailers marcam o fim do corpo.

Versão introduzida
3.0

Comportamento antigo
Os cabeçalhos de trailer de solicitação seriam adicionados à coleção
HttpRequest.Headers .

Novo comportamento
Os cabeçalhos de trailer de solicitação não estão presentes na coleção
HttpRequest.Headers . Use os seguintes métodos de extensão em HttpRequest para
acessá-los:

GetDeclaredTrailers() – Obtém o cabeçalho "Trailer" da solicitação que lista quais


trailers são esperados após o corpo.
SupportsTrailers() – Indica se a solicitação dá suporte ao recebimento de

cabeçalhos de trailer.
CheckTrailersAvailable() – Determina se a solicitação dá suporte a trailers e se

eles estão disponíveis para leitura.


GetTrailer(string trailerName) – Obtém o cabeçalho de trailer solicitado da

resposta.

Motivo da alteração
Trailers são um recurso fundamental em cenários como gRPC. A mesclagem dos trailers
nos cabeçalhos de solicitação era confusa para os usuários.

Ação recomendada

Use os métodos de extensão relacionados a trailer no HttpRequest para acessar os


trailers.
Categoria
ASP.NET Core

APIs afetadas

HttpRequest.Headers

Kestrel: abstrações de transporte removidas e alteradas


para públicas
Como parte do abandono das APIs "pubterais", as APIs da camada de transporte do
Kestrel são expostas como uma interface pública na biblioteca
Microsoft.AspNetCore.Connections.Abstractions .

Versão introduzida
3.0

Comportamento antigo
As abstrações relacionadas ao transporte estavam disponíveis na biblioteca
Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions .
A propriedade ListenOptions.NoDelay estava disponível.

Novo comportamento
A interface IConnectionListener foi introduzida na biblioteca
Microsoft.AspNetCore.Connections.Abstractions para expor a funcionalidade mais

usada da biblioteca ...Transport.Abstractions .


O NoDelay agora está disponível nas opções de transporte ( LibuvTransportOptions
e SocketTransportOptions ).
SchedulingMode não está mais disponível.

Motivo da alteração
O ASP.NET Core 3.0 abandonou as APIs "pubterais".

Ação recomendada
Categoria
ASP.NET Core

APIs afetadas

Nenhum

Localização: ResourceManagerWithCultureStringLocalizer
e WithCulture marcados como obsoletos
A classe ResourceManagerWithCultureStringLocalizer e o membro da interface
WithCulture geralmente são motivos de confusão para os usuários de localização,
principalmente quando eles criam a própria implementação de IStringLocalizer . Esses
itens dão ao usuário a impressão de que uma instância de IStringLocalizer varia de
acordo com o idioma e o recurso. Na realidade, as instâncias devem variar apenas por
recurso. O idioma procurado é determinado pelo CultureInfo.CurrentUICulture em
tempo de execução. Para eliminar a fonte de confusão, as APIs foram marcadas como
obsoletas no ASP.NET Core 3.0 versão prévia 3. As APIs serão removidas em uma versão
futura.

Para ver o contexto, confira dotnet/aspnetcore#3324 . Para ver a discussão, confira


dotnet/aspnetcore#7756 .

Versão introduzida

3.0

Comportamento antigo

Os métodos não eram marcados como Obsolete .

Novo comportamento

Os métodos são marcados como Obsolete .

Motivo da alteração
As APIs representavam um caso de uso que não é recomendado. Havia confusão sobre
o design da localização.
Ação recomendada
Nesse caso, a recomendação é usar ResourceManagerStringLocalizer . Deixe que a
cultura seja definida pelo CurrentCulture . Se não houver essa opção, crie e use uma
cópia de ResourceManagerWithCultureStringLocalizer .

Categoria

ASP.NET Core

APIs afetadas

ResourceManagerWithCultureStringLocalizer
ResourceManagerStringLocalizer.WithCulture

Log: classe DebugLogger alterada para interna


Antes do ASP.NET Core 3.0, o modificador de acesso de DebugLogger era public . No
ASP.NET Core 3.0, o modificador de acesso foi alterado para internal .

Versão introduzida
3.0

Motivo da alteração
A alteração está sendo feita para:

Impor consistência com outras implementações de agente, como ConsoleLogger .


Reduzir a superfície da API.

Ação recomendada

Use o método de extensão AddDebug ILoggingBuilder para habilitar o registro em log


de depuração. Além disso, DebugLoggerProvider ainda é public quando o serviço
precisa ser registrado manualmente.

Categoria
ASP.NET Core
APIs afetadas
Microsoft.Extensions.Logging.Debug.DebugLogger

MVC: sufixo assíncrono cortado de nomes de ação do


controlador
Como parte da solução de dotnet/aspnetcore#4849 , o ASP.NET Core MVC corta o
sufixo Async de nomes de ação por padrão. Do ASP.NET Core 3.0 em diante, essa
alteração afeta o roteamento e a geração de link.

Versão introduzida
3.0

Comportamento antigo
Considere o seguinte controlador do ASP.NET Core MVC:

C#

public class ProductController : Controller


{
public async IActionResult ListAsync()
{
var model = await DbContext.Products.ToListAsync();
return View(model);
}
}

A ação é roteável por meio de Product/ListAsync . A geração de link requer a


especificação do sufixo Async . Por exemplo:

CSHTML

<a asp-controller="Product" asp-action="ListAsync">List</a>

Novo comportamento
No ASP.NET Core 3.0, a ação é roteável por meio de Product/List . O código de geração
de link deve omitir o sufixo Async . Por exemplo:
CSHTML

<a asp-controller="Product" asp-action="List">List</a>

Essa alteração não afeta os nomes especificados usando o atributo [ActionName] . O


novo comportamento pode ser desabilitado definindo
MvcOptions.SuppressAsyncSuffixInActionNames como false em
Startup.ConfigureServices :

C#

services.AddMvc(options =>
{
options.SuppressAsyncSuffixInActionNames = false;
});

Motivo da alteração
Por convenção, os métodos .NET assíncronos têm o sufixo Async . No entanto, quando
um método define uma ação MVC, não convém usar o sufixo Async .

Ação recomendada
Se o aplicativo depender de ações de MVC que preservam o sufixo Async do nome,
escolha uma das seguintes mitigações:

Use o atributo [ActionName] para preservar o nome original.


Desabilite a renomeação inteiramente definindo
MvcOptions.SuppressAsyncSuffixInActionNames como false em

Startup.ConfigureServices :

C#

services.AddMvc(options =>
{
options.SuppressAsyncSuffixInActionNames = false;
});

Categoria

ASP.NET Core
APIs afetadas
Nenhum

MVC: JsonResult mudou para


Microsoft.AspNetCore.Mvc.Core
JsonResult mudou para o assembly Microsoft.AspNetCore.Mvc.Core . Esse tipo
costumava ser definido em Microsoft.AspNetCore.Mvc.Formatters.Json . Um atributo
[TypeForwardedTo] no nível do assembly foi adicionado a
Microsoft.AspNetCore.Mvc.Formatters.Json a fim de resolver esse problema para a
maioria dos usuários. Aplicativos que usam bibliotecas de terceiros podem encontrar
problemas.

Versão introduzida

3.0 versão prévia 6

Comportamento antigo

Um aplicativo que usa uma biblioteca baseada em 2.2 é compilado com êxito.

Novo comportamento

Um aplicativo que usa uma biblioteca baseada em 2.2 falha na compilação. Um erro que
contém uma variação do seguinte texto é fornecido:

Saída

The type 'JsonResult' exists in both 'Microsoft.AspNetCore.Mvc.Core,


Version=3.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60' and
'Microsoft.AspNetCore.Mvc.Formatters.Json, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=adb9793829ddae60'

Para obter um exemplo desse problema, confira dotnet/aspnetcore#7220 .

Motivo da alteração
Alterações no nível da plataforma na composição do ASP.NET Core conforme a
descrição em aspnet/Announcements#325 .
Ação recomendada
As bibliotecas compiladas na versão 2.2 do Microsoft.AspNetCore.Mvc.Formatters.Json
podem precisar ser recompiladas a fim de resolver o problema para todos os
consumidores. Se você foi afetado, entre em contato com o autor da biblioteca. Solicite
a recompilação da biblioteca para direcioná-la ao ASP.NET Core 3.0.

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Mvc.JsonResult

MVC: ferramenta de pré-compilação preterida


No ASP.NET Core 1.1, o pacote (ferramenta de pré-compilação MVC)
Microsoft.AspNetCore.Mvc.Razor.ViewCompilation foi introduzido para adicionar suporte

para compilação em tempo de publicação de arquivos Razor (arquivos .cshtml). No


ASP.NET Core 2.1, o SDK do Razor foi introduzido para expandir os recursos da
ferramenta de pré-configuração. O SDK do Razor adicionou suporte para compilação de
arquivos Razor em tempo de compilação e de publicação. O SDK verifica se os arquivos
.cshtml estão corretos em tempo de build enquanto, aprimorando o tempo de
inicialização do aplicativo. O SDK do Razor está ativado por padrão e não é necessário
fazer nada para começar a usá-lo.

No ASP.NET Core 3.0, a ferramenta de pré-compilação MVC do ASP.NET Core 1.1 foi
removida. As versões anteriores do pacote continuarão recebendo correções
importantes de bug e de segurança no lançamento do patch.

Versão introduzida
3.0

Comportamento antigo
O pacote Microsoft.AspNetCore.Mvc.Razor.ViewCompilation era usado para pré-compilar
exibições do Razor do MVC.
Novo comportamento
O SDK do Razor dá suporte nativo a essa funcionalidade. O pacote
Microsoft.AspNetCore.Mvc.Razor.ViewCompilation não é mais atualizado.

Motivo da alteração
O SDK do Razor fornece mais funcionalidade e verifica se os arquivos .cshtml estão
corretos em tempo de build. O SDK também aprimora o tempo de inicialização do
aplicativo.

Ação recomendada
Os usuários do ASP.NET Core 2.1 ou posteriores devem fazer a atualização para usar o
suporte nativo para pré-recompilação no SDK do Razor. Se bugs ou recursos ausentes
impedirem a migração para o SDK do Razor, abra um problema em
dotnet/aspnetcore .

Categoria
ASP.NET Core

APIs afetadas
Nenhum

MVC: tipos "pubterais" alterados para internos


No ASP.NET Core 3.0, todos os tipos "pubterais" no MVC foram atualizados para ser
public em um namespace com suporte ou internal conforme apropriado.

Descrição das alterações


No ASP.NET Core, os tipos "pubternal" são declarados como public , mas residem em
um namespace com o sufixo .Internal . Embora esses tipos sejam public , eles não têm
nenhuma política de suporte e estão sujeitos a alterações interruptivas. O uso acidental
desses tipos tem sido comum, resultando em alterações interruptivas nesses projetos e
limitando a capacidade de manutenção da estrutura.

Versão introduzida
3.0

Comportamento antigo
Alguns tipos no MVC eram public , mas em um namespace .Internal . Esses tipos não
tinham nenhuma política de suporte e estavam sujeitos a alterações interruptivas.

Novo comportamento

Todos esses tipos foram atualizados para ser public em um namespace com suporte ou
foram marcados como internal .

Motivo da alteração
O uso acidental dos tipos "pubternal" tem sido comum, resultando em alterações
interruptivas nesses projetos e limitando a capacidade de manutenção da estrutura.

Ação recomendada
Se você estiver usando tipos que realmente se tornaram public e foram movidos para
um namespace novo e com suporte, atualize as referências para corresponder aos
novos namespaces.

Se estiver usando tipos marcados como internal , você precisará encontrar uma
alternativa. Os tipos "pubterais" anteriores nunca foram compatíveis com uso público.
Se houver tipos específicos nesses namespaces que sejam essenciais para seus
aplicativos, registre um problema em dotnet/aspnetcore . Podem ser exigidas algumas
considerações para que os tipos solicitados sejam alterados para public .

Categoria
ASP.NET Core

APIs afetadas
Essa alteração inclui tipos nos seguintes namespaces:

Microsoft.AspNetCore.Mvc.Cors.Internal
Microsoft.AspNetCore.Mvc.DataAnnotations.Internal

Microsoft.AspNetCore.Mvc.Formatters.Internal
Microsoft.AspNetCore.Mvc.Formatters.Json.Internal

Microsoft.AspNetCore.Mvc.Formatters.Xml.Internal
Microsoft.AspNetCore.Mvc.Internal

Microsoft.AspNetCore.Mvc.ModelBinding.Internal
Microsoft.AspNetCore.Mvc.Razor.Internal

Microsoft.AspNetCore.Mvc.RazorPages.Internal

Microsoft.AspNetCore.Mvc.TagHelpers.Internal
Microsoft.AspNetCore.Mvc.ViewFeatures.Internal

MVC: shim de compatibilidade da Web API removido


Do ASP.NET Core 3.0 em diante, o pacote Microsoft.AspNetCore.Mvc.WebApiCompatShim
não está mais disponível.

Descrição das alterações


O pacote Microsoft.AspNetCore.Mvc.WebApiCompatShim (WebApiCompatShim) fornece
compatibilidade parcial no ASP.NET Core com a Web API 2 do ASP.NET 4.x para
simplificar a migração de implementações da Web API existentes para o ASP.NET Core.
No entanto, os aplicativos que usam o WebApiCompatShim não se beneficiam dos
recursos relacionados à API enviados em versões recentes do ASP.NET Core. Esses
recursos incluem melhor geração de especificação de API aberta, tratamento de erro
padronizado e geração de código cliente. Para concentrar melhor os esforços de API na
3.0, o WebApiCompatShim foi removido. Os aplicativos existentes que usam o
WebApiCompatShim devem ser migrados para o modelo [ApiController] mais recente.

Versão introduzida
3.0

Motivo da alteração
O shim de compatibilidade da Web API era uma ferramenta de migração. Ele restringe o
acesso do usuário a novas funcionalidades adicionadas no ASP.NET Core.

Ação recomendada

Remova o uso desse shim e migre diretamente para a funcionalidade semelhante no


próprio ASP.NET Core.
Categoria
ASP.NET Core

APIs afetadas

Microsoft.AspNetCore.Mvc.WebApiCompatShim

Razor: API RazorTemplateEngine removida


A API RazorTemplateEngine foi removida e substituída por
Microsoft.AspNetCore.Razor.Language.RazorProjectEngine .

Para ver a discussão, confira o problema do GitHub dotnet/aspnetcore#25215 .

Versão introduzida
3.0

Comportamento antigo
Um mecanismo de modelo pode ser criado e usado para analisar e gerar código para
arquivos Razor.

Novo comportamento

RazorProjectEngine pode ser criado e receber o mesmo tipo de informação que


RazorTemplateEngine para analisar e gerar código para arquivos Razor.

RazorProjectEngine também fornece níveis extras de configuração.

Motivo da alteração
RazorTemplateEngine era muito acoplado às implementações existentes. Esse forte

acoplamento gerava mais questionamentos ao tentar configurar corretamente um


pipeline de análise/geração do Razor.

Ação recomendada
Use RazorProjectEngine em vez de RazorTemplateEngine . Considere os exemplos a
seguir.
Criar e configurar o RazorProjectEngine

C#

RazorProjectEngine projectEngine =
RazorProjectEngine.Create(RazorConfiguration.Default,

RazorProjectFileSystem.Create(@"C:\source\repos\ConsoleApp4\ConsoleApp4"),
builder =>
{
builder.ConfigureClass((document, classNode) =>
{
classNode.ClassName = "MyClassName";

// Can also configure other aspects of the class here.


});

// More configuration can go here


});

Gerar código para um arquivo Razor

C#

RazorProjectItem item = projectEngine.FileSystem.GetItem(


@"C:\source\repos\ConsoleApp4\ConsoleApp4\Example.cshtml",
FileKinds.Legacy);
RazorCodeDocument output = projectEngine.Process(item);

// Things available
RazorSyntaxTree syntaxTree = output.GetSyntaxTree();
DocumentIntermediateNode intermediateDocument =
output.GetDocumentIntermediateNode();
RazorCSharpDocument csharpDocument = output.GetCSharpDocument();

Categoria
ASP.NET Core

APIs afetadas
RazorTemplateEngine

RazorTemplateEngineOptions

Razor: compilação de runtime movida para um pacote


O suporte para compilação de runtime de exibições Razor e Razor Pages foi movido
para um pacote separado.

Versão introduzida

3.0

Comportamento antigo

A compilação de runtime está disponível sem exigir pacotes adicionais.

Novo comportamento

A funcionalidade foi movida para o pacote


Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation .

As APIs a seguir estavam disponíveis no


Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions para dar suporte à compilação
de runtime. As APIs agora estão disponíveis por meio de
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation.MvcRazorRuntimeCompilationOption
s.

RazorViewEngineOptions.FileProviders agora é
MvcRazorRuntimeCompilationOptions.FileProviders

RazorViewEngineOptions.AdditionalCompilationReferences agora é

MvcRazorRuntimeCompilationOptions.AdditionalReferencePaths

Além disso,
Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions.AllowRecompilingViewsOnFileC
hange foi removido. A recompilação em alterações de arquivo é habilitada por padrão

com a referência ao pacote Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation .

Motivo da alteração
Essa alteração foi necessária para remover a dependência da estrutura compartilhada do
ASP.NET Core no Roslyn.

Ação recomendada

Os aplicativos que exigem compilação ou recompilação de runtime de arquivos Razor


devem seguir as seguintes etapas:
1. Adicione uma referência ao pacote
Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation .

2. Atualize o método Startup.ConfigureServices do projeto para incluir uma


chamada para AddRazorRuntimeCompilation . Por exemplo:

C#

services.AddMvc()
.AddRazorRuntimeCompilation();

Categoria

ASP.NET Core

APIs afetadas

Microsoft.AspNetCore.Mvc.Razor.RazorViewEngineOptions

Estado de sessão: APIs obsoletas removidas


As APIs obsoletas para configurar cookies de sessão foram removidas. Para obter mais
informações, confira aspnet/Announcements#257 .

Versão introduzida
3.0

Motivo da alteração
Essa alteração impõe consistência entre APIs para configurar recursos que usam cookies.

Ação recomendada
Migre o uso das APIs removidas para suas substituições mais recentes. Considere o
exemplo a seguir em Startup.ConfigureServices :

C#

public void ConfigureServices(ServiceCollection services)


{
services.AddSession(options =>
{
// Removed obsolete APIs
options.CookieName = "SessionCookie";
options.CookieDomain = "contoso.com";
options.CookiePath = "/";
options.CookieHttpOnly = true;
options.CookieSecure = CookieSecurePolicy.Always;

// new API
options.Cookie.Name = "SessionCookie";
options.Cookie.Domain = "contoso.com";
options.Cookie.Path = "/";
options.Cookie.HttpOnly = true;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
});
}

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Builder.SessionOptions.CookieDomain
Microsoft.AspNetCore.Builder.SessionOptions.CookieHttpOnly
Microsoft.AspNetCore.Builder.SessionOptions.CookieName
Microsoft.AspNetCore.Builder.SessionOptions.CookiePath
Microsoft.AspNetCore.Builder.SessionOptions.CookieSecure

Estrutura compartilhada: assemblies removidos de


Microsoft.AspNetCore.App
Do ASP.NET Core 3.0 em diante, a estrutura compartilhada do ASP.NET Core
( Microsoft.AspNetCore.App ) contém apenas assemblies da Microsoft totalmente
desenvolvidos, com suporte e que podem receber manutenção da Microsoft.

Descrição das alterações


Pense na alteração como a redefinição de limites da "plataforma" ASP.NET Core. A
estrutura compartilhada poderá ser compilada por qualquer pessoa por meio do
GitHub e continuará oferecendo os benefícios existentes das estruturas
compartilhadas do .NET Core aos aplicativos. Alguns benefícios incluem tamanho de
implantação menor, aplicação de patch centralizada e tempo de inicialização mais
rápido.

Como parte da alteração, algumas alterações interruptivas importantes são introduzidas


em Microsoft.AspNetCore.App .

Versão introduzida

3.0

Comportamento antigo

Os projetos referenciavam Microsoft.AspNetCore.App por meio de um elemento


<PackageReference> no arquivo de projeto.

Além disso, Microsoft.AspNetCore.App continha os seguintes subcomponentes:

Json.NET ( Newtonsoft.Json )
Entity Framework Core (assemblies prefixados com
Microsoft.EntityFrameworkCore. )
Roslyn ( Microsoft.CodeAnalysis )

Novo comportamento
Uma referência a Microsoft.AspNetCore.App não exige mais um elemento
<PackageReference> no arquivo de projeto. O SDK do .NET Core dá suporte a um novo

elemento chamado <FrameworkReference> , que substitui o uso de <PackageReference> .

Para obter mais informações, confira dotnet/aspnetcore#3612 .

O Entity Framework Core é enviado como pacotes NuGet. Essa alteração alinha o
modelo de envio com todas as outras bibliotecas de acesso a dados no .NET. Ela fornece
ao Entity Framework Core o caminho mais simples para continuar inovando enquanto
dá suporte a várias plataformas .NET. A mudança do Entity Framework Core para deixar
de usar a estrutura compartilhada não afeta seu status como uma biblioteca
desenvolvida pela Microsoft, que recebe suporte e manutenção da Microsoft. Ela
continua coberta pela política de suporte do .NET Core .

O Json.NET e o Entity Framework Core continuam a funcionar com o ASP.NET Core. No


entanto, eles não serão incluídos na estrutura compartilhada.
Para obter mais informações, confira O futuro do JSON no .NET Core 3.0 . Veja
também a lista completa de binários removidos da estrutura compartilhada.

Motivo da alteração

Essa alteração simplifica o consumo de Microsoft.AspNetCore.App e reduz a duplicação


entre pacotes NuGet e estruturas compartilhadas.

Para obter mais informações sobre a motivação dessa alteração, confira esta postagem
no blog .

Ação recomendada
Do ASP.NET Core 3.0 em diante, não é mais necessário que os projetos consumam
assemblies em Microsoft.AspNetCore.App como pacotes NuGet. Para simplificar o
direcionamento e o uso da estrutura compartilhada do ASP.NET Core, muitos pacotes
NuGet enviados desde o ASP.NET Core 1.0 não são mais produzidos. As APIs fornecidas
por esses pacotes ainda estão disponíveis para aplicativos usando um
<FrameworkReference> para Microsoft.AspNetCore.App . Exemplos comuns de API

incluem Kestrel, MVC e Razor.

Essa alteração não se aplica a todos os binários referenciados por


Microsoft.AspNetCore.App no ASP.NET Core 2.x. Exceções notáveis incluem:

As bibliotecas Microsoft.Extensions que continuam a ser direcionadas ao .NET


Standard estão disponíveis como pacotes NuGet (confira
https://github.com/dotnet/extensions ).
APIs produzidas pela equipe do ASP.NET Core que não fazem parte do
Microsoft.AspNetCore.App . Por exemplo, os seguintes componentes estão

disponíveis como pacotes NuGet:


Entity Framework Core
APIs que fornecem integração de terceiros
Recursos experimentais
APIs com dependências que não puderam atender aos requisitos para estar na
estrutura compartilhada
Extensões para MVC que mantêm o suporte para Json.NET. Uma API é fornecida
como um pacote NuGet para dar suporte ao uso de Json.NET e MVC. Confira o
Guia de migração do ASP.NET Core para obter mais detalhes.
O cliente .NET do SignalR continua a dar suporte ao .NET Standard e é fornecido
como um pacote NuGet . Ele pode ser usado em vários runtimes do .NET, como
Xamarin e UWP.
Para obter mais informações, confira Parar de produzir pacotes para assemblies de
estrutura compartilhada no 3.0 . Para ver a discussão, confira
dotnet/aspnetcore#3757 .

Categoria
ASP.NET Core

APIs afetadas
Microsoft.CodeAnalysis
Microsoft.EntityFrameworkCore

Estrutura compartilhada: Microsoft.AspNetCore.All


removido
Do ASP.NET Core 3.0 em diante, o metapacote Microsoft.AspNetCore.All e a estrutura
compartilhada correspondente Microsoft.AspNetCore.All não são mais produzidos.
Esse pacote está disponível no ASP.NET Core 2.2 e continuará recebendo atualizações
de manutenção no ASP.NET Core 2.1.

Versão introduzida
3.0

Comportamento antigo
Os aplicativos podem usar o metapacote Microsoft.AspNetCore.All para serem
direcionados à estrutura compartilhada Microsoft.AspNetCore.All no .NET Core.

Novo comportamento

O .NET Core 3.0 não inclui uma estrutura compartilhada Microsoft.AspNetCore.All .

Motivo da alteração

O metapacote Microsoft.AspNetCore.All incluía um grande número de dependências


externas.
Ação recomendada
Migre seu projeto para usar a estrutura Microsoft.AspNetCore.App . Os componentes que
estavam disponíveis no Microsoft.AspNetCore.All ainda estão disponíveis no NuGet.
Esses componentes agora são implantados com o aplicativo em vez de serem incluídos
na estrutura compartilhada.

Categoria
ASP.NET Core

APIs afetadas
Nenhum

SignalR: HandshakeProtocol.SuccessHandshakeData
substituído
O campo HandshakeProtocol.SuccessHandshakeData foi removido e substituído por
um método auxiliar que gera uma resposta de handshake bem-sucedida quando um
IHubProtocol específico é fornecido.

Versão introduzida

3.0

Comportamento antigo
HandshakeProtocol.SuccessHandshakeData era um campo public static

ReadOnlyMemory<byte> .

Novo comportamento

HandshakeProtocol.SuccessHandshakeData foi substituído por um método


static GetSuccessfulHandshake(IHubProtocol protocol) que retorna um

ReadOnlyMemory<byte> com base no protocolo especificado.

Motivo da alteração
Campos adicionais foram adicionados à resposta de handshake que não são constantes
e são alterados dependendo do protocolo selecionado.

Ação recomendada

Nenhum. Esse tipo não foi projetado para ser usado por meio do código do usuário. Ele
é public , portanto, pode ser compartilhado entre o servidor e o cliente SignalR. Ele
também pode ser usado por clientes SignalR do cliente escritos no .NET. Os usuários do
SignalR não devem ser afetados por essa alteração.

Categoria
ASP.NET Core

APIs afetadas

HandshakeProtocol.SuccessHandshakeData

SignalR: Métodos HubConnection ResetSendPing e


ResetTimeout removidos
Os métodos ResetSendPing e ResetTimeout foram removidos da API HubConnection do
SignalR. Esses métodos eram destinados inicialmente apenas a uso interno, mas ficaram
públicos no ASP.NET Core 2.2. Esses métodos não estarão disponíveis do ASP.NET Core
3.0 versão prévia 4 em diante. Para ver a discussão, confira dotnet/aspnetcore#8543 .

Versão introduzida

3.0

Comportamento antigo

As APIs estavam disponíveis.

Novo comportamento

As APIs foram removidas.

Motivo da alteração
Esses métodos eram destinados inicialmente apenas a uso interno, mas ficaram públicos
no ASP.NET Core 2.2.

Ação recomendada

Não use esses métodos.

Categoria

ASP.NET Core

APIs afetadas

HubConnection.ResetSendPing()
HubConnection.ResetTimeout()

SignalR: construtores HubConnectionContext alterados


Os construtores HubConnectionContext do SignalR foram alterados para aceitar um tipo
de opções, em vez de vários parâmetros, considerando futuras opções de adição. Essa
alteração substitui dois construtores por um só construtor que aceita um tipo de
opções.

Versão introduzida

3.0

Comportamento antigo

HubConnectionContext tem dois construtores:

C#

public HubConnectionContext(ConnectionContext connectionContext, TimeSpan


keepAliveInterval, ILoggerFactory loggerFactory);
public HubConnectionContext(ConnectionContext connectionContext, TimeSpan
keepAliveInterval, ILoggerFactory loggerFactory, TimeSpan
clientTimeoutInterval);

Novo comportamento
Os dois construtores foram removidos e substituídos por um construtor:

C#

public HubConnectionContext(ConnectionContext connectionContext,


HubConnectionContextOptions contextOptions, ILoggerFactory loggerFactory)

Motivo da alteração
O novo construtor usa um novo objeto de opções. Consequentemente, os recursos do
HubConnectionContext podem ser expandidos no futuro sem necessidade de mais
construtores nem de alterações interruptivas.

Ação recomendada
Em vez de usar o seguinte construtor:

C#

HubConnectionContext connectionContext = new HubConnectionContext(


connectionContext,
keepAliveInterval: TimeSpan.FromSeconds(15),
loggerFactory,
clientTimeoutInterval: TimeSpan.FromSeconds(15));

Use o seguinte construtor:

C#

HubConnectionContextOptions contextOptions = new


HubConnectionContextOptions()
{
KeepAliveInterval = TimeSpan.FromSeconds(15),
ClientTimeoutInterval = TimeSpan.FromSeconds(15)
};
HubConnectionContext connectionContext = new
HubConnectionContext(connectionContext, contextOptions, loggerFactory);

Categoria

ASP.NET Core

APIs afetadas
HubConnectionContext(ConnectionContext, TimeSpan, ILoggerFactory)
HubConnectionContext(ConnectionContext, TimeSpan, ILoggerFactory, TimeSpan)

SignalR: nome do pacote do cliente JavaScript alterado


No ASP.NET Core 3.0 versão prévia 7, o nome do pacote do cliente JavaScript do
SignalR foi alterado de @aspnet/signalr para @microsoft/signalr . A alteração de nome
reflete o fato de que o SignalR é útil em mais do que apenas aplicativos ASP.NET Core,
graças ao Serviço do Azure SignalR.

Para reagir a essa alteração, altere as referências em arquivos package.json, instruções


require e instruções ECMAScript import . Nenhuma API será alterada nessa

renomeação.

Para ver a discussão, confira dotnet/aspnetcore#11637 .

Versão introduzida
3.0

Comportamento antigo
O pacote cliente se chamava @aspnet/signalr .

Novo comportamento
O pacote cliente se chama @microsoft/signalr .

Motivo da alteração
A alteração de nome esclarece que o SignalR é útil em mais do que apenas aplicativos
ASP.NET Core, graças ao Serviço do Azure SignalR.

Ação recomendada
Mude para o novo pacote @microsoft/signalr .

Categoria
ASP.NET Core
APIs afetadas
Nenhum

SignalR: métodos UseSignalR e UseConnections


marcados como obsoletos
Os métodos UseConnections e UseSignalR as classes ConnectionsRouteBuilder e
HubRouteBuilder foram marcados como obsoletos no ASP.NET Core 3.0.

Versão introduzida
3.0

Comportamento antigo
O roteamento do hub do SignalR era configurado usando UseSignalR ou
UseConnections .

Novo comportamento
A maneira antiga de configurar o roteamento ficou obsoleta e foi substituída pelo
roteamento de ponto de extremidade.

Motivo da alteração

O middleware está passando para o novo sistema de roteamento de ponto de


extremidade. A maneira antiga de adicionar middleware está ficando obsoleta.

Ação recomendada
Substituir UseSignalR por UseEndpoints :

Código antigo:

C#

app.UseSignalR(routes =>
{
routes.MapHub<SomeHub>("/path");
});
Novo código:

C#

app.UseEndpoints(endpoints =>
{
endpoints.MapHub<SomeHub>("/path");
});

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Builder.ConnectionsAppBuilderExtensions.UseConnections(I
ApplicationBuilder, Action<ConnectionsRouteBuilder>)
Microsoft.AspNetCore.Builder.SignalRAppBuilderExtensions.UseSignalR(IApplicatio
nBuilder, Action<HubRouteBuilder>)
Microsoft.AspNetCore.Http.Connections.ConnectionsRouteBuilder
Microsoft.AspNetCore.SignalR.HubRouteBuilder

SPAs: SpaServices e NodeServices marcados como


obsoletos
O conteúdo dos pacotes NuGet a seguir ficou desnecessário desde o ASP.NET Core 2.1.
Consequentemente, os seguintes pacotes estão sendo marcados como obsoletos:

Microsoft.AspNetCore.SpaServices
Microsoft.AspNetCore.NodeServices

Pelo mesmo motivo, os seguintes módulos npm estão sendo marcados como
preteridos:

aspnet-angular
aspnet-prerendering
aspnet-webpack
aspnet-webpack-react
domain-task

Os pacotes e os módulos npm anteriores serão removidos posteriormente no .NET 5.


Versão introduzida
3.0

Comportamento antigo

Os pacotes e os módulos npm preteridos eram destinados a integrar o ASP.NET Core a


várias estruturas de SPA (aplicativo de página única). Essas estruturas incluem Angular,
React e React com Redux.

Novo comportamento

Existe um novo mecanismo de integração no pacote NuGet


Microsoft.AspNetCore.SpaServices.Extensions . O pacote continua sendo a base dos
modelos de projeto Angular e React desde o ASP.NET Core 2.1.

Motivo da alteração
O ASP.NET Core dá suporte à integração com várias estruturas de SPA (aplicativo de
página única), incluindo Angular, React e React com Redux. Inicialmente, a integração
com essas estruturas foi realizada com componentes específicos do ASP.NET Core que
lidavam com cenários como a pré-renderização do servidor e a integração com o
Webpack. Com o passar do tempo, os padrões do setor mudaram. Cada uma das
estruturas de SPA lançou as próprias interfaces de linha de comando padrão. Por
exemplo, a CLI do Angular e o create-react-app.

Quando o ASP.NET Core 2.1 foi lançado em maio de 2018, a equipe respondeu à
mudança nos padrões. Foi fornecida uma forma mais nova e mais simples de se integrar
às próprias cadeiras de ferramentas das estruturas SPA. O novo mecanismo de
integração existe no pacote Microsoft.AspNetCore.SpaServices.Extensions e continua
sendo a base dos modelos de projeto Angular e React desde o ASP.NET Core 2.1.

Para esclarecer que os componentes mais antigos específicos do ASP.NET Core são
irrelevantes e não recomendados:

O mecanismo de integração pré-2.1 foi marcado como obsoleto.


Os pacotes npm de suporte foram marcados como preteridos.

Ação recomendada

Se você estiver usando esses pacotes, atualize os aplicativos para usar a funcionalidade:
No pacote Microsoft.AspNetCore.SpaServices.Extensions .
Fornecida pelas estruturas do SPA que você está usando

Para habilitar recursos como pré-renderização do servidor e recarregamento de


módulos quentes, confira a documentação da estrutura do SPA correspondente. A
funcionalidade em Microsoft.AspNetCore.SpaServices.Extensions não está obsoleta e
continuará com suporte.

Categoria
ASP.NET Core

APIs afetadas
Microsoft.AspNetCore.Builder.SpaRouteExtensions

Microsoft.AspNetCore.Builder.WebpackDevMiddleware

Microsoft.AspNetCore.NodeServices.EmbeddedResourceReader

Microsoft.AspNetCore.NodeServices.INodeServices

Microsoft.AspNetCore.NodeServices.NodeServicesFactory

Microsoft.AspNetCore.NodeServices.NodeServicesOptions

Microsoft.AspNetCore.NodeServices.StringAsTempFile

Microsoft.AspNetCore.NodeServices.HostingModels.INodeInstance

Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationException

Microsoft.AspNetCore.NodeServices.HostingModels.NodeInvocationInfo

Microsoft.AspNetCore.NodeServices.HostingModels.NodeServicesOptionsExtensio
ns

Microsoft.AspNetCore.NodeServices.HostingModels.OutOfProcessNodeInstance

Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerenderer

Microsoft.AspNetCore.SpaServices.Prerendering.ISpaPrerendererBuilder

Microsoft.AspNetCore.SpaServices.Prerendering.JavaScriptModuleExport

Microsoft.AspNetCore.SpaServices.Prerendering.Prerenderer
Microsoft.AspNetCore.SpaServices.Prerendering.PrerenderTagHelper

Microsoft.AspNetCore.SpaServices.Prerendering.RenderToStringResult

Microsoft.AspNetCore.SpaServices.Webpack.WebpackDevMiddlewareOptions

Microsoft.Extensions.DependencyInjection.NodeServicesServiceCollectionExtension
s

Microsoft.Extensions.DependencyInjection.PrerenderingServiceCollectionExtension
s

SPAs: SpaServices e NodeServices não retornam mais ao


agente de console
Microsoft.AspNetCore.SpaServices e Microsoft.AspNetCore.NodeServices não exibirão
logs de console, a menos que o registro em log esteja configurado.

Versão introduzida

3.0

Comportamento antigo

Microsoft.AspNetCore.SpaServices e Microsoft.AspNetCore.NodeServices criavam um


agente de console automaticamente quando o registro em log não estava configurado.

Novo comportamento
Microsoft.AspNetCore.SpaServices e Microsoft.AspNetCore.NodeServices não exibirão

logs de console, a menos que o registro em log esteja configurado.

Motivo da alteração

É necessário se alinhar com a forma como outros pacotes do ASP.NET Core


implementam o registro em log.

Ação recomendada
Se o comportamento antigo for necessário, para configurar o registro em log do
console, adicione services.AddLogging(builder => builder.AddConsole()) ao método
Setup.ConfigureServices .

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Estrutura de destino: suporte ao .NET Framework


removido
Do ASP.NET Core 3.0 em diante, o .NET Framework é uma estrutura de destino sem
suporte.

Descrição das alterações


O .NET Framework 4.8 é a última versão principal do .NET Framework. Novos aplicativos
ASP.NET Core devem ser criados no .NET Core. Do lançamento do .NET Core 3.0 em
diante, considere o ASP.NET Core 3.0 como parte do .NET Core.

Os clientes que usam o ASP.NET Core com o .NET Framework puderam continuar
usando a versão 2.1 LTS contando com suporte completo. O suporte e a manutenção
do 2.1 continuaram até pelo menos 21 de agosto de 2021. Essa data foi três anos após a
declaração da versão LTS de acordo com a Política de Suporte do .NET . O suporte
para pacotes ASP.NET Core 2.1 no .NET Framework será estendido indefinidamente,
semelhante à política de manutenção para outras estruturas do ASP.NET baseadas em
pacote .

Para obter mais informações sobre a portabilidade do .NET Framework para o .NET
Core, confira Portabilidade para o .NET Core.

Os pacotes Microsoft.Extensions (como registro em log, injeção de dependência e


configuração) e o Entity Framework Core não foram afetados. Eles continuarão a dar
suporte ao .NET Standard.

Para obter mais informações sobre a motivação dessa alteração, confira a postagem
original no blog .
Versão introduzida
3.0

Comportamento antigo

Os aplicativos ASP.NET Core podiam ser executados no .NET Core ou no .NET


Framework.

Novo comportamento
Os aplicativos ASP.NET Core só podem ser executados no .NET Core.

Ação recomendada
Execute uma das seguintes ações:

Mantenha o aplicativo no ASP.NET Core 2.1.


Migre o aplicativo e as dependências para o .NET Core.

Categoria
ASP.NET Core

APIs afetadas
Nenhum

Bibliotecas principais do .NET


APIs que relatam a versão agora relatam o produto e não a versão do arquivo
As instâncias personalizadas do EncoderFallbackBuffer não podem fazer fallback
recursivamente
Alterações de comportamento de formatação e análise de ponto flutuante
As operações de análise de ponto flutuante não falham mais ou lançam uma
OverflowException
InvalidAsynchronousStateException movido para outro assembly
Substituir sequências de bytes UTF-8 mal formadas segue as diretrizes do Unicode
TypeDescriptionProviderAttribute movido para outro assembly
ZipArchiveEntry não lida mais com arquivos com tamanhos de entrada
inconsistentes
FieldInfo.SetValue lança exceção para campos estáticos somente init
Passar GroupCollection para métodos de extensão tomando IEnumerable<T>
requer desambiguidade

APIs que relatam a versão agora relatam o produto e não


a versão do arquivo
Muitas das APIs que retornam versões no .NET Core agora retornam a versão do
produto em vez da versão do arquivo.

Descrição das alterações


No .NET Core 2.2, e em versões anteriores, métodos como Environment.Version,
RuntimeInformation.FrameworkDescription e a caixa de diálogo propriedades do
arquivo para assemblies do .NET Core refletem a versão do arquivo. Do .NET Core 3.0
em diante, eles refletem a versão do produto.

A figura a seguir ilustra a diferença nas informações de versão do assembly


System.Runtime.dll para o .NET Core 2.2 (à esquerda) e o .NET Core 3.0 (à direita),
conforme exibido pela caixa de diálogo propriedades do arquivo do Windows Explorer.

Versão introduzida
3.0

Ação recomendada
Nenhum. Essa alteração deve tornar a detecção de versão intuitiva em vez de obtusa.

Categoria
Bibliotecas principais do .NET

APIs afetadas
Environment.Version
RuntimeInformation.FrameworkDescription

As instâncias personalizadas do EncoderFallbackBuffer


não podem fazer fallback recursivamente
As instâncias personalizadas EncoderFallbackBuffer não podem fazer fallback
recursivamente. A implementação de EncoderFallbackBuffer.GetNextChar() deve resultar
em uma sequência de caracteres que é conversível para a codificação de destino. Caso
contrário, uma exceção ocorrerá.

Descrição das alterações

Durante uma operação de transcodificação de caractere a byte, o runtime detecta


sequências UTF-16 mal formadas ou não reversíveis e fornece esses caracteres para o
método EncoderFallbackBuffer.Fallback. O método Fallback determina quais caracteres
devem ser substituídos pelos dados não reversíveis originais e esses caracteres são
drenados chamando EncoderFallbackBuffer.GetNextChar em um loop.

Em seguida, o runtime tenta transcodificar esses caracteres de substituição para a


codificação de destino. Se essa operação for bem-sucedida, o runtime continuará a
transcodificação de onde parou na cadeia de caracteres de entrada original.

Anteriormente, as implementações personalizadas de


EncoderFallbackBuffer.GetNextChar() podem retornar sequências de caracteres que não
são conversíveis para a codificação de destino. Se os caracteres substituídos não
puderem ser transcodificados para a codificação de destino, o runtime invocará o
método EncoderFallbackBuffer.Fallback mais uma vez com os caracteres de substituição,
esperando que o método EncoderFallbackBuffer.GetNextChar() retorne uma nova
sequência de substituição. Esse processo continua até que o runtime eventualmente
veja uma substituição conversível bem formada ou até que uma contagem máxima de
recursão seja atingida.

Do .NET Core 3.0 em diante, as implementações personalizadas de


EncoderFallbackBuffer.GetNextChar() devem retornar sequências de caracteres
conversíveis para a codificação de destino. Se os caracteres substituídos não puderem
ser transcodificados para a codificação de destino, um ArgumentException será gerado.
O runtime não fará mais chamadas recursivas na instância EncoderFallbackBuffer.

Esse comportamento só se aplica quando todas as três condições seguintes são


atendidas:

O runtime detecta uma sequência UTF-16 mal formada ou uma sequência UTF-16
que não pode ser convertida na codificação de destino.
Um EncoderFallback personalizado foi especificado.
As tentativas personalizadas EncoderFallback de substituir uma nova sequência
UTF-16 mal formada ou não reversível.

Versão introduzida
3.0

Ação recomendada
A maioria dos desenvolvedores não precisa tomar nenhuma ação.

Se um aplicativo usa classes EncoderFallback e EncoderFallbackBuffer personalizadas,


verifique se a implementação de EncoderFallbackBuffer.Fallback popula o buffer de
fallback com os dados UTF-16 bem formados que são diretamente conversíveis para a
codificação de destino, quando o método Fallback é invocado pela primeira vez pelo
runtime.

Categoria
Bibliotecas principais do .NET

APIs afetadas
EncoderFallbackBuffer.Fallback
EncoderFallbackBuffer.GetNextChar()
Alterações de comportamento de formatação e análise
de ponto flutuante
O comportamento de análise e formatação de ponto flutuante (por tipos Double e
Single) agora é compatível com IEEE . Isso garante que o comportamento de tipos de
ponto flutuante no .NET corresponda ao de outras linguagens compatíveis com IEEE.
Por exemplo, double.Parse("SomeLiteral") sempre deve corresponder ao que o C#
produz para double x = SomeLiteral .

Descrição das alterações


No .NET Core 2.2 e versões anteriores, a formatação com Double.ToString e
Single.ToString e a análise com Double.Parse, Double.TryParse, Single.Parse e
Single.TryParse não são compatíveis com IEEE. Como resultado, é impossível garantir
que um valor seja arredondado com qualquer cadeia de caracteres de formato padrão
ou personalizado com suporte. Para algumas entradas, a tentativa de analisar um valor
formatado pode falhar e, para outras, o valor analisado não é igual ao valor original.

Do .NET Core 3.0 em diante, as operações de análise e formatação de ponto flutuante


são compatíveis com o IEEE 754.

A tabela a seguir mostra dois snippets de código e como a saída é alterada entre o .NET
Core 2.2 e o .NET Core 3.1.

Snippet de código Saída no .NET Saída no .NET


Core 2.2 Core 3.1

Console.WriteLine((-0.0).ToString()); 0 -0

var value = -3.123456789123456789; False True


Console.WriteLine(value ==
double.Parse(value.ToString()));

Para obter mais informações, confira a postagem no blog Aprimoramentos de


formatação e análise de ponto flutuante no .NET Core 3.0 .

Versão introduzida

3.0

Ação recomendada
O impacto potencial na seção de código existente das melhorias de análise e
formatação de ponto flutuante na postagem de blog do .NET Core 3.0 sugere
algumas alterações que você pode fazer ao seu código se quiser manter o
comportamento anterior.

Para algumas diferenças na formatação, você pode obter um comportamento


equivalente ao comportamento anterior especificando uma cadeia de caracteres
de formato diferente.
Para diferenças na análise, não há mecanismo para voltar ao comportamento
anterior.

Categoria

Bibliotecas principais do .NET

APIs afetadas

Double.ToString
Single.ToString
Double.Parse
Double.TryParse
Single.Parse
Single.TryParse

As operações de análise de ponto flutuante não falham


mais ou lançam uma OverflowException
Os métodos de análise de ponto flutuante não lançam mais um OverflowException ou
retornam false quando analisam uma cadeia de caracteres cujo valor numérico está
fora do intervalo do tipo Single ou Double ponto flutuante.

Descrição das alterações


No .NET Core 2.2 e em versões anteriores, os métodos Double.Parse e Single.Parse
lançam OverflowException para valores que estão fora do intervalo de seu respectivo
tipo. Os métodos Double.TryParse e Single.TryParse retornam false para as
representações de cadeia de caracteres de valores numéricos fora do intervalo.

Do .NET Core 3.0 em diante, os métodos Double.Parse, Double.TryParse, Single.Parse e


Single.TryParse não falham mais ao analisar cadeias de caracteres numéricas fora do
intervalo. Em vez disso, os métodos de análise Double retornam Double.PositiveInfinity,
para valores que excedem Double.MaxValue, e retornam Double.NegativeInfinity, para
valores menores que Double.MinValue. Similarmente, os métodos de análise Single
retornam Single.PositiveInfinity, para valores que excedem Single.MaxValue, e retornam
Single.NegativeInfinity, para valores menores que Single.MinValue.

Essa alteração foi feita para melhorar a conformidade do IEEE 754:2008.

Versão introduzida
3.0

Ação recomendada
Essa alteração pode afetar seu código de duas maneiras:

Seu código depende do manipulador para a execução OverflowException quando


ocorrer um estouro. Nesse caso, você deve remover a instrução catch e colocar
qualquer código necessário em uma instrução If que testa se Double.IsInfinity ou
Single.IsInfinity é true .

Seu código pressupõe que os valores de ponto flutuante não são Infinity . Nesse
caso, você deve adicionar o código necessário para verificar se há valores de ponto
flutuante PositiveInfinity e NegativeInfinity .

Categoria
Bibliotecas principais do .NET

APIs afetadas
Double.Parse
Double.TryParse
Single.Parse
Single.TryParse

InvalidAsynchronousStateException movido para outro


assembly
A classe InvalidAsynchronousStateException foi movida.
Descrição das alterações
No .NET Core 2.2 e versões anteriores, a classe InvalidAsynchronousStateException é
encontrada no assembly System.ComponentModel.TypeConverter.

Do .NET Core 3.0 em diante, ele é encontrado no assembly


System.ComponentModel.Primitives.

Versão introduzida
3.0

Ação recomendada
Essa alteração afeta apenas os aplicativos que usam reflexão para carregar
InvalidAsynchronousStateException chamando um método como Assembly.GetType ou
uma sobrecarga de Activator.CreateInstance que pressupõe que o tipo esteja em um
assembly específico. Se esse for o caso, atualize o assembly referenciado na chamada de
método para refletir o novo local do assembly do tipo.

Categoria

Bibliotecas principais do .NET

APIs afetadas

Nenhum.

Substituir sequências de bytes UTF-8 mal formadas segue


as diretrizes do Unicode
Quando a classe UTF8Encoding encontra uma sequência de bytes UTF-8 mal formada
durante uma operação de transcodificação de byte para caractere, ela substitui essa
sequência por um caractere ' (U+FFFD REPLACEMENT CHARACTER) na cadeia de
caracteres de saída. O .NET Core 3.0 é diferente das versões anteriores do .NET Core e
do .NET Framework seguindo a melhor prática de unicode para executar essa
substituição durante a operação de transcodificação.

Isso faz parte de um esforço maior para melhorar a manipulação do UTF-8 em todo o
.NET, inclusive pelos novos tipos System.Text.Unicode.Utf8 e System.Text.Rune. O tipo
UTF8Encoding recebeu uma mecânica aprimorada de tratamento de erros para que
produza uma saída consistente com os tipos recém-introduzidos.

Descrição das alterações

Do .NET Core 3.0 em diante, ao transcodificar bytes para caracteres, a classe


UTF8Encoding executa a substituição de caracteres com base nas práticas
recomendadas Unicode. O mecanismo de substituição usado é descrito pelo Unicode
Standard, Versão 12.0, S. 3.9 (PDF) no título intitulado Substituição U+FFFD de
Subpartes Máximas.

Esse comportamento só se aplica quando a sequência de bytes de entrada contém


dados UTF-8 mal formados. Além disso, se a instância UTF8Encoding tiver sido
construída com throwOnInvalidBytes: true , a instância UTF8Encoding continuará a ser
lançada na entrada inválida em vez de executar a substituição U+FFFD. Para obter mais
informações sobre o construtor UTF8Encoding , confira UTF8Encoding(Boolean, Boolean).

A seguinte tabela ilustra o impacto dessa alteração com uma entrada inválida de 3
bytes:

Entrada de 3 bytes mal Saída antes do .NET Core Começando com o SDK do .NET
formada 3.0 Core 3.0

[ ED A0 90 ] [ FFFD FFFD ] (saída de 2 [ FFFD FFFD FFFD ] (saída de 3


caracteres) caracteres)

A saída de 3 caracteres é a preferencial, de acordo com a Tabela 3-9 do PDF Standard


Unicode vinculado anteriormente.

Versão introduzida
3.0

Ação recomendada
Nenhuma ação é necessária por parte do desenvolvedor.

Categoria

Bibliotecas principais do .NET


APIs afetadas
UTF8Encoding.GetCharCount
UTF8Encoding.GetChars
UTF8Encoding.GetString(Byte[], Int32, Int32)

TypeDescriptionProviderAttribute movido para outro


assembly
A classe TypeDescriptionProviderAttribute foi movida.

Descrição das alterações


No .NET Core 2.2 e versões anteriores, a classe TypeDescriptionProviderAttribute é
encontrada no assembly System.ComponentModel.TypeConverter .

Do .NET Core 3.0 em diante, ela é encontrada no assembly System.ObjectModel.

Versão introduzida
3.0

Ação recomendada
Essa alteração afeta apenas os aplicativos que usam reflexão para carregar o tipo
TypeDescriptionProviderAttribute chamando um método como Assembly.GetType ou
uma sobrecarga de Activator.CreateInstance que pressupõe que o tipo esteja em um
assembly específico. Se esse for o caso, o assembly referenciado na chamada de
método deverá ser atualizado para refletir o novo local do assembly do tipo.

Categoria

Windows Forms

APIs afetadas

Nenhum.
ZipArchiveEntry não lida mais com arquivos com
tamanhos de entrada inconsistentes
Os arquivos zip listam o tamanho compactado e o tamanho não compactado no
diretório central e no cabeçalho local. Os próprios dados de entrada também indicam
seu tamanho. No .NET Core 2.2 e em versões anteriores, esses valores nunca foram
verificados quanto à consistência. Do .NET Core 3.0 em diante, eles serão verificados.

Descrição das alterações

No .NET Core 2.2 e nas versões anteriores, ZipArchiveEntry.Open() é bem-sucedido


mesmo que o cabeçalho local discordou do cabeçalho central do arquivo zip. Os dados
são descompactados até que o final do fluxo compactado seja atingido, mesmo que seu
comprimento exceda o tamanho do arquivo não compactado listado no diretório
central/cabeçalho local.

Do .NET Core 3.0 em diante, o método ZipArchiveEntry.Open() verifica se o cabeçalho


local e o cabeçalho central concordam em tamanhos compactados e não compactados
de uma entrada. Se não o fizerem, o método vai gerar InvalidDataException, se o
cabeçalho local do arquivo morto e/ou descritor de dados listar os tamanhos que
discordam do diretório central do arquivo zip. Ao ler uma entrada, os dados
descompactados são truncados para o tamanho do arquivo não compactado listado no
cabeçalho.

Essa alteração foi feita para garantir que um valor ZipArchiveEntry representa
corretamente o tamanho de seus dados e que somente essa quantidade de dados seja
lida.

Versão introduzida

3.0

Ação recomendada
Reempacotar qualquer arquivo zip que exponha esses problemas.

Categoria
Bibliotecas principais do .NET
APIs afetadas
ZipArchiveEntry.Open()
ZipFileExtensions.ExtractToDirectory
ZipFileExtensions.ExtractToFile
ZipFile.ExtractToDirectory

FieldInfo.SetValue lança exceção para campos estáticos


somente init
Do .NET Core 3.0 em diante, uma exceção é gerada quando você tenta definir um valor
em um campo estático InitOnly chamando System.Reflection.FieldInfo.SetValue.

Descrição das alterações


Em .NET Framework e versões do .NET Core anteriores à 3.0, você pode definir o valor
de um campo estático que é constante depois que ele é inicializado (somente leitura em
C#) chamando System.Reflection.FieldInfo.SetValue. No entanto, definir esse campo
dessa forma resultou em um comportamento imprevisível com base na estrutura de
destino e nas configurações de otimização.

No .NET Core 3.0 e versões posteriores, quando você chama SetValue em um campo
estático InitOnly uma exceção System.FieldAccessException é lançada.

 Dica

Um campo InitOnly é aquele que só pode ser definido no momento em que é


declarado ou no construtor da classe que contém. Em outras palavras, ele é
constante depois de inicializado.

Versão introduzida
3.0

Ação recomendada
Inicialize campos estáticos InitOnly em um construtor estático. Isso se aplica a tipos
dinâmicos e não dinâmicos.
Como alternativa, você pode remover o atributo do campo FieldAttributes.InitOnly e,
em seguida, chamar FieldInfo.SetValue.

Categoria

Bibliotecas principais do .NET

APIs afetadas

FieldInfo.SetValue(Object, Object)
FieldInfo.SetValue(Object, Object, BindingFlags, Binder, CultureInfo)

Passar GroupCollection para métodos de extensão


tomando IEnumerable<T> requer desambiguidade
Ao chamar um método de extensão que usa IEnumerable<T> ou GroupCollection, você
deve desambiguar o tipo usando uma conversão.

Descrição das alterações


Do .NET Core 3.0 em diante, System.Text.RegularExpressions.GroupCollection
implementa IEnumerable<KeyValuePair<String,Group>> além dos outros tipos
implementados, incluindo IEnumerable<Group> . Isso resulta em ambiguidade ao chamar
um método de extensão que usa um IEnumerable<T>. Se você chamar esse método de
extensão em uma instância GroupCollection, por exemplo, Enumerable.Count, verá o
seguinte erro do compilador:

CS1061: 'GroupCollection' não contém uma definição para "{1}" e não foi possível
encontrar nenhum método de extensão "{1}" que aceite um primeiro argumento do
tipo ‘{0}’ (você está se esquecendo de usar uma diretiva ou uma referência de
assembly?)

Nas versões anteriores do .NET, não havia ambiguidade nem erro do compilador.

Versão introduzida
3.0

Motivo da alteração
Essa foi uma alteração interruptiva involuntária . Pois tem sido assim há algum tempo,
não planejamos revertê-la. Além disso, tal alteração seria, por si só, interruptiva.

Ação recomendada

Para instâncias GroupCollection, desambiguar chamadas para métodos de extensão que


aceitam IEnumerable<T> com uma conversão.

C#

// Without a cast - causes CS1061.


match.Groups.Count(_ => true)

// With a disambiguating cast.


((IEnumerable<Group>)m.Groups).Count(_ => true);

Categoria
Bibliotecas principais do .NET

APIs afetadas
Qualquer método de extensão que aceite IEnumerable<T> é afetado. Por exemplo:

System.Collections.Immutable.ImmutableArray.ToImmutableArray<TSource>
(IEnumerable<TSource>)
System.Collections.Immutable.ImmutableDictionary.ToImmutableDictionary
System.Collections.Immutable.ImmutableHashSet.ToImmutableHashSet
System.Collections.Immutable.ImmutableList.ToImmutableList<TSource>
(IEnumerable<TSource>)
System.Collections.Immutable.ImmutableSortedDictionary.ToImmutableSortedDicti
onary
System.Collections.Immutable.ImmutableSortedSet.ToImmutableSortedSet
System.Data.DataTableExtensions.CopyToDataTable
A maioria dos métodos System.Linq.Enumerable , por exemplo,
System.Linq.Enumerable.Count
System.Linq.ParallelEnumerable.AsParallel
System.Linq.Queryable.AsQueryable

Criptografia
A sintaxe BEGIN TRUSTED CERTIFICATE não tem mais suporte no Linux
Os envelopedCms são padrão para a criptografia AES-256
O tamanho mínimo para a geração de chave RSAOpenSsl aumentou
O .NET Core 3.0 prefere o OpenSSL 1.1.x ao OpenSSL 1.0.x
CryptoStream.Dispose transforma o bloco final somente ao gravar

A sintaxe "BEGIN TRUSTED CERTIFICATE" não tem mais


suporte para certificados raiz no Linux
Certificados raiz no Linux e em outros sistemas semelhantes a Unix (mas não macOS)
podem ser apresentados em duas formas: o cabeçalho PEM padrão BEGIN CERTIFICATE
e o cabeçalho PEM específico BEGIN TRUSTED CERTIFICATE do OpenSSL. A última sintaxe
permite uma configuração adicional que causou problemas de compatibilidade com a
classe System.Security.Cryptography.X509Certificates.X509Chain do .NET Core. BEGIN
TRUSTED CERTIFICATE O conteúdo do certificado raiz não é mais carregado pelo
mecanismo de cadeia do .NET Core 3.0 em diante.

Descrição das alterações


Anteriormente, as sintaxes BEGIN CERTIFICATE e BEGIN TRUSTED CERTIFICATE eram
usadas para preencher a lista de confiança raiz. Se a sintaxe BEGIN TRUSTED CERTIFICATE
era usada e opções adicionais eram especificadas no arquivo, X509Chain pode ter
relatado que a relação de confiança da cadeia era explicitamente não permitida
(X509ChainStatusFlags.ExplicitDistrust). No entanto, se o certificado também foi
especificado com a sintaxe BEGIN CERTIFICATE em um arquivo carregado anteriormente,
a confiança da cadeia foi permitida.

Do .NET Core 3.0 em diante, BEGIN TRUSTED CERTIFICATE o conteúdo não é mais lido. Se
o certificado também não for especificado por meio de uma sintaxe padrão BEGIN
CERTIFICATE , o X509Chain relata que a raiz não é confiável
(X509ChainStatusFlags.UntrustedRoot).

Versão introduzida
3.0

Ação recomendada
A maioria dos aplicativos não é afetada por essa alteração, mas aplicativos que não
podem ver ambas as fontes de certificado raiz devido a problemas de permissões
podem sofrer erros UntrustedRoot inesperados após a atualização.

Muitas distribuições (ou distros) do Linux gravam certificados raiz em dois locais: um
diretório de um certificado por arquivo e uma concatenação de um arquivo. Em
algumas distribuições, o diretório de um certificado por arquivo usa a sintaxe BEGIN
TRUSTED CERTIFICATE enquanto a concatenação do arquivo usa a sintaxe padrão BEGIN

CERTIFICATE . Verifique se todos os certificados raiz personalizados são adicionados

como BEGIN CERTIFICATE em pelo menos um desses locais e se ambos os locais podem
ser lidos pelo aplicativo.

O diretório típico é /etc/ssl/certs/ e o arquivo concatenado típico é /etc/ssl/cert.pem. Use


o comando openssl version -d para determinar a raiz específica da plataforma, que
pode ser diferente de /etc/ssl/. Por exemplo, no Ubuntu 18.04, o diretório é
/usr/lib/ssl/certs/ e o arquivo é /usr/lib/ssl/cert.pem. No entanto, /usr/lib/ssl/certs/ é um
symlink para /etc/ssl/certs/ e /usr/lib/ssl/cert.pem não existe.

Bash

$ openssl version -d
OPENSSLDIR: "/usr/lib/ssl"
$ ls -al /usr/lib/ssl
total 12
drwxr-xr-x 3 root root 4096 Dec 12 17:10 .
drwxr-xr-x 73 root root 4096 Feb 20 15:18 ..
lrwxrwxrwx 1 root root 14 Mar 27 2018 certs -> /etc/ssl/certs
drwxr-xr-x 2 root root 4096 Dec 12 17:10 misc
lrwxrwxrwx 1 root root 20 Nov 12 16:58 openssl.cnf ->
/etc/ssl/openssl.cnf
lrwxrwxrwx 1 root root 16 Mar 27 2018 private -> /etc/ssl/private

Categoria
Criptografia

APIs afetadas
System.Security.Cryptography.X509Certificates.X509Chain

Os envelopedCms são padrão para a criptografia AES-256


O algoritmo de criptografia simétrica padrão usado por EnvelopedCms foi alterado de
TripleDES para AES-256.
Descrição das alterações
Nas versões anteriores, quando EnvelopedCms é usado para criptografar dados sem
especificar um algoritmo de criptografia simétrica por meio de uma sobrecarga de
construtor, os dados são criptografados com o algoritmo TripleDES/3DES/3DEA/DES3-
EDE.

Começando com o .NET Core 3.0 (via versão 4.6.0 do pacote NuGet
System.Security.Cryptography.Pkcs ), o algoritmo padrão foi alterado para AES-256
para modernização de algoritmo e para melhorar a segurança das opções padrão. Se
um certificado de destinatário de mensagem tiver uma chave pública (não EC Diffie-
Hellman), a operação de criptografia poderá falhar com um CryptographicException
devido a limitações na plataforma subjacente.

No código de exemplo a seguir, os dados são criptografados com TripleDES se


estiverem em execução no .NET Core 2.2 ou anterior. Se estiver em execução no .NET
Core 3.0 ou posterior, ele será criptografado com o AES-256.

C#

EnvelopedCms cms = new EnvelopedCms(content);


cms.Encrypt(recipient);
return cms.Encode();

Versão introduzida

3.0

Ação recomendada

Se você for afetado negativamente pela alteração, poderá restaurar a criptografia


TripleDES especificando explicitamente o identificador de algoritmo de criptografia em
um construtor EnvelopedCms que inclui um parâmetro de tipo AlgorithmIdentifier,
como:

C#

Oid tripleDesOid = new Oid("1.2.840.113549.3.7", null);


AlgorithmIdentifier tripleDesIdentifier = new
AlgorithmIdentifier(tripleDesOid);
EnvelopedCms cms = new EnvelopedCms(content, tripleDesIdentifier);

cms.Encrypt(recipient);
return cms.Encode();
Categoria
Criptografia

APIs afetadas

EnvelopedCms()
EnvelopedCms(ContentInfo)
EnvelopedCms(SubjectIdentifierType, ContentInfo)

O tamanho mínimo para a geração de chave RSAOpenSsl


aumentou
O tamanho mínimo para gerar novas chaves RSA no Linux aumentou de 384 bits para
512 bits.

Descrição das alterações

Do .NET Core 3.0 em diante, o tamanho mínimo de chave legal relatado pela
propriedade LegalKeySizes em instâncias RSA de RSA.Create, RSAOpenSsl e
RSACryptoServiceProvider no Linux aumentou de 384 para 512.

Como resultado, no .NET Core 2.2 e em versões anteriores, uma chamada de método
como RSA.Create(384) é bem-sucedida. No .NET Core 3.0 e versões posteriores, a
chamada de método RSA.Create(384) gera uma exceção indicando que o tamanho é
muito pequeno.

Essa alteração foi feita porque o OpenSSL, que executa as operações criptográficas no
Linux, elevou seu mínimo entre as versões 1.0.2 e 1.1.0. O .NET Core 3.0 prefere o
OpenSSL 1.1.x a 1.0.x e a versão mínima relatada foi gerada para refletir essa nova
limitação de dependência mais alta.

Versão introduzida

3.0

Ação recomendada

Se você chamar qualquer uma das APIs afetadas, verifique se o tamanho de qualquer
chave gerada não é menor que o mínimo do provedor.
7 Observação

O RSA de 384 bits já é considerado inseguro (assim como o RSA de 512 bits).
Recomendações modernas, como a Publicação Especial NIST 800-57 Parte 1
Revisão 4 , sugerem 2048 bits como o tamanho mínimo para chaves recém-
geradas.

Categoria
Criptografia

APIs afetadas
AsymmetricAlgorithm.LegalKeySizes
RSA.Create
RSAOpenSsl
RSACryptoServiceProvider

O .NET Core 3.0 prefere o OpenSSL 1.1.x ao OpenSSL 1.0.x


O .NET Core para Linux, que funciona em várias distribuições do Linux, pode dar suporte
ao OpenSSL 1.0.x e ao OpenSSL 1.1.x. O .NET Core 2.1 e o .NET Core 2.2 primeiro
procuram 1.0.x e, em seguida, recuam para 1.1.x; o .NET Core 3.0 procura 1.1.x primeiro.
Essa alteração foi feita para adicionar suporte a novos padrões criptográficos.

Essa alteração pode afetar bibliotecas ou aplicativos que fazem interoperabilidade de


plataforma com os tipos de interoperabilidade específicos do OpenSSL no .NET Core.

Descrição das alterações

No .NET Core 2.2 e em versões anteriores, o runtime prefere carregar o OpenSSL 1.0.x
em vez de 1.1.x. Isso significa que os tipos IntPtr e SafeHandle para interoperabilidade
com OpenSSL são usados com libcrypto.so.1.0.0/libcrypto.so.1.0/libcrypto.so.10 por
preferência.

Começando com o .NET Core 3.0, o runtime prefere carregar o OpenSSL 1.1.x em vez do
OpenSSL 1.0.x, de modo que os tipos IntPtr e SafeHandle para interoperabilidade com
OpenSSL sejam usados com
libcrypto.so.1.1/libcrypto.so.11/libcrypto.so.1.1.0/libcrypto.so.1.1.1 por preferência.
Como resultado, bibliotecas e aplicativos que interoperam diretamente com o OpenSSL
podem ter ponteiros incompatíveis com os valores expostos pelo .NET Core ao atualizar
do .NET Core 2.1 ou do .NET Core 2.2.

Versão introduzida
3.0

Ação recomendada
Bibliotecas e aplicativos que fazem operações diretas com o OpenSSL precisam ter
cuidado para garantir que estejam usando a mesma versão do OpenSSL que o runtime
do .NET Core.

Todas as bibliotecas ou aplicativos que usam valores IntPtr ou SafeHandle dos tipos
criptográficos do .NET Core diretamente com o OpenSSL devem comparar a versão da
biblioteca que usam com a nova propriedade SafeEvpPKeyHandle.OpenSslVersion para
garantir que os ponteiros sejam compatíveis.

Categoria

Criptografia

APIs afetadas

SafeEvpPKeyHandle
RSAOpenSsl(IntPtr)
RSAOpenSsl(SafeEvpPKeyHandle)
RSAOpenSsl.DuplicateKeyHandle()
DSAOpenSsl(IntPtr)
DSAOpenSsl(SafeEvpPKeyHandle)
DSAOpenSsl.DuplicateKeyHandle()
ECDsaOpenSsl(IntPtr)
ECDsaOpenSsl(SafeEvpPKeyHandle)
ECDsaOpenSsl.DuplicateKeyHandle()
ECDiffieHellmanOpenSsl(IntPtr)
ECDiffieHellmanOpenSsl(SafeEvpPKeyHandle)
ECDiffieHellmanOpenSsl.DuplicateKeyHandle()
X509Certificate.Handle
CryptoStream.Dispose transforma o bloco final somente
ao gravar
O método CryptoStream.Dispose, que é usado para concluir operações CryptoStream ,
não tenta mais transformar o bloco final ao ler.

Descrição das alterações


Nas versões anteriores do .NET, se um usuário executasse uma leitura incompleta ao
usar CryptoStream no modo Read, o método Dispose poderia gerar uma exceção (por
exemplo, ao usar o AES com preenchimento). A exceção foi gerada porque o bloco final
tentou ser transformado, mas os dados estavam incompletos.

No .NET Core 3.0 e versões posteriores, Dispose não tenta mais transformar o bloco
final ao ler, o que permite leituras incompletas.

Motivo da alteração
Essa alteração permite leituras incompletas do fluxo de criptografia quando uma
operação de rede é cancelada, sem a necessidade de capturar uma exceção.

Versão introduzida
3.0

Ação recomendada
A maioria dos aplicativos não deve ser afetada por essa alteração.

Se o aplicativo tiver pego uma exceção anteriormente em caso de uma leitura


incompleta, você poderá excluir esse bloco catch . Se o aplicativo usou a transformação
do bloco final em cenários de hash, talvez seja necessário garantir que todo o fluxo seja
lido antes de ser descartado.

Categoria

Criptografia

APIs afetadas

System.Security.Cryptography.CryptoStream.Dispose
Entity Framework Core
Alterações interruptivas do Entity Framework Core

Globalização
A localidade "C" é mapeada para a localidade invariável

A localidade "C" é mapeada para a localidade invariável


O .NET Core 2.2 e versões anteriores dependem do comportamento padrão da ICU, que
mapeia a localidade "C" para a localidade en_US_POSIX. A localidade en_US_POSIX tem
um comportamento de agrupamento indesejável, pois não dá suporte a comparações
de cadeia de caracteres que não diferenciam maiúsculas de minúsculas. Como algumas
distribuições do Linux definem a localidade "C" como a localidade padrão, os usuários
estavam tendo um comportamento inesperado.

Descrição das alterações


Do .NET Core 3.0 em diante, o mapeamento de localidade "C" foi alterado para usar a
localidade invariável em vez de en_US_POSIX. O mapeamento da localidade "C" para
invariável também é aplicada ao Windows para consistência.

O mapeamento de "C" para a cultura en_US_POSIX causou confusão no cliente, pois


en_US_POSIX não dá suporte a operações de cadeia de caracteres de
classificação/pesquisa sem diferenciar maiúsculas de minúsculas. Como a localidade "C"
é usada como uma localidade padrão em algumas das distribuições do Linux, os clientes
experimentaram esse comportamento indesejado nesses sistemas operacionais.

Versão introduzida
3.0

Ação recomendada
Nada mais específico do que a consciência dessa mudança. Essa alteração afeta apenas
os aplicativos que usam o mapeamento de localidade "C".
Categoria
Globalização

APIs afetadas

Todas as APIs de agrupamento e cultura são afetadas por essa alteração.

MSBuild
Alteração do nome do arquivo de manifesto do recurso

Alteração do nome do arquivo de manifesto do recurso


Do .NET Core 3.0 em diante, no caso padrão, o MSBuild gera um nome de arquivo de
manifesto diferente para arquivos de recursos.

Versão introduzida

3.0

Descrição das alterações

Antes do .NET Core 3.0, se o metadado LogicalName , ManifestResourceName ou


DependentUpon não fosse especificado para um item EmbeddedResource no arquivo de

projeto, o MSBuild gerava um nome de arquivo de manifesto no padrão


<RootNamespace>.<ResourceFilePathFromProjectRoot>.resources . Se RootNamespace não
estava definido no arquivo de projeto, ele era assumido como o nome do projeto. Por
exemplo, o nome do manifesto gerado para um arquivo de recurso chamado Form1.resx
no diretório raiz do projeto era MyProject.Form1.resources.

Do .NET Core 3.0 em diante, se um arquivo de recurso estiver no mesmo local que um
arquivo de origem com o mesmo nome (por exemplo, Form1.resx e Form1.cs), o
MSBuild usará as informações de tipo do arquivo de origem para gerar o nome do
arquivo de manifesto no padrão <Namespace>.<ClassName>.resources . O namespace e o
nome de classe são extraídos do primeiro tipo no arquivo de origem no mesmo local.
Por exemplo, o nome do manifesto gerado para um arquivo de recurso chamado
Form1.resx que está no mesmo local que um arquivo de origem chamado Form1.cs é
MyNamespace.Form1.resources. É importante observar que a primeira parte do nome do
arquivo é diferente das versões anteriores do .NET Core (MyNamespace em vez de
MyProject).

7 Observação

Se o metadado LogicalName , ManifestResourceName ou DependentUpon estiver


especificado em um item EmbeddedResource no arquivo de projeto, essa alteração
não afetará esse arquivo de recurso.

Essa alteração interruptiva foi introduzida com a adição da propriedade


EmbeddedResourceUseDependentUponConvention aos projetos do .NET Core. Por padrão, os

arquivos de recursos não são listados explicitamente em um arquivo de projeto do .NET


Core, portanto, eles não têm metadados DependentUpon para especificar como nomear o
arquivo .resources gerado. Quando EmbeddedResourceUseDependentUponConvention é
definido como true , que é o padrão, o MSBuild procura um arquivo de origem no
mesmo local e extrai um namespace e um nome de classe desse arquivo. Se você definir
EmbeddedResourceUseDependentUponConvention como false , o MSBuild vai gerar o nome
do manifesto de acordo com o comportamento anterior, que combina RootNamespace e
o caminho do arquivo relativo.

Ação recomendada
Na maioria dos casos, nenhuma ação é necessária por parte do desenvolvedor e o
aplicativo deve continuar funcionando. Mas se essa alteração interromper o aplicativo,
você poderá:

Alterar o código para esperar o novo nome do manifesto.

Recusar a nova convenção de nomenclatura definindo


EmbeddedResourceUseDependentUponConvention como false no arquivo de projeto.

XML

<PropertyGroup>

<EmbeddedResourceUseDependentUponConvention>false</EmbeddedResourceUseD
ependentUponConvention>
</PropertyGroup>

Categoria
MSBuild

APIs afetadas
N/D

Rede
O valor padrão de HttpRequestMessage.Version mudou para 1.1

O valor padrão de HttpRequestMessage.Version mudou


para 1.1
O valor padrão da propriedade System.Net.Http.HttpRequestMessage.Version mudou
de 2.0 para 1.1.

Versão introduzida
3.0

Descrição das alterações


No .NET Core 1.0 a 2.0, o valor padrão da propriedade
System.Net.Http.HttpRequestMessage.Version é 1.1. Do .NET Core 2.1 em diante, o valor
foi alterado para 2.1.

Do .NET Core 3.0 em diante, o número de versão padrão retornado pela propriedade
System.Net.Http.HttpRequestMessage.Version é novamente 1.1.

Ação recomendada

Atualize seu código se ele depender do retorno do valor padrão 2.0 pela propriedade
System.Net.Http.HttpRequestMessage.Version.

Categoria
Rede

APIs afetadas
System.Net.Http.HttpRequestMessage.Version

Confira também
Novidades do .NET Core 3.0
Novidades do .NET Core 2.2
Artigo • 10/05/2023

O .NET Core 2.2 inclui aprimoramentos na implantação do aplicativo, na manipulação de


eventos para serviços de runtime, na autenticação de Bancos de Dados SQL do Azure,
no desempenho do compilador JIT e na injeção de código antes da execução do
método Main .

Novo modo de implantação


A partir do .NET Core 2.2, você pode implantar executáveis dependentes da estrutura,
que são arquivos .exe em vez de arquivos .dll. Com uma funcionalidade semelhante às
implantações dependentes de estrutura, os FDEs (executáveis dependentes de estrutura)
ainda contam com a presença de uma versão compartilhada de todo o sistema do .NET
Core para executar. Seu aplicativo contém apenas seu código e quaisquer dependências
de terceiros. Ao contrário de implantações dependentes de estrutura, os FDEs são
específicos da plataforma.

Esse novo modo de implantação tem a vantagem distinta da criar um executável em vez
de uma biblioteca, o que significa que você pode executar seu aplicativo diretamente,
sem invocar dotnet primeiro.

Núcleo
Manipulação de eventos nos serviços de runtime

Muitas vezes, convém monitorar o uso dos serviços de runtime do seu aplicativo, como
o GC, o JIT e o ThreadPool, para entender como eles afetam seu aplicativo. Em sistemas
Windows, isso normalmente é feito pelo monitorando de eventos ETW do processo
atual. Embora isso continue funcionando bem, nem sempre é possível usar ETW se você
estiver executando em um ambiente com poucos privilégios, ou no Linux ou macOS.

A partir do .NET Core 2.2, os eventos de CoreCLR podem ser consumidos usando a
classe System.Diagnostics.Tracing.EventListener. Esses eventos descrevem o
comportamento desses serviços de runtime como GC, JIT, ThreadPool e
interoperabilidade. Esses são os mesmos eventos são expostos como parte do provedor
ETW CoreCLR. Isso permite que os aplicativos consumam esses eventos ou usem um
mecanismo de transporte para enviá-los para um serviço de agregação de telemetria.
Veja como assinar eventos no exemplo de código a seguir:
C#

internal sealed class SimpleEventListener : EventListener


{
// Called whenever an EventSource is created.
protected override void OnEventSourceCreated(EventSource eventSource)
{
// Watch for the .NET runtime EventSource and enable all of its
events.
if (eventSource.Name.Equals("Microsoft-Windows-DotNETRuntime"))
{
EnableEvents(eventSource, EventLevel.Verbose, (EventKeywords)
(-1));
}
}

// Called whenever an event is written.


protected override void OnEventWritten(EventWrittenEventArgs eventData)
{
// Write the contents of the event to the console.
Console.WriteLine($"ThreadID = {eventData.OSThreadId} ID =
{eventData.EventId} Name = {eventData.EventName}");
for (int i = 0; i < eventData.Payload.Count; i++)
{
string payloadString = eventData.Payload[i]?.ToString() ??
string.Empty;
Console.WriteLine($"\tName = \"{eventData.PayloadNames[i]}\"
Value = \"{payloadString}\"");
}
Console.WriteLine("\n");
}
}

Além disso, o .NET Core 2.2 adiciona as duas propriedades a seguir à classe
EventWrittenEventArgs para fornecer informações adicionais sobre eventos ETW:

EventWrittenEventArgs.OSThreadId

EventWrittenEventArgs.TimeStamp

Dados
Autenticação do AAD para Bancos de Dados SQL do Azure com a propriedade
SqlConnection.AccessToken

A partir do .NET Core 2.2, um token de acesso emitido pelo Azure Active Directory pode
ser usado para autenticar em um Banco de Dados SQL do Azure. Para dar suporte a
tokens de acesso, a propriedade AccessToken foi adicionada à classe SqlConnection.
Para aproveitar a autenticação do AAD, baixe a versão 4.6 do pacote NuGet
System.Data.SqlClient. Para usar o recurso, obtenha o valor do token de acesso usando a
Biblioteca de Autenticação do Active Directory para .NET contida no pacote NuGet
Microsoft.IdentityModel.Clients.ActiveDirectory .

Melhorias do compilador JIT


A compilação em camadas permanece um recurso ativado mediante consentimento

No .NET Core 2.1, o compilador JIT implementou uma nova tecnologia compiladora, a
compilação hierárquica, como um recurso ativado mediante consentimento. O objetivo
da compilação em camadas é melhorar o desempenho. Uma das tarefas importantes
executadas pelo compilador JIT é otimizar a execução de código. No entanto, para
caminhos de código pouco usados, o compilador pode gastar mais tempo otimizando o
código do que o runtime gasta executando código não otimizado. A compilação em
camadas introduz dois estágios na compilação JIT:

Uma primeira camada, que gera código o mais rápido possível.

Uma segunda camada, que gera código otimizado para os métodos executados
com frequência. A segunda camada de compilação é executada em paralelo para
melhorar o desempenho.

Para saber mais sobre melhoria de desempenho que pode resultar da compilação em
camadas, consulte Anúncio do .NET Core 2.2 Versão prévia 2 .

Para saber mais sobre o consentimento com a compilação em camadas, confira


Aprimoramentos do compilador Jit em O que há de novo no .NET Core 2.1.

Runtime
Injeção de código antes de executar o método Main

A partir do .NET Core 2.2, você pode usar um gancho de inicialização para injetar código
antes da execução do método Main de um aplicativo. Ganchos de inicialização tornam
possível para um host personalizar o comportamento de aplicativos após eles terem
sido implantados, sem a necessidade de recompilar ou alterar o aplicativo.

Esperamos que os provedores de hospedagem definam a configuração e a política


personalizadas, incluindo configurações que possivelmente influenciam o
comportamento de carregamento do ponto de entrada principal, como o
comportamento System.Runtime.Loader.AssemblyLoadContext. O gancho pode ser
usado para configurar a injeção de rastreamento ou de telemetria, para configurar os
retornos de chamada para tratamento ou para definir outro comportamento
dependente do ambiente. O gancho é separado do ponto de entrada, para que o
código do usuário não precise ser modificado.

Confira Gancho de inicialização do host para saber mais.

Confira também
Novidades do .NET Core 3.1
Novidades do ASP.NET Core 2.2
Novos recursos no EF Core 2.2
Novidades do .NET Core 2.1
Artigo • 09/05/2023

O .NET Core 2.1 contém melhorias e novos recursos nas seguintes áreas:

Ferramentas
Efetuar roll forward
Implantação
Pacote de Compatibilidade do Windows
Aprimoramentos da compilação JIT
Alterações de API

Ferramentas
O SDK do .NET Core 2.1 (v 2.1.300), o conjunto de ferramentas incluído no .NET Core
2.1, inclui as seguintes alterações e aprimoramentos:

Melhorias de desempenho de build


Um dos principais focos do .NET Core 2.1 é melhorar o desempenho de tempo de build,
especialmente para builds incrementais. Essas melhorias de desempenho se aplicam a
ambos os builds de linha de comando que usam dotnet build e a builds no Visual
Studio. Algumas áreas individuais de melhoria incluem:

Na resolução de ativos de pacote, resolução somente de ativos usados por um


build, em vez de todos os ativos.

Armazenamento em cache de referências de assembly.

Uso de servidores de build do SDK de longa execução, que são processos que se
estendem por chamadas individuais de dotnet build . Eles eliminam a necessidade
de compilação JIT de grandes blocos de código toda vez que dotnet build é
executado. Os processos do servidor de build podem ser finalizados
automaticamente com o seguinte comando:

CLI do .NET

dotnet buildserver shutdown

Novos comandos da CLI


Várias ferramentas que estavam disponíveis apenas por projeto usando
DotnetCliToolReference agora estão disponíveis como parte do SDK do .NET Core. Essas
ferramentas incluem:

dotnet watch fornece um observador do sistema de arquivos que aguarda que um


arquivo seja alterado antes de executar um conjunto designado de comandos. Por
exemplo, o comando a seguir recria automaticamente o projeto atual e gera uma
saída detalhada sempre que um arquivo é alterado nele:

CLI do .NET

dotnet watch -- --verbose build

Observe a opção -- que precede a opção --verbose . Isso delimita as opções


passadas diretamente para o comando dotnet watch dos argumentos que são
passados para o processo dotnet filho. Sem isso, a opção --verbose se aplica ao
comando dotnet watch , não ao comando dotnet build .

Para saber mais, confira Desenvolver aplicativos ASP.NET Core usando dotnet
watch.

dotnet dev-certs gera e gerencia os certificados usados durante o


desenvolvimento de aplicativos do ASP.NET Core.

dotnet user-secrets gerencia os segredos em um repositório de segredos do


usuário em aplicativos do ASP.NET Core.

dotnet sql-cache cria uma tabela e índices em um banco de dados do Microsoft

SQL Server a serem usados para armazenamento em cache distribuído.

dotnet ef é uma ferramenta para gerenciar bancos de dados, objetos DbContext e

migrações em aplicativos Entity Framework Core. Para saber mais, confira


Ferramentas da linha de comando do .NET EF Core.

Ferramentas Globais
O .NET Core 2.1 oferece suporte a Ferramentas Globais, ou seja, ferramentas
personalizadas que estão disponíveis globalmente a partir da linha de comando. O
modelo de extensibilidade em versões anteriores do .NET Core disponibilizava
ferramentas personalizadas apenas por projeto usando DotnetCliToolReference .

Para instalar uma Ferramenta Global, use o comando dotnet tool install. Por exemplo:
CLI do .NET

dotnet tool install -g dotnetsay

Uma vez instalada, a ferramenta pode ser executada a partir da linha de comando,
especificando o nome da ferramenta. Para saber mais, confira Visão geral das
Ferramentas Globais do .NET Core.

Gerenciamento de ferramentas com o comando dotnet


tool

No SDK do .NET Core 2.1, todas as operações de ferramentas usam o comando dotnet
tool . As seguintes opções estão disponíveis:

dotnet tool install para instalar uma ferramenta.

dotnet tool update para desinstalar e reinstalar uma ferramenta, o que a atualiza
com eficiência.

dotnet tool list para listar ferramentas atualmente instaladas.

dotnet tool uninstall para desinstalar ferramentas atualmente instaladas.

Efetuar roll forward


Do .NET Core 2.0 em diante, todos os aplicativos .NET Core efetuam roll forward
automaticamente para a última versão secundária instalada em um sistema.

A partir do .NET Core 2.0, se a versão do .NET Core com a qual um aplicativo foi criado
não estiver presente no tempo de execução, o aplicativo será executado
automaticamente com a versão secundária do .NET Core mais recente instalada. Em
outras palavras, se um aplicativo for criado com o .NET Core 2.0, e o .NET Core 2.0 não
estiver presente no sistema do host, mas o .NET Core 2.1 estiver, o aplicativo será
executado com o .NET Core 2.1.

) Importante

Esse comportamento de roll-forward não se aplica a versões prévias. Por padrão,


ele também não se aplica a versões principais, mas isso pode ser alterado com as
configurações abaixo.
Modifique esse comportamento alterando a configuração de roll forward em nenhuma
estrutura compartilhada candidata. As configurações disponíveis são:

0 – desabilitar o comportamento de roll forward da versão secundária. Com essa

configuração, um aplicativo criado para o .NET Core 2.0.0 efetuará roll forward
para o .NET Core 2.0.1, mas não para o .NET Core 2.2.0 ou o .NET Core 3.0.0.
1 – habilitar o comportamento de roll forward da versão secundária. Esse é o valor

padrão para a configuração. Com essa configuração, um aplicativo criado para o


.NET Core 2.0.0 efetuará roll forward para o .NET Core 2.0.1 ou o .NET Core 2.2.0,
dependendo de qual estiver instalado, mas não efetuará roll forward para o .NET
Core 3.0.0.
2 – habilitar o comportamento de roll forward das versões secundária e principal.

Se essa opção estiver definida, até mesmo versões principais diferentes serão
consideradas e, portanto, um aplicativo criado para o .NET Core 2.0.0 efetuará roll
forward para o .NET Core 3.0.0.

Modifique essa configuração de uma das três maneiras:

Defina a variável de ambiente DOTNET_ROLL_FORWARD_ON_NO_CANDIDATE_FX com o


valor desejado.

Adicione a seguinte linha com o valor desejado ao arquivo .runtimeconfig.json:

JSON

"rollForwardOnNoCandidateFx" : 0

Ao usar o .NET Core CLI, adicione a seguinte opção com o valor desejado a um
comando do .NET Core como run :

CLI do .NET

dotnet run --rollForwardOnNoCandidateFx=0

O roll forward da versão de patch é independente dessa configuração e é feito após a


aplicação de qualquer roll forward potencial da versão secundária ou principal.

Implantação

Serviço de aplicativo autocontido


dotnet publish agora publica aplicativos autocontidos com uma versão de runtime

atendido. Quando você publica um aplicativo autocontido com o SDK do .NET Core 2.1
(v 2.1.300), seu aplicativo inclui a versão mais recente de runtime atendido conhecida
por esse SDK. Quando você atualizar para o SDK mais recente, publicará com a versão
mais recente de runtime do .NET Core. Isso se aplica aos runtimes do .NET Core 1.0 e
posteriores.

A publicação autocontida depende de versões de runtime em NuGet.org. Você não


precisa ter o runtime atendido no seu computador.

Com o uso do SDK do .NET Core 2.0, os aplicativos autocontidos são publicados com o
runtime do .NET Core 2.0.0, a menos que uma versão diferente seja especificada por
meio da propriedade RuntimeFrameworkVersion . Com esse novo comportamento, você
não precisará mais definir essa propriedade para selecionar uma versão de runtime
superior para um aplicativo autocontido. A abordagem mais fácil daqui para frente é
sempre publicar com o SDK do .NET Core 2.1 (v 2.1.300).

Veja mais informações em Efetuar roll forward de runtime de implantação


autossuficiente.

Pacote de Compatibilidade do Windows


Ao transmitir código existente do .NET Framework para o .NET Core, você pode usar o
Pacote de Compatibilidade do Windows . Ele fornece acesso 20.000 APIs a mais do
que as disponíveis no .NET Core. Essas APIs incluem tipos no namespace
System.Drawing, a classe EventLog, WMI, contadores de desempenho, Windows Services
e os tipos de registro e membros do Windows.

Melhorias do compilador JIT


O .NET Core incorpora uma nova tecnologia de compilador JIT chamada de compilação
em camadas (também conhecida como otimização adaptativa) que pode melhorar
significativamente o desempenho. A compilação em camadas é uma configuração
opcional.

Uma das tarefas importantes executadas pelo compilador JIT é otimizar a execução de
código. No entanto, para caminhos de código pouco usados, o compilador pode gastar
mais tempo otimizando o código do que o runtime gasta executando código não
otimizado. A compilação em camadas introduz dois estágios na compilação JIT:

Uma primeira camada, que gera código o mais rápido possível.


Uma segunda camada, que gera código otimizado para os métodos executados
com frequência. A segunda camada de compilação é executada em paralelo para
melhorar o desempenho.

Você pode optar pela compilação em camadas de duas maneiras.

Para usar a compilação em camadas em todos os projetos que usam o SDK do


.NET Core 2.1, defina a seguinte variável de ambiente:

Console

COMPlus_TieredCompilation="1"

Para usar a compilação em camadas por projeto, adicione a propriedade


<TieredCompilation> à seção <PropertyGroup> do arquivo de projeto MSBuild,

como mostra o exemplo a seguir:

XML

<PropertyGroup>
<!-- other property definitions -->

<TieredCompilation>true</TieredCompilation>
</PropertyGroup>

Alterações de API

Span<T> e Memory<T>

O .NET Core 2.1 inclui alguns novos tipos de tornam o trabalho com matrizes e outros
tipos de memória muito mais eficiente. Os novos tipos incluem:

System.Span<T> e System.ReadOnlySpan<T>.

System.Memory<T> e System.ReadOnlyMemory<T>.

Sem esses tipos, ao transmitir esses itens como parte de uma matriz ou seção de um
buffer de memória, você precisará fazer uma cópia de uma parte dos dados antes de
transmiti-los a um método. Esses tipos fornecem uma visão virtual desses dados, o que
elimina a necessidade de alocação de memória adicional e operações de cópia.

O exemplo a seguir usa uma instância Span<T> e Memory<T> para fornecer uma visão
virtual de 10 elementos de uma matriz.
C#

using System;

class Program
{
static void Main()
{
int[] numbers = new int[100];
for (int i = 0; i < 100; i++)
{
numbers[i] = i * 2;
}

var part = new Span<int>(numbers, start: 10, length: 10);


foreach (var value in part)
Console.Write($"{value} ");
}
}
// The example displays the following output:
// 20 22 24 26 28 30 32 34 36 38

Compactação Brotli
O .NET Core 2.1 adiciona suporte para compactação e descompactação Brotli. Brotli é
um algoritmo de compactação sem perda, de uso geral, que é definido em RFC 7932
e é compatível com a maioria dos navegadores da Web e com os principais servidores
Web. Você pode usar a classe System.IO.Compression.BrotliStream baseada em fluxo ou
as classes System.IO.Compression.BrotliEncoder e System.IO.Compression.BrotliDecoder
baseadas em span de alto desempenho. O exemplo a seguir ilustra a compactação com
a classe BrotliStream:

C#

public static Stream DecompressWithBrotli(Stream toDecompress)


{
MemoryStream decompressedStream = new MemoryStream();
using (BrotliStream decompressionStream = new BrotliStream(toDecompress,
CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedStream);
}
decompressedStream.Position = 0;
return decompressedStream;
}

O comportamento BrotliStream é o mesmo de DeflateStream e GZipStream, o que


facilita a conversão de código que chame essas APIs para BrotliStream.
Novas APIs de criptografia e aprimoramentos de
criptografia
O .NET Core 2.1 inclui vários aprimoramentos para a APIs de criptografia:

System.Security.Cryptography.Pkcs.SignedCms está disponível no pacote


System.Security.Cryptography.Pkcs. A implementação é igual à classe SignedCms
no .NET Framework.

Novas sobrecargas dos métodos X509Certificate.GetCertHash e


X509Certificate.GetCertHashString aceitam um identificador de algoritmo de hash
para permitir que os chamadores obtenham valores de impressão digital do
certificado usando algoritmos diferentes de SHA-1.

Novas APIs de criptografia baseadas em Span<T> estão disponíveis para hash,


HMAC, geração de número aleatório criptográfico, geração de assinatura
assimétrica, processamento de assinatura assimétrica e criptografia RSA.

O desempenho de System.Security.Cryptography.Rfc2898DeriveBytes melhorou


em cerca de 15% usando uma implementação baseada em Span<T>.

A nova classe System.Security.Cryptography.CryptographicOperations inclui dois


novos métodos:

FixedTimeEquals leva um período fixo para retornar para duas entradas do


mesmo tamanho, o que o torna adequado para uso em verificação criptográfica
a fim de evitar contribuir com informações de temporização.

ZeroMemory é uma rotina de limpeza de memória que não pode ser otimizada.

O método estático RandomNumberGenerator.Fill preenche um Span<T> com


valores aleatórios.

O System.Security.Cryptography.Pkcs.EnvelopedCms agora tem suporte no Linux e


no macOS.

A curva elíptica Diffie-Hellman (ECDH) agora está disponível na família de classes


System.Security.Cryptography.ECDiffieHellman. A área de superfície é o mesmo
que no .NET Framework.

A instância retornada por RSA.Create pode criptografar ou descriptografar com


OAEP usando um resumo de SHA-2, bem como gerar ou validar assinaturas
usando RSA-PSS.
Aperfeiçoamentos de soquetes
O .NET Core inclui um novo tipo, System.Net.Http.SocketsHttpHandler, e um
System.Net.Http.HttpMessageHandler reescrito, que formam a base de APIs de rede de
nível superior. System.Net.Http.SocketsHttpHandler, por exemplo, é a base da
implementação HttpClient. Nas versões anteriores do .NET Core, as APIs de nível
superior eram baseadas em implementações de redes nativas.

A implementação de soquetes introduzida no .NET Core 2.1 tem várias vantagens:

Uma melhoria de desempenho significativa quando comparada com a


implementação anterior.

Eliminação das dependências da plataforma, o que simplifica a implantação e a


manutenção.

Comportamento consistente em todas as plataformas .NET Core.

SocketsHttpHandler é a implementação padrão do .NET Core 2.1. No entanto, você


pode configurar seu aplicativo para usar a classe HttpClientHandler mais antiga
chamando o método AppContext.SetSwitch:

C#

AppContext.SetSwitch("System.Net.Http.UseSocketsHttpHandler", false);

Também é possível usar uma variável de ambiente para desativar o uso de


implementações de soquetes com base em SocketsHttpHandler. Para fazer isso, defina
DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER para false ou 0.

No Windows, você também pode escolher usar System.Net.Http.WinHttpHandler, que


depende de uma implementação nativa, ou a classe SocketsHttpHandler transmitindo
uma instância da classe para o construtor HttpClient.

No Linux e no macOS, só é possível configurar HttpClient por processo. No Linux, você


precisa implantar libcurl se quiser usar a implementação HttpClient antiga. (Ele é
instalado com .NET Core 2.0.)

Alterações da falha
Para obter informações sobre alterações interruptivas, confira Alterações interruptivas
para a migração da versão 2.0 para a 2.1.
Confira também
Novidades do .NET Core 3.1
Novos recursos no EF Core 2.1
Novidades do ASP.NET Core 2.1
Alterações interruptivas no .NET Core
2.1
Artigo • 18/03/2023

Se você estiver migrando para a versão 2.1 do .NET Core, as alterações interruptivas
listadas neste artigo poderão afetar seu aplicativo.

Bibliotecas principais do .NET


As APIs de caminho não geram uma exceção para caracteres inválidos
Campos privados adicionados a tipos de struct internos
Versões do OpenSSL no macOS

As APIs de caminho não geram uma exceção para


caracteres inválidos
As APIs que envolvem caminhos de arquivo não validam mais caracteres de caminho
nem geram ArgumentException quando um caractere inválido é encontrado.

Descrição das alterações

No .NET Framework e no .NET Core 1.0 – 2.0, os métodos listados na seção APIs
afetadas geram ArgumentException quando o argumento de caminho contém um
caractere de caminho inválido. Do .NET Core 2.1 em diante, esses métodos não
verificam mais se há caracteres de caminho inválidos nem geram uma exceção quando
um caractere inválido é encontrado.

Motivo da alteração
A validação agressiva de caracteres de caminho bloqueia alguns cenários
multiplataforma. Essa alteração foi introduzida para que o .NET não tente replicar nem
prever o resultado das chamadas à API do sistema operacional. Para obter mais
informações, confira a postagem no blog Novidade sobre o System.IO no .NET Core 2.1.

Versão introduzida

.NET Core 2.1


Ação recomendada
Se o código depende dessas APIs para verificar se há caracteres inválidos, você pode
adicionar uma chamada a Path.GetInvalidPathChars.

APIs afetadas
System.IO.Directory.CreateDirectory
System.IO.Directory.Delete
System.IO.Directory.EnumerateDirectories
System.IO.Directory.EnumerateFiles
System.IO.Directory.EnumerateFileSystemEntries
System.IO.Directory.GetCreationTime(String)
System.IO.Directory.GetCreationTimeUtc(String)
System.IO.Directory.GetDirectories
System.IO.Directory.GetDirectoryRoot(String)
System.IO.Directory.GetFiles
System.IO.Directory.GetFileSystemEntries
System.IO.Directory.GetLastAccessTime(String)
System.IO.Directory.GetLastAccessTimeUtc(String)
System.IO.Directory.GetLastWriteTime(String)
System.IO.Directory.GetLastWriteTimeUtc(String)
System.IO.Directory.GetParent(String)
System.IO.Directory.Move(String, String)
System.IO.Directory.SetCreationTime(String, DateTime)
System.IO.Directory.SetCreationTimeUtc(String, DateTime)
System.IO.Directory.SetCurrentDirectory(String)
System.IO.Directory.SetLastAccessTime(String, DateTime)
System.IO.Directory.SetLastAccessTimeUtc(String, DateTime)
System.IO.Directory.SetLastWriteTime(String, DateTime)
System.IO.Directory.SetLastWriteTimeUtc(String, DateTime)
System.IO.DirectoryInfo ctor
System.IO.Directory.GetDirectories
System.IO.Directory.GetFiles
System.IO.DirectoryInfo.GetFileSystemInfos
System.IO.File.AppendAllText
System.IO.File.AppendAllTextAsync
System.IO.File.Copy
System.IO.File.Create
System.IO.File.CreateText
System.IO.File.Decrypt
System.IO.File.Delete
System.IO.File.Encrypt
System.IO.File.GetAttributes(String)
System.IO.File.GetCreationTime(String)
System.IO.File.GetCreationTimeUtc(String)
System.IO.File.GetLastAccessTime(String)
System.IO.File.GetLastAccessTimeUtc(String)
System.IO.File.GetLastWriteTime(String)
System.IO.File.GetLastWriteTimeUtc(String)
System.IO.File.Move
System.IO.File.Open
System.IO.File.OpenRead(String)
System.IO.File.OpenText(String)
System.IO.File.OpenWrite(String)
System.IO.File.ReadAllBytes(String)
System.IO.File.ReadAllBytesAsync(String, CancellationToken)
System.IO.File.ReadAllLines(String)
System.IO.File.ReadAllLinesAsync(String, CancellationToken)
System.IO.File.ReadAllText(String)
System.IO.File.ReadAllTextAsync(String, CancellationToken)
System.IO.File.SetAttributes(String, FileAttributes)
System.IO.File.SetCreationTime(String, DateTime)
System.IO.File.SetCreationTimeUtc(String, DateTime)
System.IO.File.SetLastAccessTime(String, DateTime)
System.IO.File.SetLastAccessTimeUtc(String, DateTime)
System.IO.File.SetLastWriteTime(String, DateTime)
System.IO.File.SetLastWriteTimeUtc(String, DateTime)
System.IO.File.WriteAllBytes(String, Byte[])
System.IO.File.WriteAllBytesAsync(String, Byte[], CancellationToken)
System.IO.File.WriteAllLines
System.IO.File.WriteAllLinesAsync
System.IO.File.WriteAllText
System.IO.FileInfo ctor
System.IO.FileInfo.CopyTo
System.IO.FileInfo.MoveTo
System.IO.FileStream ctor
System.IO.Path.GetFullPath(String)
System.IO.Path.IsPathRooted(String)
System.IO.Path.GetPathRoot(String)
System.IO.Path.ChangeExtension(String, String)
System.IO.Path.GetDirectoryName(String)
System.IO.Path.GetExtension(String)
System.IO.Path.HasExtension(String)
System.IO.Path.Combine

Confira também

Novidades do System.IO no .NET Core 2.1

Campos privados adicionados a tipos de struct internos


Campos privados foram adicionados a determinados tipos de struct em assemblies de
referência. Como resultado, em C#, sempre é necessário criar uma instância desses tipos
de struct usando o operador new ou o literal padrão.

Descrição das alterações

No .NET Core 2.0 e nas versões anteriores, era possível criar uma instância de alguns
tipos de struct fornecidos, por exemplo, ConsoleKeyInfo, sem usar o operador new ou o
literal padrão em C#. Isso ocorria porque os assemblies de referência usados pelo
compilador C# não continham os campos privados dos structs. Todos os campos
privados de tipos de struct do .NET são adicionados aos assemblies de referência do
.NET Core 2.1 em diante.

Por exemplo, o seguinte código C# é compilado no .NET Core 2.0, mas não no .NET
Core 2.1:

C#

ConsoleKeyInfo key; // Struct type

if (key.ToString() == "y")
{
Console.WriteLine("Yes!");
}

No .NET Core 2.1, o código anterior resultava no seguinte erro do compilador: CS0165 –
Uso da variável local não atribuída 'key'

Versão introduzida

2.1
Ação recomendada
Crie instâncias de tipos de struct usando o operador new ou o literal padrão.

Por exemplo:

C#

ConsoleKeyInfo key = new ConsoleKeyInfo(); // Struct type.

if (key.ToString() == "y")
Console.WriteLine("Yes!");

C#

ConsoleKeyInfo key = default; // Struct type.

if (key.ToString() == "y")
Console.WriteLine("Yes!");

Categoria
Bibliotecas principais do .NET

APIs afetadas
System.ArraySegment<T>.Enumerator
System.ArraySegment<T>
System.Boolean
System.Buffers.MemoryHandle
System.Buffers.StandardFormat
System.Byte
System.Char
System.Collections.DictionaryEntry
System.Collections.Generic.Dictionary<TKey,TValue>.Enumerator
System.Collections.Generic.Dictionary<TKey,TValue>.KeyCollection.Enumerator
System.Collections.Generic.Dictionary<TKey,TValue>.ValueCollection.Enumerator
System.Collections.Generic.HashSet<T>.Enumerator
System.Collections.Generic.KeyValuePair<TKey,TValue>
System.Collections.Generic.LinkedList<T>.Enumerator
System.Collections.Generic.List<T>.Enumerator
System.Collections.Generic.Queue<T>.Enumerator
System.Collections.Generic.SortedDictionary<TKey,TValue>.Enumerator
System.Collections.Generic.SortedDictionary<TKey,TValue>.KeyCollection.Enumerat
or
System.Collections.Generic.SortedDictionary<TKey,TValue>.ValueCollection.Enumer
ator
System.Collections.Generic.SortedSet<T>.Enumerator
System.Collections.Generic.Stack<T>.Enumerator
System.Collections.Immutable.ImmutableArray<T>.Enumerator
System.Collections.Immutable.ImmutableArray<T>
System.Collections.Immutable.ImmutableDictionary<TKey,TValue>.Enumerator
System.Collections.Immutable.ImmutableHashSet<T>.Enumerator
System.Collections.Immutable.ImmutableList<T>.Enumerator
System.Collections.Immutable.ImmutableQueue<T>.Enumerator
System.Collections.Immutable.ImmutableSortedDictionary<TKey,TValue>.Enumerat
or
System.Collections.Immutable.ImmutableSortedSet<T>.Enumerator
System.Collections.Immutable.ImmutableStack<T>.Enumerator
System.Collections.Specialized.BitVector32.Section
System.Collections.Specialized.BitVector32
LazyMemberInfo
System.ComponentModel.Design.Serialization.MemberRelationship
System.ConsoleKeyInfo
System.Data.SqlTypes.SqlBinary
System.Data.SqlTypes.SqlBoolean
System.Data.SqlTypes.SqlByte
System.Data.SqlTypes.SqlDateTime
System.Data.SqlTypes.SqlDecimal
System.Data.SqlTypes.SqlDouble
System.Data.SqlTypes.SqlGuid
System.Data.SqlTypes.SqlInt16
System.Data.SqlTypes.SqlInt32
System.Data.SqlTypes.SqlInt64
System.Data.SqlTypes.SqlMoney
System.Data.SqlTypes.SqlSingle
System.Data.SqlTypes.SqlString
System.DateTime
System.DateTimeOffset
System.Decimal
System.Diagnostics.CounterSample
System.Diagnostics.SymbolStore.SymbolToken
System.Diagnostics.Tracing.EventSource.EventData
System.Diagnostics.Tracing.EventSourceOptions
System.Double
System.Drawing.CharacterRange
System.Drawing.Point
System.Drawing.PointF
System.Drawing.Rectangle
System.Drawing.RectangleF
System.Drawing.Size
System.Drawing.SizeF
System.Guid
System.HashCode
System.Int16
System.Int32
System.Int64
System.IntPtr
System.IO.Pipelines.FlushResult
System.IO.Pipelines.ReadResult
System.IO.WaitForChangedResult
System.Memory<T>
System.ModuleHandle
System.Net.Security.SslApplicationProtocol
System.Net.Sockets.IPPacketInformation
System.Net.Sockets.SocketInformation
System.Net.Sockets.UdpReceiveResult
System.Net.WebSockets.ValueWebSocketReceiveResult
System.Nullable<T>
System.Numerics.BigInteger
System.Numerics.Complex
System.Numerics.Vector<T>
System.ReadOnlyMemory<T>
System.ReadOnlySpan<T>.Enumerator
System.ReadOnlySpan<T>
System.Reflection.CustomAttributeNamedArgument
System.Reflection.CustomAttributeTypedArgument
System.Reflection.Emit.Label
System.Reflection.Emit.OpCode
System.Reflection.Metadata.ArrayShape
System.Reflection.Metadata.AssemblyDefinition
System.Reflection.Metadata.AssemblyDefinitionHandle
System.Reflection.Metadata.AssemblyFile
System.Reflection.Metadata.AssemblyFileHandle
System.Reflection.Metadata.AssemblyFileHandleCollection.Enumerator
System.Reflection.Metadata.AssemblyFileHandleCollection
System.Reflection.Metadata.AssemblyReference
System.Reflection.Metadata.AssemblyReferenceHandle
System.Reflection.Metadata.AssemblyReferenceHandleCollection.Enumerator
System.Reflection.Metadata.AssemblyReferenceHandleCollection
System.Reflection.Metadata.Blob
System.Reflection.Metadata.BlobBuilder.Blobs
System.Reflection.Metadata.BlobContentId
System.Reflection.Metadata.BlobHandle
System.Reflection.Metadata.BlobReader
System.Reflection.Metadata.BlobWriter
System.Reflection.Metadata.Constant
System.Reflection.Metadata.ConstantHandle
System.Reflection.Metadata.CustomAttribute
System.Reflection.Metadata.CustomAttributeHandle
System.Reflection.Metadata.CustomAttributeHandleCollection.Enumerator
System.Reflection.Metadata.CustomAttributeHandleCollection
System.Reflection.Metadata.CustomAttributeNamedArgument<TType>
System.Reflection.Metadata.CustomAttributeTypedArgument<TType>
System.Reflection.Metadata.CustomAttributeValue<TType>
System.Reflection.Metadata.CustomDebugInformation
System.Reflection.Metadata.CustomDebugInformationHandle
System.Reflection.Metadata.CustomDebugInformationHandleCollection.Enumerato
r
System.Reflection.Metadata.CustomDebugInformationHandleCollection
System.Reflection.Metadata.DeclarativeSecurityAttribute
System.Reflection.Metadata.DeclarativeSecurityAttributeHandle
System.Reflection.Metadata.DeclarativeSecurityAttributeHandleCollection.Enumera
tor
System.Reflection.Metadata.DeclarativeSecurityAttributeHandleCollection
System.Reflection.Metadata.Document
System.Reflection.Metadata.DocumentHandle
System.Reflection.Metadata.DocumentHandleCollection.Enumerator
System.Reflection.Metadata.DocumentHandleCollection
System.Reflection.Metadata.DocumentNameBlobHandle
System.Reflection.Metadata.Ecma335.ArrayShapeEncoder
System.Reflection.Metadata.Ecma335.BlobEncoder
System.Reflection.Metadata.Ecma335.CustomAttributeArrayTypeEncoder
System.Reflection.Metadata.Ecma335.CustomAttributeElementTypeEncoder
System.Reflection.Metadata.Ecma335.CustomAttributeNamedArgumentsEncoder
System.Reflection.Metadata.Ecma335.CustomModifiersEncoder
System.Reflection.Metadata.Ecma335.EditAndContinueLogEntry
System.Reflection.Metadata.Ecma335.ExceptionRegionEncoder
System.Reflection.Metadata.Ecma335.FixedArgumentsEncoder
System.Reflection.Metadata.Ecma335.GenericTypeArgumentsEncoder
System.Reflection.Metadata.Ecma335.InstructionEncoder
System.Reflection.Metadata.Ecma335.LabelHandle
System.Reflection.Metadata.Ecma335.LiteralEncoder
System.Reflection.Metadata.Ecma335.LiteralsEncoder
System.Reflection.Metadata.Ecma335.LocalVariablesEncoder
System.Reflection.Metadata.Ecma335.LocalVariableTypeEncoder
System.Reflection.Metadata.Ecma335.MethodBodyStreamEncoder.MethodBody
System.Reflection.Metadata.Ecma335.MethodBodyStreamEncoder
System.Reflection.Metadata.Ecma335.MethodSignatureEncoder
System.Reflection.Metadata.Ecma335.NamedArgumentsEncoder
System.Reflection.Metadata.Ecma335.NamedArgumentTypeEncoder
System.Reflection.Metadata.Ecma335.NameEncoder
System.Reflection.Metadata.Ecma335.ParametersEncoder
System.Reflection.Metadata.Ecma335.ParameterTypeEncoder
System.Reflection.Metadata.Ecma335.PermissionSetEncoder
System.Reflection.Metadata.Ecma335.ReturnTypeEncoder
System.Reflection.Metadata.Ecma335.ScalarEncoder
System.Reflection.Metadata.Ecma335.SignatureDecoder<TType,TGenericContext>
System.Reflection.Metadata.Ecma335.SignatureTypeEncoder
System.Reflection.Metadata.Ecma335.VectorEncoder
System.Reflection.Metadata.EntityHandle
System.Reflection.Metadata.EventAccessors
System.Reflection.Metadata.EventDefinition
System.Reflection.Metadata.EventDefinitionHandle
System.Reflection.Metadata.EventDefinitionHandleCollection.Enumerator
System.Reflection.Metadata.EventDefinitionHandleCollection
System.Reflection.Metadata.ExceptionRegion
System.Reflection.Metadata.ExportedType
System.Reflection.Metadata.ExportedTypeHandle
System.Reflection.Metadata.ExportedTypeHandleCollection.Enumerator
System.Reflection.Metadata.ExportedTypeHandleCollection
System.Reflection.Metadata.FieldDefinition
System.Reflection.Metadata.FieldDefinitionHandle
System.Reflection.Metadata.FieldDefinitionHandleCollection.Enumerator
System.Reflection.Metadata.FieldDefinitionHandleCollection
System.Reflection.Metadata.GenericParameter
System.Reflection.Metadata.GenericParameterConstraint
System.Reflection.Metadata.GenericParameterConstraintHandle
System.Reflection.Metadata.GenericParameterConstraintHandleCollection.Enumera
tor
System.Reflection.Metadata.GenericParameterConstraintHandleCollection
System.Reflection.Metadata.GenericParameterHandle
System.Reflection.Metadata.GenericParameterHandleCollection.Enumerator
System.Reflection.Metadata.GenericParameterHandleCollection
System.Reflection.Metadata.GuidHandle
System.Reflection.Metadata.Handle
System.Reflection.Metadata.ImportDefinition
System.Reflection.Metadata.ImportDefinitionCollection.Enumerator
System.Reflection.Metadata.ImportDefinitionCollection
System.Reflection.Metadata.ImportScope
System.Reflection.Metadata.ImportScopeCollection.Enumerator
System.Reflection.Metadata.ImportScopeCollection
System.Reflection.Metadata.ImportScopeHandle
System.Reflection.Metadata.InterfaceImplementation
System.Reflection.Metadata.InterfaceImplementationHandle
System.Reflection.Metadata.InterfaceImplementationHandleCollection.Enumerator
System.Reflection.Metadata.InterfaceImplementationHandleCollection
System.Reflection.Metadata.LocalConstant
System.Reflection.Metadata.LocalConstantHandle
System.Reflection.Metadata.LocalConstantHandleCollection.Enumerator
System.Reflection.Metadata.LocalConstantHandleCollection
System.Reflection.Metadata.LocalScope
System.Reflection.Metadata.LocalScopeHandle
System.Reflection.Metadata.LocalScopeHandleCollection.ChildrenEnumerator
System.Reflection.Metadata.LocalScopeHandleCollection.Enumerator
System.Reflection.Metadata.LocalScopeHandleCollection
System.Reflection.Metadata.LocalVariable
System.Reflection.Metadata.LocalVariableHandle
System.Reflection.Metadata.LocalVariableHandleCollection.Enumerator
System.Reflection.Metadata.LocalVariableHandleCollection
System.Reflection.Metadata.ManifestResource
System.Reflection.Metadata.ManifestResourceHandle
System.Reflection.Metadata.ManifestResourceHandleCollection.Enumerator
System.Reflection.Metadata.ManifestResourceHandleCollection
System.Reflection.Metadata.MemberReference
System.Reflection.Metadata.MemberReferenceHandle
System.Reflection.Metadata.MemberReferenceHandleCollection.Enumerator
System.Reflection.Metadata.MemberReferenceHandleCollection
System.Reflection.Metadata.MetadataStringComparer
System.Reflection.Metadata.MethodDebugInformation
System.Reflection.Metadata.MethodDebugInformationHandle
System.Reflection.Metadata.MethodDebugInformationHandleCollection.Enumerato
r
System.Reflection.Metadata.MethodDebugInformationHandleCollection
System.Reflection.Metadata.MethodDefinition
System.Reflection.Metadata.MethodDefinitionHandle
System.Reflection.Metadata.MethodDefinitionHandleCollection.Enumerator
System.Reflection.Metadata.MethodDefinitionHandleCollection
System.Reflection.Metadata.MethodImplementation
System.Reflection.Metadata.MethodImplementationHandle
System.Reflection.Metadata.MethodImplementationHandleCollection.Enumerator
System.Reflection.Metadata.MethodImplementationHandleCollection
System.Reflection.Metadata.MethodImport
System.Reflection.Metadata.MethodSignature<TType>
System.Reflection.Metadata.MethodSpecification
System.Reflection.Metadata.MethodSpecificationHandle
System.Reflection.Metadata.ModuleDefinition
System.Reflection.Metadata.ModuleDefinitionHandle
System.Reflection.Metadata.ModuleReference
System.Reflection.Metadata.ModuleReferenceHandle
System.Reflection.Metadata.NamespaceDefinition
System.Reflection.Metadata.NamespaceDefinitionHandle
System.Reflection.Metadata.Parameter
System.Reflection.Metadata.ParameterHandle
System.Reflection.Metadata.ParameterHandleCollection.Enumerator
System.Reflection.Metadata.ParameterHandleCollection
System.Reflection.Metadata.PropertyAccessors
System.Reflection.Metadata.PropertyDefinition
System.Reflection.Metadata.PropertyDefinitionHandle
System.Reflection.Metadata.PropertyDefinitionHandleCollection.Enumerator
System.Reflection.Metadata.PropertyDefinitionHandleCollection
System.Reflection.Metadata.ReservedBlob<THandle>
System.Reflection.Metadata.SequencePoint
System.Reflection.Metadata.SequencePointCollection.Enumerator
System.Reflection.Metadata.SequencePointCollection
System.Reflection.Metadata.SignatureHeader
System.Reflection.Metadata.StandaloneSignature
System.Reflection.Metadata.StandaloneSignatureHandle
System.Reflection.Metadata.StringHandle
System.Reflection.Metadata.TypeDefinition
System.Reflection.Metadata.TypeDefinitionHandle
System.Reflection.Metadata.TypeDefinitionHandleCollection.Enumerator
System.Reflection.Metadata.TypeDefinitionHandleCollection
System.Reflection.Metadata.TypeLayout
System.Reflection.Metadata.TypeReference
System.Reflection.Metadata.TypeReferenceHandle
System.Reflection.Metadata.TypeReferenceHandleCollection.Enumerator
System.Reflection.Metadata.TypeReferenceHandleCollection
System.Reflection.Metadata.TypeSpecification
System.Reflection.Metadata.TypeSpecificationHandle
System.Reflection.Metadata.UserStringHandle
System.Reflection.ParameterModifier
System.Reflection.PortableExecutable.CodeViewDebugDirectoryData
System.Reflection.PortableExecutable.DebugDirectoryEntry
System.Reflection.PortableExecutable.PEMemoryBlock
System.Reflection.PortableExecutable.SectionHeader
System.Runtime.CompilerServices.AsyncTaskMethodBuilder<TResult>
System.Runtime.CompilerServices.AsyncTaskMethodBuilder
System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder<TResult>
System.Runtime.CompilerServices.AsyncValueTaskMethodBuilder
System.Runtime.CompilerServices.AsyncVoidMethodBuilder
System.Runtime.CompilerServices.ConfiguredTaskAwaitable<TResult>.ConfiguredT
askAwaiter
System.Runtime.CompilerServices.ConfiguredTaskAwaitable<TResult>
System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter
System.Runtime.CompilerServices.ConfiguredTaskAwaitable
System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<TResult>
System.Runtime.CompilerServices.ConfiguredValueTaskAwaitable<TResult>.Config
uredValueTaskAwaiter
System.Runtime.CompilerServices.TaskAwaiter<TResult>
System.Runtime.CompilerServices.TaskAwaiter
System.Runtime.CompilerServices.ValueTaskAwaiter<TResult>
System.Runtime.CompilerServices.ValueTaskAwaiter<TResult>
System.Runtime.InteropServices.ArrayWithOffset
System.Runtime.InteropServices.GCHandle
System.Runtime.InteropServices.HandleRef
System.Runtime.InteropServices.OSPlatform
System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken
System.Runtime.Serialization.SerializationEntry
System.Runtime.Serialization.StreamingContext
System.RuntimeArgumentHandle
System.RuntimeFieldHandle
System.RuntimeMethodHandle
System.RuntimeTypeHandle
System.SByte
System.Security.Cryptography.CngProperty
System.Security.Cryptography.ECCurve
System.Security.Cryptography.HashAlgorithmName
System.Security.Cryptography.X509Certificates.X509ChainStatus
System.Security.Cryptography.Xml.X509IssuerSerial
System.ServiceProcess.SessionChangeDescription
System.Single
System.Span<T>.Enumerator
System.Span<T>
System.Threading.AsyncFlowControl
System.Threading.AsyncLocalValueChangedArgs<T>
System.Threading.CancellationToken
System.Threading.CancellationTokenRegistration
System.Threading.LockCookie
System.Threading.SpinLock
System.Threading.SpinWait
System.Threading.Tasks.Dataflow.DataflowMessageHeader
System.Threading.Tasks.ParallelLoopResult
System.Threading.Tasks.ValueTask<TResult>
System.TimeSpan
System.TimeZoneInfo.TransitionTime
System.Transactions.TransactionOptions
System.TypedReference
System.TypedReference
System.UInt16
System.UInt32
System.UInt64
System.UIntPtr
System.Windows.Forms.ColorDialog.Color
System.Windows.Media.Animation.KeyTime
System.Windows.Media.Animation.RepeatBehavior
System.Xml.Serialization.XmlDeserializationEvents
Windows.Foundation.Point
Windows.Foundation.Rect
Windows.Foundation.Size
Windows.UI.Color
Windows.UI.Xaml.Controls.Primitives.GeneratorPosition
Windows.UI.Xaml.CornerRadius
Windows.UI.Xaml.Duration
Windows.UI.Xaml.GridLength
Windows.UI.Xaml.Media.Matrix
Windows.UI.Xaml.Media.Media3D.Matrix3D
Windows.UI.Xaml.Thickness

Versões do OpenSSL no macOS


O .NET Core 3.0 e os runtimes posteriores no macOS agora preferem as versões do
OpenSSL 1.1.x a 1.0.x para os tipos AesCcm, AesGcm, DSAOpenSsl,
ECDiffieHellmanOpenSsl, ECDsaOpenSsl, RSAOpenSsl e SafeEvpPKeyHandle.

O runtime do .NET Core 2.1 agora dá suporte a versões do OpenSSL 1.1.x, mas ainda
prefere as versões do OpenSSL 1.0.x.

Descrição das alterações


Antes, o runtime do .NET Core usava as versões do OpenSSL 1.0.x no macOS para tipos
que interagiam com o OpenSSL. A versão mais recente do OpenSSL 1.0.x, que é a
OpenSSL 1.0.2, agora está sem suporte. Para manter os tipos que usam o OpenSSL em
versões com suporte do OpenSSL, os runtimes do .NET Core 3.0 e posteriores agora
usam versões mais recentes do OpenSSL no macOS.

Com essa alteração, o comportamento dos runtimes do .NET Core no macOS é o


seguinte:

Os runtimes do .NET Core 3.0 e de versões posteriores usam o OpenSSL 1.1.x, se


disponível, e retornam ao OpenSSL 1.0.x somente quando não há nenhuma versão
1.1.x disponível.
Para chamadores que usam os tipos de interoperabilidade do OpenSSL com
P/Invokes personalizados, siga as diretrizes nas observações de
SafeEvpPKeyHandle.OpenSslVersion. O aplicativo poderá falhar se você não
verificar o valor de OpenSslVersion.

O runtime do .NET Core 2.1 usa o OpenSSL 1.0.x, se disponível, e retorna ao


OpenSSL 1.1.x quando não há nenhuma versão 1.0.x disponível.

O runtime 2.1 prefere a versão anterior do OpenSSL porque a propriedade


SafeEvpPKeyHandle.OpenSslVersion não existe no .NET Core 2.1, portanto, a
versão do OpenSSL não pode ser determinada com confiança em tempo de
execução.

Versão introduzida
.NET Core 2.1.16
.NET Core 3.0.3
.NET Core 3.1.2

Ação recomendada

Desinstale o OpenSSL versão 1.0.2 se ele não for mais necessário.

Instale o OpenSSL 1.1.x se você usar os tipos AesCcm, AesGcm, DSAOpenSsl,


ECDiffieHellmanOpenSsl, ECDsaOpenSsl, RSAOpenSsl ou SafeEvpPKeyHandle.

Se você usar os tipos de interoperabilidade do OpenSSL com P/Invokes


personalizados, siga as diretrizes nas observações de
SafeEvpPKeyHandle.OpenSslVersion.

Categoria

Bibliotecas principais do .NET

APIs afetadas

System.Security.Cryptography.AesCcm
System.Security.Cryptography.AesGcm
System.Security.Cryptography.DSAOpenSsl
System.Security.Cryptography.ECDiffieHellmanOpenSsl
System.Security.Cryptography.ECDsaOpenSsl
System.Security.Cryptography.RSAOpenSsl
System.Security.Cryptography.SafeEvpPKeyHandle

MSBuild
As ferramentas de projeto agora estão incluídas no SDK

As ferramentas de projeto agora estão incluídas no SDK


O SDK do .NET Core 2.1 agora inclui ferramentas comuns da CLI e você não precisa mais
fazer referência a essas ferramentas do projeto.

Descrição das alterações


No .NET Core 2.0, os projetos fazem referência a ferramentas externas do .NET com a
configuração de projeto <DotNetCliToolReference> . No .NET Core 2.1, algumas dessas
ferramentas são incluídas no SDK do .NET Core e a configuração não é mais necessária.
Se você incluir referências a essas ferramentas no projeto, receberá um erro semelhante
ao seguinte: A ferramenta 'Microsoft.EntityFrameworkCore.Tools.DotNet' agora está
incluída no SDK do .NET Core.

Ferramentas agora incluídas no SDK do .NET Core 2.1:

<DotNetCliToolReference> value Ferramenta

Microsoft.DotNet.Watcher.Tools dotnet-watch

Microsoft.Extensions.SecretManager.Tools dotnet-user-secrets

Microsoft.Extensions.Caching.SqlConfig.Tools dotnet-sql-cache

Microsoft.EntityFrameworkCore.Tools.DotNet dotnet-ef

Versão introduzida

SDK do .NET Core 2.1.300

Ação recomendada

Remova a configuração <DotNetCliToolReference> do projeto.

Categoria
MSBuild

APIs afetadas
N/D

Confira também
Novidades do .NET Core 2.1
Novidades do .NET Core 2.0
Artigo • 10/05/2023

O .NET Core 2.0 contém melhorias e novos recursos nas seguintes áreas:

Ferramentas
Suporte ao idioma
Aprimoramentos da plataforma
Alterações de API
integração com o Visual Studio
Aprimoramentos de documentação

Ferramentas

dotnet restore é executado implicitamente


Em versões anteriores do .NET Core, era preciso executar o comando dotnet restore
para baixar dependências logo após a criação de um novo projeto com o comando
dotnet new, bem como sempre que uma nova dependência era adicionada ao seu
projeto.

Não é necessário executar dotnet restore, pois ele é executado implicitamente por
todos os comandos que exigem uma restauração, como dotnet new , dotnet build ,
dotnet run , dotnet test , dotnet publish e dotnet pack . Para desabilitar a restauração
implícita, use a opção --no-restore .

O comando dotnet restore ainda é útil em determinados cenários em que realizar uma
restauração explícita faz sentido, como compilações de integração contínua no Azure
DevOps Services ou em sistemas de compilação que precisam controlar explicitamente
quando a restauração ocorrerá.

Para obter informações sobre como gerenciar feeds do NuGet, confira a documentação
do dotnet restore.

Você também pode desabilitar a invocação automática do dotnet restore transmitindo


a opção --no-restore para os comandos new , run , build , publish , pack e test .

Redirecionar para o .NET Core 2.0


Se o SDK do .NET Core 2.0 estiver instalado, os projetos destinados ao .NET Core 1.x
poderão ser redirecionados para o .NET Core 2.0.

Para redirecionar para o .NET Core 2.0, edite o arquivo do projeto alterando o valor do
elemento <TargetFramework> (ou o elemento <TargetFrameworks> se você tiver mais de
um destino no seu arquivo de projeto) de 1.x para 2.0:

XML

<PropertyGroup>
<TargetFramework>netcoreapp2.0</TargetFramework>
</PropertyGroup>

Você também pode redirecionar bibliotecas do .NET Standard para o .NET Standard 2.0
da mesma maneira:

XML

<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>

Para mais informações sobre como migrar seu projeto para o .NET Core 2.0, confira
Migrar do ASP.NET Core 1.x para o ASP.NET Core 2.0.

Suporte ao idioma
Além do suporte a C# e F#, o .NET Core 2.0 também suporta Visual Basic.

Visual Basic
Com a versão 2.0, o .NET Core agora oferece suporte para o Visual Basic 2017. Você
pode usar o Visual Basic para criar os seguintes tipos de projeto:

Aplicativo de console do .NET Core


Bibliotecas de classe do .NET Core
Bibliotecas de classe do .NET Standard
Projetos de testes de unidade do .NET Core
Projetos de testes de xUnit do .NET Core

Por exemplo, para criar um aplicativo "Olá, Mundo" do Visual Basic, siga as seguintes
etapas da linha de comando:
1. Abra uma janela do console, crie um diretório para seu projeto e torne-o o
diretório atual.

2. Digite o comando dotnet new console -lang vb .

O comando cria um arquivo de projeto com uma extensão de arquivo .vbproj ,


junto com um arquivo de código-fonte do Visual Basic chamado Program.vb. Este
arquivo contém o código-fonte para gravar a cadeia de caracteres "Olá, Mundo!"
na janela do console.

3. Digite o comando dotnet run . A CLI do .NET Core compila e executa


automaticamente o aplicativo, que exibe a mensagem "Olá, Mundo!" na janela do
console.

Suporte para C# 7.1


O .NET Core 2.0 suporta C# 7.1, que adiciona uma série de novos recursos, incluindo:

O método Main , o ponto de entrada do aplicativo, pode ser marcado com a


palavra-chave async.
Nomes de tuplas inferidos.
Expressões padrão.

Aprimoramentos da plataforma
O .NET Core 2.0 inclui uma série de recursos que facilitam a instalação do .NET Core e o
uso dele em sistemas operacionais com suporte.

O .NET Core para Linux é uma implementação única


O .NET Core 2.0 oferece uma implementação única do Linux que funciona em várias
distribuições do Linux. O .NET Core 1.x requer que você baixe uma implementação do
Linux específica para distribuição.

Você também pode desenvolver aplicativos destinados ao Linux como um sistema


operacional único. O .NET Core 1.x requer que você direcione cada distribuição do Linux
separadamente.

Suporte para as bibliotecas criptográficas da Apple


O .NET Core 1.x no macOS exigia a biblioteca criptográfica do kit de ferramentas
OpenSSL. O .NET Core 2.0 usa as bibliotecas criptográficas da Apple e não requer o
OpenSSL, portanto, não é mais preciso instalá-lo.

Alterações de API e suporte à biblioteca

Suporte para .NET Standard 2.0


O .NET Standard define um conjunto com versões das APIs que devem estar disponíveis
em implementações .NET que estejam de acordo com a versão standard. O .NET
Standard é destinado a desenvolvedores de bibliotecas. O objetivo dele é garantir a
funcionalidade que está disponível para uma biblioteca destinada a uma versão do .NET
Standard em cada implementação do .NET. O .NET Core 1.x dá suporte ao .NET Standard
versão 1.6; o .NET Core 2.0 dá suporte à versão mais recente, o .NET Standard 2.0. Para
obter mais informações, confira .NET Standard.

O .NET Standard 2.0 inclui 20.000 APIs a mais do que havia disponível no .NET Standard
1.6. A maior parte dessa área de superfície expandida resulta da incorporação de APIs
que são comuns ao .NET Framework e Xamarin no .NET Standard.

As bibliotecas de classe .NET Standard 2.0 também podem fazer referência às


bibliotecas de classe do .NET Framework, desde que elas chamem APIs que estão
presentes no .NET Standard 2.0. Nenhuma recompilação das bibliotecas do .NET
Framework é necessária.

Para obter uma lista com as APIs que foram adicionadas ao .NET Standard desde a
última versão, o .NET Standard 1.6, confira .NET Standard 2.0 vs. 1.6 .

Área de superfície expandida


O número total de APIs disponíveis no .NET Core 2.0 mais que dobrou em comparação
com o .NET Core 1.1.

E com o Pacote de Compatibilidade do Windows, a portabilidade do .NET Framework


também se tornou muito mais simples.

Suporte a bibliotecas do .NET Framework


O código .NET Core pode fazer referência a bibliotecas existentes do .NET Framework,
incluindo pacotes do NuGet. Observe que as bibliotecas devem usar APIs que são
encontradas no .NET Standard.
integração com o Visual Studio
O Visual Studio 2017 versão 15.3 e, em alguns casos, o Visual Studio para Mac oferece
vários aprimoramentos significativos para os desenvolvedores do .NET Core.

Redirecionamento de aplicativos .NET Core e bibliotecas


do .NET Standard
Se o SDK do .NET Core 2.0 estiver instalado, você poderá redirecionar projetos do .NET
Core 1.x para o .NET Core 2.0 e bibliotecas do .NET Standard 1.x para o .NET Standard
2.0.

Para redirecionar seu projeto no Visual Studio, abra a guia Aplicativo da caixa de
diálogo de propriedades do projeto e altere o valor Estrutura de destino para .NET
Core 2.0 ou .NET Standard 2.0. Você também pode alterar clicando com o botão direito
do mouse no projeto e selecionando a opção Editar arquivo *.csproj. Para obter mais
informações, confira a seção Ferramentas neste tópico.

Suporte a Live Unit Testing para .NET Core


Sempre que você modifica o código, o Live Unit Testing executa os testes de unidade
afetados automaticamente em segundo plano e exibe os resultados e a cobertura de
código de forma dinâmica no ambiente do Visual Studio. O .NET Core 2.0 agora oferece
suporte ao Live Unit Testing. Anteriormente, o Live Unit Testing estava disponível
somente para aplicativos do .NET Framework.

Para obter mais informações, confira Live Unit Testing com o Visual Studio e Perguntas
Frequentes sobre o Live Unit Testing.

Melhor suporte a várias estruturas de destino


Se você estiver criando um projeto para várias estruturas de destino, selecione a
plataforma de destino no menu de nível superior. Na imagem a seguir, um projeto
chamado SCD1 é direcionado ao macOS X 10.11 ( osx.10.11-x64 ) de 64 bits e ao
Windows 10/Windows Server 2016 ( win10-x64 ) de 64 bits. Selecione a estrutura de
destino antes de selecionar o botão do projeto, neste caso para executar um build de
depuração.
Suporte lado a lado para SDKs do .NET Core
Agora você pode instalar o SDK do .NET Core independentemente do Visual Studio. Isso
possibilita que uma única versão do Visual Studio crie projetos destinados a diferentes
versões do .NET Core. Anteriormente, o Visual Studio e o SDK do .NET Core foram
acoplados; uma versão específica do SDK acompanhava uma versão específica do Visual
Studio.

Melhorias na documentação

Arquitetura do Aplicativo .NET


A Arquitetura do Aplicativo .NET proporciona o acesso a um conjunto de livros
eletrônicos que oferecem diretrizes, práticas recomendadas e aplicativos de exemplo ao
usar o .NET para compilar:

Microsserviços e contêineres do Docker


Aplicativos Web com o ASP.NET
Aplicativos móveis com o Xamarin
Aplicativos que são implantados na Nuvem com o Azure

Confira também
Novidades do ASP.NET Core 2.0
Novidades no .NET Standard
Artigo • 10/05/2023

O .NET Standard é uma especificação formal que define um conjunto com versões das
APIs que devem estar disponíveis em implementações .NET que estejam de acordo com
a versão do Standard. O .NET Standard é destinado a desenvolvedores de bibliotecas.
Uma biblioteca direcionada a uma versão do .NET Standard pode ser usada em
qualquer implementação do .NET ou do Xamarin que dê suporte a essa versão do
Standard.

O .NET Standard está incluído no SDK do .NET. Ele também será incluído no Visual
Studio se você selecionar a carga de trabalho do .NET.

O .NET Standard 2.1 é a última versão do .NET Standard que será lançada. Para obter
mais informações, confira .NET 5+ e .NET Standard.

Implementações .NET com suporte


O .NET Standard 2.1 tem suporte das seguintes implementações do .NET:

.NET Core 3.0 ou posterior (incluindo .NET 5 e posterior)


Mono 6.4 ou posterior
Xamarin.iOS 12.16 ou posterior
Xamarin.Android 10.0 ou posterior

O .NET Standard 2.0 tem suporte das seguintes implementações do .NET:

.NET Core 2.0 ou posterior (incluindo .NET 5 e posterior)


.NET Framework 4.6.1 ou posterior
Mono 5.4 ou posterior
Xamarin.iOS 10.14 ou posterior
Xamarin.Mac 3.8 ou posterior
Xamarin.Android 8.0 ou posterior
Plataforma Universal do Windows 10.0.16299 ou posterior

Novidades no .NET Standard 2.1


O .NET Standard 2.1 adiciona muitas APIs ao Standard. Algumas delas são novas APIs e
outras são APIs existentes que ajudam a convergir ainda mais as implementações do
.NET. Para obter uma lista com as APIs que foram adicionadas ao .NET Standard 2.1,
confira .NET Standard 2.1 versus 2.0 .
Para obter mais informações, consulte a postagem no blog Anunciando o .NET Standard
2.1 .

Novidades no .NET Standard 2.0


O .NET Standard 2.0 inclui estes recursos novos.

Um conjunto muito maior de APIs


Na versão 1.6, o .NET Standard incluía um subconjunto de APIs comparativamente
pequeno. Entre os excluídos estavam várias APIs usadas normalmente no .NET
Framework ou no Xamarin. Isso complica o desenvolvimento, pois exige que os
desenvolvedores encontrem substituições adequadas para APIs conhecidas quando
desenvolvem aplicativos e bibliotecas direcionadas a várias implementações do .NET. O
.NET Standard 2.0 resolve essa limitação adicionando mais de 20.000 APIs que estavam
disponíveis no .NET Standard 1.6, a versão anterior do Standard. Para obter uma lista
com as APIs que foram adicionadas ao .NET Standard 2.0, confira .NET Standard 2.0
versus 1.6 .

Algumas das adições ao namespace System no .NET Standard 2.0 incluem:

Suporte para a classe AppDomain.


Aprimoramento do suporte para trabalhar com matrizes de membros adicionais na
classe Array.
Aprimoramento do suporte para trabalhar com atributos de membros adicionais
na classe Attribute.
Aprimoramento do suporte ao calendário e opções de formatação adicionais para
valores DateTime.
Funcionalidade de arredondamento Decimal adicional.
Funcionalidade adicional na classe Environment.
Aprimoramento do controle sobre o coletor de lixo por meio da classe GC.
Aprimoramento do suporte para comparação, enumeração e normalização de
cadeia de caracteres na classe String.
Suporte para ajustes de horário de verão e tempos de transição nas classes
TimeZoneInfo.AdjustmentRule e TimeZoneInfo.TransitionTime.
Aprimoramento considerável da funcionalidade na classe Type.
Aprimoramento do suporte para desserialização de objetos de exceção com a
adição de um construtor de exceção com os parâmetros SerializationInfo e
StreamingContext.
Suporte a bibliotecas do .NET Framework
Muitas bibliotecas são destinadas ao .NET Framework em vez do .NET Standard. No
entanto, a maioria das chamadas nessas bibliotecas são para APIs incluídas no .NET
Standard 2.0. A partir do .NET Standard 2.0, você pode acessar bibliotecas do .NET
Framework de uma biblioteca do .NET Standard usando uma shim de
compatibilidade . Essa camada de compatibilidade é transparente para os
desenvolvedores; você não precisa fazer nada para tirar proveito das bibliotecas do .NET
Framework.

O único requisito é que as APIs chamadas pela biblioteca de classes .NET Framework
estejam incluídas no .NET Standard 2.0.

Suporte para Visual Basic


Agora, você pode desenvolver bibliotecas .NET Standard no Visual Basic. O Visual Studio
2019 e o Visual Studio 2017 versão 15.3 ou posterior com a carga de trabalho do .NET
Core instalada incluem um modelo de biblioteca de classes .NET Standard. Para
desenvolvedores em Visual Basic que usam outros ambientes e ferramentas de
desenvolvimento, use o comando dotnet new para criar um projeto de biblioteca do
.NET Standard. Para saber mais, confira o Suporte a ferramentas para bibliotecas .NET
Standard.

Suporte a ferramentas para bibliotecas .NET Standard


Com o lançamento do .NET Core 2.0 e do .NET Standard 2.0, o Visual Studio 2017 e a CLI
do .NET incluem o suporte a ferramentas para criação de bibliotecas .NET Standard.

Se instalar o Visual Studio com a carga de trabalho desenvolvimento de plataforma


cruzada do .NET Core, você poderá criar um projeto de biblioteca .NET Standard 2.0
usando um modelo de projeto, como mostra a figura a seguir:

C#
Se você estiver usando a CLI do .NET, o seguinte comando dotnet new criará um
projeto de biblioteca de classes direcionado ao .NET Standard 2.0:

CLI do .NET

dotnet new classlib

Confira também
.NET Standard
Apresentando o .NET Standard
Baixar o SDK do .NET
Common Type System
Artigo • 03/01/2024

O Common Type System define como os tipos são declarados, usados e gerenciados no
Common Language Runtime e também é uma parte importante do suporte do tempo
de execução para a integração entre linguagens. O Common Type System executa as
seguintes funções:

Estabelece uma estrutura que ajuda a habilitar integração entre linguagens,


segurança de tipos e execução de código de alto desempenho.

Fornece um modelo orientado a objetos que dá suporte à implementação


completa de muitas linguagens de programação.

Define regras que as linguagens devem seguir, o que ajuda a assegurar que
objetos escritos em linguagens diferentes possam interagir entre si.

Fornece uma biblioteca que contém os tipos de dados primitivos (como Boolean,
Byte, Char, Int32 e UInt64) usados no desenvolvimento de aplicativos.

Tipos no .NET
Todos os tipos no .NET são tipos de valor ou tipos de referência.

Tipos de valor são tipos de dados cujos objetos são representados pelo valor real do
objeto. Se uma instância de um tipo de valor é atribuída a uma variável, essa variável
recebe uma nova cópia do valor.

Tipos de referência são tipos de dados cujos objetos são representados por uma
referência (semelhante a um ponteiro) para o valor real do objeto. Se um tipo de
referência é atribuído a uma variável, essa variável referencia (aponta para) o valor
original. Nenhuma cópia é feita.

O Common Type System no .NET dá suporte às seguintes cinco categorias de tipos:

Classes
Estruturas
Enumerações
Interfaces
Representantes

Classes
Uma classe é um tipo de referência que pode ser derivada diretamente de outra classe e
que é derivada implicitamente de System.Object. A classe define as operações que um
objeto (que é uma instância da classe) pode executar (métodos, eventos ou
propriedades) e os dados que o objeto contém (campos). Embora uma classe
geralmente inclua a definição e a implementação (diferente de interfaces, por exemplo,
que contêm somente a definição sem implementação), ela pode ter um ou mais
membros que não têm implementação.

A tabela a seguir descreve algumas das características que uma classe pode ter. Cada
linguagem que dá suporte ao runtime fornece uma maneira para indicar que uma classe
ou um membro da classe tem uma ou mais dessas características. No entanto, as
linguagens de programação individuais que segmentam o .NET não podem
disponibilizar todas essas características.

ノ Expandir a tabela

Característica Descrição

sealed Especifica que outra classe não pode ser derivada desse tipo.

implementa Indica que a classe usa uma ou mais interfaces, fornecendo implementações
de membros da interface.

abstract Indica que a classe não pode ser instanciada. Para usá-la, você deve derivar
outra classe a partir dela.

herda Indica que as instâncias da classe podem ser usadas em qualquer lugar em
que a classe base for especificada. Uma classe derivada que herda de uma
classe base pode usar a implementação de todos os membros públicos
fornecidos pela classe base ou a classe derivada pode substituir a
implementação dos membros públicos com sua própria implementação.

exportado ou Indica se uma classe está visível fora do assembly em que ela está definida.
não exportado Essa característica só se aplica a classes de nível superior e não a classes
aninhadas.

7 Observação

Uma classe também pode ser aninhada em uma classe ou estrutura pai. Classes
aninhadas também têm características de membro. Para obter mais informações,
consulte Tipos aninhados.

Membros da classe que não tenham implementação são membros abstratos. Uma
classe que tenha um ou mais membros abstratos é ela própria abstrata. Não é possível
criar novas instâncias dessa classe. Algumas linguagens que segmentam o runtime
permitem marcar uma classe como abstrata mesmo que nenhum de seus membros seja
abstrato. É possível usar uma classe abstrata quando você deseja encapsular um
conjunto básico de funcionalidades que as classes derivadas podem herdar ou substituir
quando apropriado. Classes que não são abstratas são chamadas de classes concretas.

Uma classe pode implementar qualquer número de interfaces, mas pode herdar apenas
de uma classe base além de System.Object, de que todas as classes herdam
implicitamente. Todas as classes devem ter pelo menos um construtor, que inicializa
novas instâncias da classe. Se você não definir explicitamente um construtor, a maioria
dos compiladores fornecerá automaticamente um construtor sem parâmetros.

Estruturas
Uma estrutura é um tipo de valor que é derivado implicitamente do System.ValueType
que, por sua vez, é derivado de System.Object. Uma estrutura é útil para representar
valores cujos requisitos de memória sejam pequenos e passar valores como parâmetros
por valor para os métodos que tenham parâmetros fortemente tipados. No .NET
Framework, todos os tipos de dados primitivos (Boolean, Byte, Char, DateTime, Decimal,
Double, Int16, Int32, Int64, SByte, Single, UInt16, UInt32 e UInt64) são definidos como
estruturas.

Assim como as classes, as estruturas definem os dados (os campos da estrutura) e as


operações que podem ser executadas nesses dados (os métodos da estrutura). Isso
significa que você pode chamar métodos em estruturas, incluindo os métodos virtuais
definidos nas classes System.Object e System.ValueType e todos os métodos definidos
no próprio tipo de valor. Em outras palavras, as estruturas podem ter campos,
propriedades e eventos, bem como métodos estáticos e não estáticos. É possível criar
instâncias de estruturas, passá-las como parâmetros, armazená-las como variáveis locais
ou armazená-las em um campo de outro tipo de valor ou tipo de referência. As
estruturas também podem implementar interfaces.

Tipos de valor também são diferentes das classes em vários pontos. Primeiro, embora
herdem implicitamente do System.ValueType, eles não podem herdar diretamente de
qualquer tipo. Da mesma forma, todos os tipos de valor são lacrados, o que significa
que nenhum outro tipo pode ser derivado deles. Eles também não exigem construtores.

Para cada tipo de valor, o Common Language Runtime fornece um tipo disponível
demarcado correspondente, que é uma classe com o mesmo estado e comportamento
que o tipo de valor. Uma instância de um tipo de valor é demarcada quando é passada
para um método que aceita um parâmetro de tipo System.Object. Ele é não demarcado
(ou seja, convertido de uma instância de uma classe de volta para uma instância de um
tipo de valor) quando o controle retorna de uma chamada de método que aceita um
tipo de valor como um parâmetro por referência. Algumas linguagens exigem que você
use sintaxe especial quando o tipo demarcado é necessário. Outras usam
automaticamente o tipo demarcado quando necessário. Ao definir um tipo de valor,
você está definindo o tipo demarcado e não demarcado.

Enumerações
Uma enumeração é um tipo de valor herdado diretamente de System.Enum e que
fornece nomes alternativos para valores de um tipo primitivo subjacente. Um tipo de
enumeração tem um nome, um tipo subjacente que deve ser um dos tipos inteiros com
ou sem sinal internos (como Byte, Int32 ou UInt64) e um conjunto de campos. Os
campos são campos literais estáticos, cada um deles representa uma constante. O
mesmo valor pode ser atribuído a vários campos. Quando isso ocorre, você deve marcar
um dos valores como o valor de enumeração primário para reflexão e conversão da
cadeia de caracteres.

Você pode atribuir um valor de tipo subjacente a uma enumeração e vice-versa


(nenhuma conversão é exigida pelo runtime). É possível criar uma instância de uma
enumeração e chamar os métodos de System.Enum, assim como qualquer método
definido no tipo subjacente da enumeração. No entanto, algumas linguagens talvez não
deixem passar uma enumeração como um parâmetro quando uma instância do tipo
subjacente é necessária (ou vice-versa).

As seguintes restrições adicionais se aplicam a enumerações:

Elas não podem definir seus próprios métodos.

Elas não podem implementar interfaces.

Elas não podem definir propriedades ou eventos.

Elas não podem ser genéricas, a menos que sejam genéricas apenas por estarem
aninhadas dentro de um tipo genérico. Ou seja, uma enumeração não pode ter
parâmetros de tipo próprios.

7 Observação

Tipos aninhados (incluindo enumerações) criados com o Visual Basic, C# e


C++ incluem os parâmetros de tipo de todos os tipos genéricos e, portanto,
serão genéricos mesmo se não tiverem parâmetros de tipo próprios. Para
obter mais informações, consulte "Tipos Aninhados" no tópico de referência
Type.MakeGenericType.
O atributo FlagsAttribute denota um tipo especial de enumeração chamado campo de
bits. O próprio runtime não faz distinção entre enumerações tradicionais e campos de
bits, mas a linguagem pode fazer isso. Quando é feita essa distinção, operadores bit a
bit podem ser usados em campos de bits, mas não em enumerações, para gerar valores
sem nome. Enumerações geralmente são usadas para listas de elementos exclusivos,
como dias da semana, país ou nomes de região etc. Os campos de bits são geralmente
usados para listas de qualidades ou quantidades que possam ocorrer em combinação,
como Red And Big And Fast .

O exemplo a seguir mostra como usar campos de bit e enumerações tradicionais.

C#

using System;
using System.Collections.Generic;

// A traditional enumeration of some root vegetables.


public enum SomeRootVegetables
{
HorseRadish,
Radish,
Turnip
}

// A bit field or flag enumeration of harvesting seasons.


[Flags]
public enum Seasons
{
None = 0,
Summer = 1,
Autumn = 2,
Winter = 4,
Spring = 8,
All = Summer | Autumn | Winter | Spring
}

public class Example


{
public static void Main()
{
// Hash table of when vegetables are available.
Dictionary<SomeRootVegetables, Seasons> AvailableIn = new
Dictionary<SomeRootVegetables, Seasons>();

AvailableIn[SomeRootVegetables.HorseRadish] = Seasons.All;
AvailableIn[SomeRootVegetables.Radish] = Seasons.Spring;
AvailableIn[SomeRootVegetables.Turnip] = Seasons.Spring |
Seasons.Autumn;

// Array of the seasons, using the enumeration.


Seasons[] theSeasons = new Seasons[] { Seasons.Summer,
Seasons.Autumn,
Seasons.Winter, Seasons.Spring };

// Print information of what vegetables are available each season.


foreach (Seasons season in theSeasons)
{
Console.Write(String.Format(
"The following root vegetables are harvested in {0}:\n",
season.ToString("G")));
foreach (KeyValuePair<SomeRootVegetables, Seasons> item in
AvailableIn)
{
// A bitwise comparison.
if (((Seasons)item.Value & season) > 0)
Console.Write(String.Format(" {0:G}\n",
(SomeRootVegetables)item.Key));
}
}
}
}
// The example displays the following output:
// The following root vegetables are harvested in Summer:
// HorseRadish
// The following root vegetables are harvested in Autumn:
// Turnip
// HorseRadish
// The following root vegetables are harvested in Winter:
// HorseRadish
// The following root vegetables are harvested in Spring:
// Turnip
// Radish
// HorseRadish

Interfaces
Uma interface define um contrato que especifica um relacionamento "possível" ou um
relacionamento de "propriedade". Interfaces são geralmente usadas para implementar a
funcionalidade, como comparação e classificação (interfaces IComparable e
IComparable<T>), testes de igualdade (a interface IEquatable<T>), ou a enumeração de
itens em uma coleção (as interfaces IEnumerable e IEnumerable<T>). Interfaces podem
ter propriedades, métodos, eventos e todos os que são membros abstratos; ou seja,
embora a interface defina os membros e suas assinaturas, ela deixa para o tipo que
implementa a interface para definir a funcionalidade de cada membro da interface. Isso
significa que qualquer classe ou estrutura que implementa uma interface deve fornecer
definições para os membros abstratos declarados na interface. Uma interface pode
exigir qualquer classe de implementação ou estrutura para também implementar uma
ou mais outras interfaces.
As restrições a seguir se aplicam a interfaces:

Uma interface pode ser declarada com qualquer acessibilidade, mas membros da
interface devem ter acessibilidade pública.
Interfaces não podem definir construtores.
Interfaces não podem definir campos.
Interfaces podem definir apenas membros de instância. Elas não podem definir
membros estáticos.

Cada linguagem deve fornecer regras para mapear uma implementação para a interface
que exija o membro, porque mais de uma interface pode declarar um membro com a
mesma assinatura e esses membros podem ter implementações separadas.

Delegados
Os delegados são tipos de referência que têm um propósito semelhante aos de
ponteiros de função no C++. Eles são usados para manipuladores de eventos e funções
de retorno de chamada no .NET. Diferentemente de ponteiros de função, delegados são
seguros, verificáveis e fortemente tipados. Um tipo de delegado pode representar
qualquer método de instância ou método estático que tenha uma assinatura
compatível.

Um parâmetro de um delegado será compatível com o parâmetro correspondente de


um método se o tipo do parâmetro de delegado for mais restritivo do que o tipo do
parâmetro de método, porque isso garante que um argumento passado para o
delegado possa ser passado com segurança para o método.

Da mesma forma, o tipo de retorno de um delegado será compatível com o tipo de


retorno de um método se o tipo de retorno do método for mais restritivo do que o tipo
de retorno do delegado, porque isso garante que o valor retornado do método possa
ser convertido com segurança para o tipo retorno do delegado.

Por exemplo, um representante que tenha um parâmetro de tipo IEnumerable e um tipo


de retorno de Object pode representar um método que tenha um parâmetro de tipo
Object e um valor de retorno de tipo IEnumerable. Para obter mais informações e um
código de exemplo, consulte Delegate.CreateDelegate(Type, Object, MethodInfo).

Diz-se que um delegado está associado ao método que representa. Além de estar
associado ao método, um delegado pode ser associado a um objeto. O objeto
representa o primeiro parâmetro do método e é passado para o método sempre que o
delegado é invocado. Se o método for um método de instância, o objeto associado será
passado como parâmetro this implícito ( Me no Visual Basic). Se o método for estático,
o objeto será passado como o primeiro parâmetro formal do método e a assinatura do
delegado deverá corresponder aos parâmetros restantes. Para obter mais informações e
um código de exemplo, consulte System.Delegate.

Todos os representantes herdam de System.MulticastDelegate, que herda de


System.Delegate. As linguagens C#, Visual Basic e C++ e não permitem a herança
desses tipos. Em vez disso, elas fornecem palavras-chave para declarar delegados.

Como representantes herdam de MulticastDelegate, um representante tem uma lista de


invocação, que é uma lista dos métodos que o representante representa e que são
executados quando o representante é invocado. Todos os métodos na lista recebem os
argumentos fornecidos quando o delegado é invocado.

7 Observação

O valor retornado não é definido para um delegado que tenha mais de um método
na lista de invocação, mesmo que o delegado tenha um tipo de retorno.

Em muitos casos, como acontece com métodos de retorno de chamada, um delegado


representa apenas um método e as únicas ações que você precisa realizar são criar e
invocar o delegado.

Para representantes que representam vários métodos, o .NET fornece métodos das
classes de representante Delegate e MulticastDelegate para dar suporte a operações
como adicionar um método à lista de invocação de um representante (o método
Delegate.Combine), remover um método (o método Delegate.Remove) e obter a lista de
invocação (o método Delegate.GetInvocationList).

7 Observação

Não é necessário usar esses métodos para representantes de manipuladores de


eventos em C#, C++ e Visual Basic, porque essas linguagens fornecem sintaxe para
adicionar e remover manipuladores de eventos.

Definições de tipo
Uma definição de tipo inclui o seguinte:

Qualquer atributo definido no tipo.


A acessibilidade do tipo (visibilidade).
O nome do tipo.
O tipo de base do tipo.
Qualquer interface implementada pelo tipo.
Definições para cada um dos membros do tipo.

Atributos
Atributos fornecem metadados adicionais definidos pelo usuário. Com frequência, eles
são usados para armazenar informações adicionais sobre um tipo em seu assembly ou
para modificar o comportamento de um membro de tipo no ambiente do tempo de
design ou do tempo de execução.

Os atributos são as próprias classe herdadas de System.Attribute. Linguagens que dão


suporte ao uso de atributos têm sua própria sintaxe para aplicar atributos a um
elemento de linguagem. Os atributos podem ser aplicados a praticamente qualquer
elemento de linguagem; os elementos específicos para os quais um atributo pode ser
aplicado são definidos pelo AttributeUsageAttribute aplicado à classe de atributo.

Acessibilidade de tipo
Todos os tipos têm um modificador que rege sua acessibilidade de outros tipos. A
tabela a seguir descreve as acessibilidades de tipo que o runtime dá suporte.

ノ Expandir a tabela

Acessibilidade Descrição

públicos O tipo é acessível por todos os assemblies.

assembly O tipo é acessível somente dentro do assembly.

A acessibilidade de um tipo aninhado depende do domínio de acessibilidade, que é


determinado pela acessibilidade declarada do membro e pelo domínio da acessibilidade
do tipo imediatamente contido. Entretanto, o domínio de acessibilidade de um tipo
aninhado não pode exceder o do tipo contido.

O domínio de acessibilidade de um membro aninhado M declarado em um tipo T em


um programa P é definido da seguinte forma (observe que M pode ser um tipo):

Se a acessibilidade declarada de M for public , o domínio de acessibilidade de M


será o domínio de acessibilidade de T .

Se a acessibilidade declarada de M for protected internal , o domínio de


acessibilidade de M será a interseção do domínio de acessibilidade de T com o
texto de programa de P e o texto de programa de qualquer tipo derivado de T
declarado fora de P .

Se a acessibilidade declarada de M for protected , o domínio de acessibilidade de


M será a interseção do domínio de acessibilidade de T com o texto do programa

de T e qualquer tipo derivado de T .

Se a acessibilidade declarada de M for internal , o domínio de acessibilidade de M


será a interseção do domínio de acessibilidade de T com o texto de programa de
P.

Se a acessibilidade declarada de M for private , o domínio de acessibilidade de M


será o texto de programa de T .

Nomes de tipo
O Common Type System impõe apenas duas restrições de nomes:

Todos os nomes são codificados como cadeias de caracteres Unicode (16 bits).
Não são permitidos nomes que tenham um valor (16 bits) inserido de 0x0000.

No entanto, a maioria das linguagens impõe restrições adicionais em nomes de tipo.


Todas as comparações são feitas em uma base byte por byte e, assim, diferenciam
maiúsculas de minúsculas e são independentes de localidade.

Embora um tipo possa referenciar tipos de outros módulos e assemblies, um tipo deve
ser totalmente definido em um módulo do .NET. (No entanto, dependendo do suporte
do compilador, ele pode ser dividido em vários arquivos de código-fonte.) Os nomes de
tipo só precisam ser exclusivos em um namespace. Para identificar totalmente um tipo,
o nome de tipo deve ser qualificado pelo namespace que contém a implementação do
tipo.

Tipos de base e interfaces


Um tipo pode herdar valores e comportamentos de outro tipo. O Common Type System
não permite que tipos sejam herdados de mais de um tipo de base.

Um tipo pode implementar um número qualquer de interfaces. Para implementar uma


interface, um tipo deve implementar todos os membros virtuais dessa interface. Um
método virtual pode ser implementado por um tipo derivado e pode ser invocado
estática ou dinamicamente.
Membros de tipos
O runtime permite que você defina os membros do tipo, o que especifica o
comportamento e o estado de um tipo. Os membros de tipo incluem o seguinte:

Fields
Propriedades
Métodos
Construtores
Eventos
Tipos aninhados

Campos
Um campo descreve e contém parte do estado do tipo. Campos podem ser de qualquer
tipo com suporte pelo runtime. Geralmente, os campos são private ou protected , de
forma que são acessíveis somente de dentro da classe ou de uma classe derivada. Se o
valor de um campo puder ser modificado fora de seu tipo, um acessador do conjunto
de propriedades normalmente será usado. Os campos expostos publicamente
geralmente são somente leitura e podem ser de dois tipos:

Constantes, cujo valor é atribuído no tempo de design. Esses são membros


estáticos de uma classe, embora eles não sejam definidos usando a palavra-chave
static ( Shared no Visual Basic).

Variáveis somente leitura, cujos valores podem ser atribuídos no construtor da


classe.

O exemplo a seguir ilustra esses dois usos de campos somente leitura.

C#

using System;

public class Constants


{
public const double Pi = 3.1416;
public readonly DateTime BirthDate;

public Constants(DateTime birthDate)


{
this.BirthDate = birthDate;
}
}

public class Example


{
public static void Main()
{
Constants con = new Constants(new DateTime(1974, 8, 18));
Console.Write(Constants.Pi + "\n");
Console.Write(con.BirthDate.ToString("d") + "\n");
}
}
// The example displays the following output if run on a system whose
current
// culture is en-US:
// 3.1416
// 8/18/1974

Propriedades
Uma propriedade nomeia um valor ou um estado do tipo e define métodos para obter
ou definir o valor da propriedade. Propriedades podem ser tipos primitivos, coleções de
tipos primitivos, tipos definidos pelo usuário ou coleções de tipos definidos pelo
usuário. Propriedades são, frequentemente, usadas para manter a interface pública de
um tipo independente da representação real do tipo. Isso permite que as propriedades
reflitam os valores que não estão armazenados diretamente na classe (por exemplo,
quando uma propriedade retorna um valor computado) ou para realizar uma validação
antes de os valores serem atribuídos a campos privados. O exemplo a seguir ilustra o
padrão mais recente.

C#

using System;

public class Person


{
private int m_Age;

public int Age


{
get { return m_Age; }
set {
if (value < 0 || value > 125)
{
throw new ArgumentOutOfRangeException("The value of the Age
property must be between 0 and 125.");
}
else
{
m_Age = value;
}
}
}
}

Além de incluir a própria propriedade, a MSIL (Microsoft Intermediate Language) para


um tipo que contém uma propriedade legível inclui um método get_ propertyname e a
MSIL para um tipo que contém uma propriedade gravável inclui um método
set_ propertyname.

Métodos
Um método descreve as operações disponíveis no tipo. A assinatura do método
especifica os tipos permitidos de todos seus parâmetros e o valor retornado.

Embora a maioria dos métodos defina o número exato de parâmetros necessários para
chamadas de método, alguns métodos dão suporte a um número variável de
parâmetros. O parâmetro final declarado desses métodos é marcado com o atributo
ParamArrayAttribute. Compiladores de linguagem normalmente fornecem uma palavra-
chave, como params no C# e ParamArray no Visual Basic, que tornam o uso explícito de
ParamArrayAttribute desnecessário.

Construtores
Um construtor é um tipo especial de método que cria novas instâncias de uma classe ou
estrutura. Assim como qualquer outro método, um construtor pode incluir parâmetros.
No entanto, os construtores não têm nenhum valor retornado (ou seja, eles retornam
void ).

Se o código-fonte para uma classe não definir explicitamente um construtor, o


compilador incluirá um construtor sem parâmetros. No entanto, se o código-fonte de
uma classe definir apenas construtores com parâmetros, os compiladores do Visual
Basic e do C# não gerarão um construtor sem parâmetros.

Se o código-fonte de uma estrutura definir construtores, eles deverão ser


parametrizados. Uma estrutura não pode definir um construtor sem parâmetros e os
compiladores não geram construtores sem parâmetros para estruturas ou outros tipos
de valor. Todos os tipos de valor têm um construtor sem parâmetros implícito. Esse
construtor é implementado pelo Common Language Runtime e inicializa todos os
campos da estrutura com seus valores padrão.

Eventos
Um evento define um incidente que pode ser respondido e define métodos para
assinar, cancelar a assinatura e acionar o evento. Eventos são frequentemente usados
para informar outros tipos de alterações de estado. Para obter mais informações,
consulte Eventos.

Tipos aninhados
Um tipo aninhado é um tipo membro de outros tipos. Os tipos aninhados devem ser
unidos ao tipo de contenção e não devem ser utilizados como tipos de uso geral. Os
tipos aninhados são úteis quando o tipo declarativo usa e cria instâncias do tipo
aninhado e o uso do tipo aninhado não é exposto em membros públicos.

Os tipos aninhados são confusos para alguns desenvolvedores e não devem ficar
publicamente visíveis, a menos que haja um motivo forte para a visibilidade. Em uma
biblioteca bem projetada, os desenvolvedores raramente precisam usar tipos aninhados
para instanciar objetos ou declarar variáveis.

Características de membros de tipo


O Common Type System permite que os membros de tipo tenham várias características.
No entanto, as linguagens não necessariamente dão suporte a todas elas. A tabela a
seguir descreve as características de um membro.

ノ Expandir a tabela

Característica Pode ser Descrição


aplicado a

abstract Métodos, O tipo não fornece a implementação do método. Tipos


propriedades e que herdam ou implementam métodos abstratos devem
eventos fornecer uma implementação para o método. A única
exceção é quando o tipo derivado é um tipo abstrato.
Todos os métodos abstratos são virtuais.

privado, família, Tudo Define a acessibilidade de um membro:


assembly, família e
assembly, família ou particulares
assembly ou Acessível somente dentro do mesmo tipo que o
público membro ou de um tipo aninhado.

família
Acessível dentro do mesmo tipo que o membro e de
tipos derivados herdados dele.

assembly
Característica Pode ser Descrição
aplicado a

Acessível somente no assembly no qual o tipo é


definido.

família e assembly
Acessíveis somente em tipos qualificados para acesso de
família e assembly.

família ou assembly
Acessíveis somente dentro de tipos qualificados para
acesso de família ou assembly.

públicos
Acessíveis dentro de qualquer tipo.

final Métodos, Um método virtual não pode ser substituído em um tipo


propriedades e derivado.
eventos

initialize-only Campos O valor pode apenas ser inicializado e não pode ser
gravado após a inicialização.

instance Campos, Se um membro não estiver marcado como static (C# e


métodos, C++), Shared (Visual Basic), virtual (C# e C++) ou
propriedades e Overridable (Visual Basic), ele será um membro de
eventos instância (não há palavra-chave de instância). Haverá
tantas cópias desses membros na memória quanto
objetos que as usam.

literal Campos O valor atribuído ao campo é um valor fixo, conhecido


no tempo de compilação, de um tipo de valor interno.
Às vezes, campos literais são conhecidos como
constantes.

newslot ou override Tudo Define como o membro interage com os membros


herdados que possuam a mesma assinatura:

newslot
Oculta os membros herdados que possuam a mesma
assinatura.

override
Substitui a definição de um método virtual herdado.

O padrão é newslot.

static Campos, O membro pertence ao tipo no qual está definido e não


métodos, a uma instância particular do tipo; o membro existirá
Característica Pode ser Descrição
aplicado a

propriedades e mesmo se uma instância do tipo não tiver sido criada e


eventos será compartilhado entre todas as instâncias do tipo.

virtual Métodos, O método pode ser implementado por um tipo


propriedades e derivado e pode ser invocado estática ou
eventos dinamicamente. Se a invocação dinâmica for usada, o
tipo da instância que faz a chamada no tempo de
execução (em vez do tipo conhecido no tempo de
compilação) determinará qual implementação do
método será chamada. Para invocar um método virtual
estaticamente, a variável precisará ser convertida em um
tipo que usa a versão desejada do método.

Sobrecarga
Cada membro de tipo tem uma assinatura exclusiva. Assinaturas de método consistem
no nome de método e em uma lista de parâmetros (a ordem e os tipos dos argumentos
do método). Os vários métodos com o mesmo nome podem ser definidos em um tipo
desde que suas assinaturas sejam diferentes. Quando dois ou mais métodos com o
mesmo nome forem definidos, diz-se que o método está sobrecarregado. Por exemplo,
em System.Char, o método IsDigit está sobrecarregado. Um método utiliza um Char. O
outro método utiliza um String e um Int32.

7 Observação

O tipo de retorno não é considerado parte da assinatura do método. Ou seja, os


métodos não poderão ser sobrecarregados se diferirem somente pelo tipo de
retorno.

Herdar, substituir e ocultar membros


Um tipo derivado herda todos os membros de seu tipo de base; ou seja, esses membros
são definidos e disponibilizados para o tipo derivado. O comportamento, ou qualidades,
de membros herdados pode ser modificado de duas maneiras:

Um tipo derivado pode ocultar um membro herdado definindo um novo membro


com a mesma assinatura. Isso pode ser feito para fazer um membro privado
anteriormente público ou para definir o novo comportamento de um método
herdado marcado como final .
Um tipo derivado pode substituir um método virtual herdado. O método de
substituição fornece uma nova definição do método que será invocado com base
no tipo do valor no tempo de execução em vez do tipo de variável conhecido no
tempo de compilação. Um método poderá substituir um método virtual somente
se o método virtual não estiver marcado como final e o novo método for tão
acessível quanto o método virtual.

Confira também
Navegador de API .NET
Common Language Runtime
Conversão de tipo no .NET

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Componentes de independência de
linguagem e componentes
independentes da linguagem
Artigo • 06/04/2023

O .NET é independente de linguagem. Isso significa que, como desenvolvedor, você


pode desenvolver em uma das muitas linguagens direcionadas às implementações do
.NET, como C#, F# e Visual Basic. É possível acessar tipos e membros de bibliotecas de
classes desenvolvidas para implementações do .NET sem que seja necessário conhecer a
linguagem em que foram originalmente escritas e sem precisar seguir as convenções da
linguagem original. Se você for um desenvolvedor de componentes, o componente
poderá ser acessado por qualquer aplicativo .NET, independentemente da linguagem.

7 Observação

A primeira parte deste artigo discorre sobre a criação de componentes


independentes de linguagem, ou seja, componentes que podem ser consumidos
por aplicativos escritos em qualquer linguagem. Você também pode criar um único
componente ou aplicativo de código-fonte gravado em várias linguagens; consulte
Interoperabilidade em qualquer idioma na segunda parte deste artigo.

Para interagir completamente com outros objetos gravados em qualquer linguagem, os


objetos devem expor aos chamadores somente os recursos comuns a todas as
linguagens. Esse conjunto comum de recursos é definido pela CLS (Common Language
Specification), que é um conjunto de regras que se aplicam aos assemblies gerados. A
Common Language Specification é definida na Partição I, cláusulas 7 a 11 do Padrão
ECMA-335: Common Language Infrastructure .

Se o componente estiver de acordo com a Common Language Specification, ele será


compatível com a CLS e poderá ser acessado pelo código em assemblies gravados em
qualquer linguagem de programação que dê suporte a CLS. É possível determinar se o
componente está de acordo com a Common Language Specification no tempo de
compilação aplicando o atributo CLSCompliantAttribute ao código-fonte. Para obter
mais informações, consulte O atributo CLSCompliantAttribute.

Regras de conformidade com CLS


Esta seção discute as regras para criar um componente compatível com CLS. Para obter
uma lista completa de regras, consulte Partição I, Cláusula 11 do Padrão ECMA-335:
Common Language Infrastructure .

7 Observação

A Common Language Specification aborda cada regra de conformidade com CLS à


medida que se aplica a consumidores (desenvolvedores que estão acessando
programaticamente um componente em conformidade com CLS), estruturas
(desenvolvedores que estão usando um compilador de linguagem para criar
bibliotecas em conformidade com CLS) e extensores (desenvolvedores que estão
criando uma ferramenta, como um compilador de linguagem ou um analisador de
código que cria componentes em conformidade com CLS). Este artigo enfoca as
regras que se aplicam às estruturas. Entretanto, algumas das regras que se aplicam
a extensores também podem ser aplicadas a assemblies criados usando
Reflection.Emit.

Para criar um componente independente de linguagem, você só precisa aplicar as regras


de conformidade com CLS à interface pública do componente. A implementação
privada não precisa estar de acordo com a especificação.

) Importante

As regras de conformidade com CLS só se aplicam à interface pública de um


componente e não à implementação privada.

Por exemplo, inteiros sem sinal que não sejam Byte não são compatíveis com CLS.
Como a classe Person no exemplo a seguir expõe uma propriedade Age de tipo UInt16,
o código a seguir exibe um aviso do compilador.

C#

using System;

[assembly: CLSCompliant(true)]

public class Person


{
private UInt16 personAge = 0;

public UInt16 Age


{ get { return personAge; } }
}
// The attempt to compile the example displays the following compiler
warning:
// Public1.cs(10,18): warning CS3003: Type of 'Person.Age' is not CLS-
compliant

É possível tornar a classe Person em conformidade com CLS alternado o tipo de


propriedade Age de UInt16 para Int16, que é um inteiro com sinal 16 bits em
conformidade com CLS. Não é necessário alterar o tipo do campo personAge privado.

C#

using System;

[assembly: CLSCompliant(true)]

public class Person


{
private Int16 personAge = 0;

public Int16 Age


{ get { return personAge; } }
}

A interface pública de uma biblioteca consiste no seguinte:

Definições de classes públicas.

Definições dos membros públicos de classes públicas e definições de membros


acessíveis para classes derivadas (ou seja, membros protegidos).

Parâmetros e tipos de retorno de métodos públicos de classes públicas e


parâmetros e tipos de retorno de métodos acessíveis para classes derivadas.

As regras de conformidade com CLS estão listadas na tabela a seguir. O texto das regras
é tirado literalmente do Padrão ECMA-335: Common Language Infrastructure , com
direitos autorais de 2012 da Ecma International. Informações mais detalhadas sobre
essas regras são encontradas nas seções a seguir.

Categoria Consulte Regra Número


da regra

Acessibilidade Acessibilidade A acessibilidade não deverá ser alterada ao substituir 10


de membro métodos herdados, exceto na substituição de um
método herdado de um assembly diferente com
acessibilidade family-or-assembly . Nesse caso, a
substituição deverá ter a acessibilidade family .
Categoria Consulte Regra Número
da regra

Acessibilidade Acessibilidade A visibilidade e a acessibilidade de tipos e membros 12


de membro deverão ser de tal forma que os tipos na assinatura
de qualquer membro sejam visíveis e acessíveis
sempre que o próprio membro estiver visível e
acessível. Por exemplo, um método público visível
fora do assembly não deve ter um argumento cujo
tipo seja visível somente dentro do assembly. A
visibilidade e a acessibilidade dos tipos que
compõem um tipo genérico instanciado usado na
assinatura de qualquer membro deverão estar
visíveis e acessíveis sempre que o próprio membro
estiver visível e acessível. Por exemplo, um tipo
genérico instanciado presente na assinatura de um
membro visível fora do assembly não deverá ter um
argumento genérico cujo tipo seja visível somente
dentro do assembly.

Matrizes matrizes As matrizes deverão ter elementos com um tipo 16


compatível com CLS e todas as dimensões da matriz
deverão ter limites inferiores iguais a zero. Se o item
for uma matriz, o tipo do elemento da matriz será
necessário para diferenciar as sobrecargas. Quando
a sobrecarga é baseada em dois ou mais tipos de
matriz, os tipos de elemento deverão ser chamados
de tipos.

Atributos Atributos Os atributos deverão ser do tipo System.Attribute ou 41


de um tipo que o herde.

Atributos Atributos A CLS só permite um subconjunto das codificações 34


de atributos personalizados. Os únicos tipos que
deverão ser exibidos nessas codificações são
(consulte a Partição IV): System.Type, System.String,
System.Char, System.Boolean, System.Byte,
System.Int16, System.Int32, System.Int64,
System.Single, System.Double e qualquer tipo de
enumeração baseado em um tipo inteiro de base
compatível com CLS.

Atributos Atributos A CLS não permite modificadores obrigatórios 35


visíveis publicamente ( modreq , consulte a Partição II),
mas permite modificadores opcionais ( modopt ,
consulte a Partição II) que ela não entende.
Categoria Consulte Regra Número
da regra

Construtores Construtores Um construtor de objeto deverá chamar um 21


construtor de instância de sua classe base antes de
qualquer acesso aos dados da instância herdados.
(Isso não se aplica a tipos de valor, que não
precisam ter construtores.)

Construtores Construtores Um construtor de objeto não deverá ser chamado, 22


exceto como parte da criação de um objeto e um
objeto não deve ser inicializado duas vezes.

Enumerações Enumerações O tipo subjacente de um enum deverá ser um tipo 7


de inteiro CLS interno, o nome do campo deverá ser
"value__", e esse campo deverá ser marcado como
RTSpecialName .

Enumerações Enumerações Há dois tipos diferentes de enums, indicados pela 8


presença ou pela ausência do atributo personalizado
System.FlagsAttribute (consulte a Biblioteca da
Partição IV). Um representa valores de inteiro
nomeados; o outro representa sinalizadores de bit
nomeados que podem ser combinados para gerar
um valor sem nome. O valor de um enum não está
limitado aos valores especificados.

Enumerações Enumerações Campos estáticos de literais de um enum deverão 9


ter o tipo do próprio enum.

Eventos Eventos Os métodos que implementam um evento deverão 29


ser marcados como SpecialName nos metadados.

Eventos Eventos A acessibilidade de um evento e de seus 30


acessadores deverá ser idêntica.

Eventos Eventos Os métodos add e remove de um evento deverão 31


estar presentes ou ausentes.

Eventos Eventos Os métodos add e remove de um evento deverão 32


utilizar um parâmetro cada um, cujo tipo defina o
tipo do evento e ele deverá ser derivado de
System.Delegate.

Eventos Eventos Os eventos deverão respeitar um padrão de 33


nomenclatura específico. O atributo SpecialName
mencionado na regra 29 da CLS deverá ser ignorado
em comparações de nome apropriadas e respeitar as
regras do identificador.
Categoria Consulte Regra Número
da regra

Exceções Exceções Os atributos acionados deverão ser do tipo 40


System.Exception ou de um tipo herdado dele.
Mesmo assim, os métodos compatíveis com CLS não
precisam bloquear a propagação de outros tipos de
exceção.

Geral Regras de As regras CLS só se aplicam a essas partes de um 1


conformidade tipo acessíveis ou visíveis fora do assembly de
com CLS definição.

Geral Regras de Membros de tipos sem conformidade com CLS não 2


conformidade deverão ser marcados como em conformidade com
com CLS CLS.

Genéricos Tipos e Os tipos aninhados deverão ter, pelo menos, tantos 42


membros parâmetros genéricos quanto o tipo delimitador. Os
genéricos parâmetros genéricos em um tipo aninhado
correspondem, por posição, aos parâmetros
genéricos no tipo delimitador.

Genéricos Tipos e O nome de um tipo genérico deverá codificar o 43


membros número de parâmetros de tipo declarados no tipo
genéricos não aninhado ou recém-introduzidos no tipo, se
aninhado, de acordo com as regras definidas
anteriormente.

Genéricos Tipos e Um tipo genérico deverá redeclarar restrições 44


membros suficientes para assegurar que todas as restrições no
genéricos tipo base ou nas interfaces sejam atendidas pelas
restrições de tipo genérico.

Genéricos Tipos e Tipos usados como restrições em parâmetros 45


membros genéricos deverão ser compatíveis com CLS.
genéricos

Genéricos Tipos e A visibilidade e a acessibilidade de membros 46


membros (incluindo tipos aninhados) em um tipo genérico
genéricos instanciado deverão ser consideradas no escopo da
instanciação específica, em vez da declaração de
tipo genérico como um todo. Supondo isso, as
regras de visibilidade e acessibilidade da regra 12 da
CLS continuam sendo aplicáveis.

Genéricos Tipos e Para cada método genérico abstrato ou virtual, 47


membros deverá haver uma implementação concreta (não
genéricos abstrata) padrão
Categoria Consulte Regra Número
da regra

Interfaces Interfaces As interfaces em conformidade com CLS não 18


deverão exigir a definição de métodos incompatíveis
com CLS para implementá-los.

Interfaces Interfaces As interfaces compatíveis com CLS não deverão 19


definir métodos estáticos, nem devem definir
campos.

Membros Membros de Campos e métodos estáticos globais não são 36


tipo em geral compatíveis com CLS.

Membros -- O valor de um estático literal é especificado usando 13


metadados de inicialização do campo. Um literal
compatível com CLS deve ter um valor especificado
em metadados de inicialização de campo que sejam
exatamente do mesmo tipo que o literal (ou do tipo
subjacente, se esse literal for um enum ).

Membros Membros de A restrição vararg não faz parte da CLS e a única 15


tipo em geral convenção de chamada com suporte pela CLS é a
convenção de chamada gerenciada padrão.

Convenções Convenções Os assemblies deverão seguir o Anexo 7 do 4


de de Relatório Técnico 15 do Padrão Unicode 3.0 que
nomenclatura nomenclatura controla o conjunto de caracteres permitidos para
iniciar e serem incluídos em identificadores,
disponíveis online em Formulários de Normalização
de Unicode . Os identificadores deverão estar no
formato canônico definido pelo Formulário C de
Normalização de Unicode. Para fins de CLS, dois
identificadores serão iguais se os mapeamentos em
minúsculas (conforme especificado pelos
mapeamentos em minúsculas um para um,
insensíveis a localidade Unicode) forem os mesmos.
Ou seja, para dois identificadores serem
considerados diferentes na CLS, eles deverão ser
diferentes além de apenas maiúsculas e minúsculas.
No entanto, para substituir uma definição herdada, a
CLI exige que a codificação precisa da declaração
original seja usada.
Categoria Consulte Regra Número
da regra

Sobrecarga Convenções Todos os nomes introduzidos em um escopo 5


de compatível com CLS deverão ser independentes e
nomenclatura distintos do tipo, exceto quando os nomes forem
idênticos e resolvidos por meio da sobrecarga. Ou
seja, embora o CTS permita que um tipo single use o
mesmo nome para um método e um campo, a CLS
não permite.

Sobrecarga Convenções Campos e tipos aninhados deverão ser diferenciados 6


de apenas por comparação de identificador, mesmo
nomenclatura que o CTS permita que assinaturas diferentes sejam
distinguidas. Métodos, propriedades e eventos com
o mesmo nome (por comparação de identificador)
deverão ser diferentes além apenas do tipo de
retorno, exceto conforme especificado na Regra 39
da CLS

Sobrecarga Sobrecargas Somente propriedades e métodos podem ser 37


sobrecarregados.

Sobrecarga Sobrecargas As propriedades e os métodos só podem ser 38


sobrecarregados com base no número e nos tipos
de seus parâmetros, exceto os operadores de
conversão chamados op_Implicit e op_Explicit ,
que também podem ser sobrecarregados com base
no tipo de retorno.

Sobrecarga -- Se dois ou mais métodos em conformidade com CLS 48


declarados em um tipo tiverem o mesmo nome e,
para um conjunto específico de instanciações de
tipo, tiverem os mesmos tipos de parâmetro e
retorno, esses métodos deverão ser semanticamente
equivalentes nessas instanciações de tipo.

Propriedades Propriedades Os métodos que implementam os métodos getter e 24


setter de uma propriedade deverão ser marcados
como SpecialName nos metadados.

Propriedades Propriedades Os acessadores de uma propriedade deverão ser 26


todos estáticos, virtuais ou de instância.
Categoria Consulte Regra Número
da regra

Propriedades Propriedades O tipo de uma propriedade deverá ser o tipo de 27


retorno do getter e o tipo do último argumento do
setter. Os tipos dos parâmetros da propriedade
deverão ser os tipos dos parâmetros do getter e os
tipos de todos os parâmetros, menos o parâmetro
final do setter. Todos esses tipos deverão ser
compatíveis com CLS e não deverão ser ponteiros
gerenciados (ou seja, não deverão ser passados por
referência).

Propriedades Propriedades As propriedades deverão seguir um padrão de 28


nomenclatura específico. O atributo SpecialName
mencionado na regra 24 da CLS deverá ser ignorado
em comparações de nome apropriadas e respeitar as
regras do identificador. Uma propriedade deverá ter
um método getter, um método setter ou ambos.

Conversão de Conversão de Se op_Implicit ou op_Explicit for fornecido, um meio 39


tipos tipos alternativo de coerção deverá ser fornecido.

Tipos Tipos e Tipos de valor demarcado não estão em 3


assinaturas de conformidade com CLS.
membro de
tipo

Tipos Tipos e Todos os tipos exibidos em uma assinatura deverão 11


assinaturas de ser compatíveis com CLS. Todos os tipos que
membro de compõem um tipo genérico instanciado deverão ser
tipo compatíveis com CLS.

Tipos Tipos e Referências com tipo não são compatíveis com CLS. 14
assinaturas de
membro de
tipo

Tipos Tipos e Tipos de ponteiro não gerenciados não são 17


assinaturas de compatíveis com CLS.
membro de
tipo

Tipos Tipos e Classes compatíveis com CLS, tipos de valor e 20


assinaturas de interfaces não deverão exigir a implementação de
membro de membros incompatíveis com CLS
tipo
Categoria Consulte Regra Número
da regra

Tipos Tipos e System. Object é compatível com CLS. Qualquer 23


assinaturas de outra classe compatível com CLS deverá herdar de
membro de uma classe compatível com CLS.
tipo

Indexar para subseções:

Tipos e assinaturas de membro de tipo


Convenções de nomenclatura
Conversão de tipos
matrizes
Interfaces
Enumerações
Membros de tipo em geral
Acessibilidade de membro
Tipos e membros genéricos
Construtores
Propriedades
Eventos
Sobrecargas
Exceções
Atributos

Tipos e assinaturas de membro de tipo


O tipo System.Object está em conformidade com CLS e é o tipo base de todos os tipos
de objeto no sistema de tipos do .NET. A herança no .NET é implícita (por exemplo, a
classe String herda implicitamente da classe Object ) ou explícita (por exemplo, a classe
CultureNotFoundException herda explicitamente da classe ArgumentException, que
herda explicitamente da classe Exception). Para que um tipo derivado esteja em
conformidade com CLS, seu tipo base também deverá estar em conformidade com CLS.

O exemplo a seguir mostra um tipo derivado cujo tipo de base não é compatível com
CLS. Ele define uma classe Counter base que usa um inteiro de 32 bits sem sinal como
um contador. Como a classe fornece funcionalidade de contador encapsulando um
inteiro sem sinal, a classe é marcada como não compatível com CLS. Assim, uma classe
derivada, NonZeroCounter , também não é compatível com CLS.

C#
using System;

[assembly: CLSCompliant(true)]

[CLSCompliant(false)]
public class Counter
{
UInt32 ctr;

public Counter()
{
ctr = 0;
}

protected Counter(UInt32 ctr)


{
this.ctr = ctr;
}

public override string ToString()


{
return String.Format("{0}). ", ctr);
}

public UInt32 Value


{
get { return ctr; }
}

public void Increment()


{
ctr += (uint) 1;
}
}

public class NonZeroCounter : Counter


{
public NonZeroCounter(int startIndex) : this((uint) startIndex)
{
}

private NonZeroCounter(UInt32 startIndex) : base(startIndex)


{
}
}
// Compilation produces a compiler warning like the following:
// Type3.cs(37,14): warning CS3009: 'NonZeroCounter': base type 'Counter'
is not
// CLS-compliant
// Type3.cs(7,14): (Location of symbol related to previous warning)

Todos os tipos exibidos em assinaturas de membro, incluindo um tipo de retorno de


método ou um tipo de propriedade, devem ser compatíveis com CLS. Além disso, para
tipos genéricos:

Todos os tipos que compõe um tipo genérico instanciado devem ser compatíveis
com CLS.

Todos os tipos usados como restrições em parâmetros genéricos devem ser


compatíveis com CLS.

O Common Type System do .NET inclui vários tipos inseridos com suporte diretamente
com o Common Language Runtime e codificados especialmente nos metadados de um
assembly. Desses tipos intrínsecos, os tipos listados na tabela a seguir estão em
conformidade com CLS.

Tipo em conformidade com Descrição


CLS

Byte Inteiro sem sinal de 8 bits

Int16 Inteiro com sinal de 16 bits

Int32 Inteiro com sinal de 32 bits

Int64 Inteiro com sinal de 64 bits

Half Valor do ponto flutuante de meia precisão

Single Valor do ponto flutuante de precisão simples

Double Valor de ponto flutuante de precisão dupla

Booliano tipo de valor verdadeiro ou falso

Char unidade de código codificado UTF-16

Decimal Número decimal de ponto não flutuante

IntPtr Ponteiro ou identificador de um tamanho definido por


plataforma

Cadeia de caracteres Coleção de zero, um ou mais objetos Char

Os tipos intrínsecos listados na tabela a seguir não são compatíveis com CLS.

Tipo não Descrição Alternativa em conformidade com


compatível CLS

SByte Tipo de dados inteiro com sinal de Int16


8 bits

UInt16 Inteiro sem sinal de 16 bits Int32


Tipo não Descrição Alternativa em conformidade com
compatível CLS

UInt32 Inteiro sem sinal de 32 bits Int64

UInt64 Inteiro sem sinal de 64 bits Int64 (pode estourar), BigInteger,


ouDouble

UIntPtr Ponteiro ou identificador sem sinal IntPtr

A biblioteca de classes do .NET ou qualquer outra biblioteca de classes pode incluir


outros tipos que não estejam em conformidade com CLS; por exemplo:

Tipos de valor demarcado. O exemplo do C# a seguir cria uma classe que tem uma
propriedade pública do tipo int* chamada Value . Como um int* é um tipo de
valor demarcado, o compilador o sinaliza como incompatível com CLS.

C#

using System;

[assembly:CLSCompliant(true)]

public unsafe class TestClass


{
private int* val;

public TestClass(int number)


{
val = (int*) number;
}

public int* Value {


get { return val; }
}
}
// The compiler generates the following output when compiling this
example:
// warning CS3003: Type of 'TestClass.Value' is not CLS-
compliant

Referências de tipo, que são constructos especiais que contêm referência a um


objeto e referência a um tipo. As referências tipadas são representadas no .NET
pela classe TypedReference.

Se um tipo não for compatível com CLS, você deverá aplicar o atributo
CLSCompliantAttribute com um valor isCompliant de false a ele. Para obter mais
informações, consulte a seção O atributo CLSCompliantAttribute.
O exemplo a seguir ilustra o problema de conformidade com CLS em uma assinatura do
método e em uma instanciação de tipo genérico. Ele define uma classe InvoiceItem
com uma propriedade do tipo UInt32, uma propriedade do tipo Nullable<UInt32> e um
construtor com parâmetros de tipo UInt32 e Nullable<UInt32> . Você recebe quatro
avisos do compilador ao tentar de compilar esse exemplo.

C#

using System;

[assembly: CLSCompliant(true)]

public class InvoiceItem


{
private uint invId = 0;
private uint itemId = 0;
private Nullable<uint> qty;

public InvoiceItem(uint sku, Nullable<uint> quantity)


{
itemId = sku;
qty = quantity;
}

public Nullable<uint> Quantity


{
get { return qty; }
set { qty = value; }
}

public uint InvoiceId


{
get { return invId; }
set { invId = value; }
}
}
// The attempt to compile the example displays the following output:
// Type1.cs(13,23): warning CS3001: Argument type 'uint' is not CLS-
compliant
// Type1.cs(13,33): warning CS3001: Argument type 'uint?' is not CLS-
compliant
// Type1.cs(19,26): warning CS3003: Type of 'InvoiceItem.Quantity' is not
CLS-compliant
// Type1.cs(25,16): warning CS3003: Type of 'InvoiceItem.InvoiceId' is
not CLS-compliant

Para eliminar os avisos do compilador, substitua os tipos não compatíveis com CLS na
interface pública InvoiceItem por tipos compatíveis:

C#
using System;

[assembly: CLSCompliant(true)]

public class InvoiceItem


{
private uint invId = 0;
private uint itemId = 0;
private Nullable<int> qty;

public InvoiceItem(int sku, Nullable<int> quantity)


{
if (sku <= 0)
throw new ArgumentOutOfRangeException("The item number is zero or
negative.");
itemId = (uint) sku;

qty = quantity;
}

public Nullable<int> Quantity


{
get { return qty; }
set { qty = value; }
}

public int InvoiceId


{
get { return (int) invId; }
set {
if (value <= 0)
throw new ArgumentOutOfRangeException("The invoice number is
zero or negative.");
invId = (uint) value; }
}
}

Além dos tipos específicos listados, algumas categorias de tipos não são compatíveis
com CLS. Entre eles estão tipos de ponteiro não gerenciados e tipos de ponteiro de
função. O exemplo a seguir gera um aviso do compilador porque ele usa um ponteiro
para um inteiro a fim de criar uma matriz de inteiros.

C#

using System;

[assembly: CLSCompliant(true)]

public class ArrayHelper


{
unsafe public static Array CreateInstance(Type type, int* ptr, int items)
{
Array arr = Array.CreateInstance(type, items);
int* addr = ptr;
for (int ctr = 0; ctr < items; ctr++) {
int value = *addr;
arr.SetValue(value, ctr);
addr++;
}
return arr;
}
}
// The attempt to compile this example displays the following output:
// UnmanagedPtr1.cs(8,57): warning CS3001: Argument type 'int*' is not
CLS-compliant

Para classes abstratas compatíveis com CLS (ou seja, classes marcadas como abstract
no C# ou como MustInherit no Visual Basic), todos os membros da classe também
devem ser compatíveis com CLS.

Convenções de nomenclatura
Como algumas linguagens de programação não diferenciam maiúsculas de minúsculas,
os identificadores (como nomes de namespaces, tipos e membros) devem se diferenciar
além de maiúsculas e minúsculas. Dois identificadores serão considerados equivalentes
se seus mapeamentos em minúsculas forem os mesmos. O exemplo do C# a seguir
define duas classes públicas, Person e person . Como elas são diferentes apenas em
maiúsculas e minúsculas, o compilador do C# as sinaliza como não compatíveis com
CLS.

C#

using System;

[assembly: CLSCompliant(true)]

public class Person : person


{
}

public class person


{
}
// Compilation produces a compiler warning like the following:
// Naming1.cs(11,14): warning CS3005: Identifier 'person' differing
// only in case is not CLS-compliant
// Naming1.cs(6,14): (Location of symbol related to previous warning)
Identificadores de linguagem de programação, como nomes de namespaces, tipos e
membros, devem estar em conformidade com o Padrão Unicode . Isso significa que:

O primeiro caractere de um identificador pode ser qualquer letra maiúscula


Unicode, letra minúscula, letra maiúscula do título, letra modificadora, outra letra
ou o número da letra. Para obter informações sobre categorias de caracteres
Unicode, consulte a enumeração System.Globalization.UnicodeCategory.

Os caracteres subsequentes podem ser de qualquer uma das categorias como o


primeiro caractere e também podem incluir marcas sem espaçamento, marcas que
combinam espaçamento, números decimais, pontuações de conector e códigos de
formatação.

Antes de comparar identificadores, você deve filtrar códigos de formatação e converter


os identificadores em Formulário C de Normalização de Unicode, porque um caractere
único pode ser representado por várias unidades de código codificadas em UTF-16. As
sequências de caracteres que produzem as mesmas unidades de código no Formulário
C de Normalização de Unicode não são compatíveis com CLS. O exemplo a seguir
define uma propriedade chamada Å , que consiste no caractere SÍMBOLO DE
ANGSTROM (U+212B) e uma segunda propriedade chamada Å , que consiste no
caractere LETRA LATINA MAIÚSCULA A COM ANEL SUPERIOR (U+00C5). Os
compiladores do C# e do Visual Basic sinalizam o código-fonte como incompatível com
CLS.

C#

public class Size


{
private double a1;
private double a2;

public double Å
{
get { return a1; }
set { a1 = value; }
}

public double Å
{
get { return a2; }
set { a2 = value; }
}
}
// Compilation produces a compiler warning like the following:
// Naming2a.cs(16,18): warning CS3005: Identifier 'Size.Å' differing only
in case is not
// CLS-compliant
// Naming2a.cs(10,18): (Location of symbol related to previous warning)
// Naming2a.cs(18,8): warning CS3005: Identifier 'Size.Å.get' differing
only in case is not
// CLS-compliant
// Naming2a.cs(12,8): (Location of symbol related to previous warning)
// Naming2a.cs(19,8): warning CS3005: Identifier 'Size.Å.set' differing
only in case is not
// CLS-compliant
// Naming2a.cs(13,8): (Location of symbol related to previous warning)

Os nomes de membro em um escopo específico (como os namespaces em um


assembly, os tipos em um namespace ou os membros em um tipo) devem ser
exclusivos, exceto os nomes resolvidos por meio de sobrecarga. Esse requisito é mais
rígido do que o do Common Type System, que permite que vários membros em um
escopo tenham nomes idênticos desde que sejam tipos diferentes de membros (por
exemplo, um é um método e outro é um campo). Em particular, para membros de tipo:

Campos e tipos aninhados são diferenciados apenas por nome.

Métodos, propriedades e eventos que tenham o mesmo nome devem ser


diferentes além apenas do tipo de retorno.

O exemplo a seguir ilustra o requisito de que nomes de membros devem ser exclusivos
dentro de seu escopo. Ele define uma classe chamada Converter que inclui quatro
membros chamados Conversion . Três são métodos e um é uma propriedade. O método
que inclui um parâmetro Int64 tem um nome exclusivo, mas os dois métodos com um
parâmetro Int32 não têm, porque o valor retornado não é considerado parte da
assinatura de um membro. A propriedade Conversion também viola esse requisito
porque as propriedades não podem ter o mesmo nome dos métodos sobrecarregados.

C#

using System;

[assembly: CLSCompliant(true)]

public class Converter


{
public double Conversion(int number)
{
return (double) number;
}

public float Conversion(int number)


{
return (float) number;
}

public double Conversion(long number)


{
return (double) number;
}

public bool Conversion


{
get { return true; }
}
}
// Compilation produces a compiler error like the following:
// Naming3.cs(13,17): error CS0111: Type 'Converter' already defines a
member called
// 'Conversion' with the same parameter types
// Naming3.cs(8,18): (Location of symbol related to previous error)
// Naming3.cs(23,16): error CS0102: The type 'Converter' already contains
a definition for
// 'Conversion'
// Naming3.cs(8,18): (Location of symbol related to previous error)

Linguagens individuais incluem palavras-chave exclusivas, portanto, linguagens que


apontam para o Common Language Runtime também devem oferecer algum
mecanismo para fazer referência a identificadores (como nomes de tipo) que coincidam
com palavras-chave. Por exemplo, case é uma palavra-chave no C# e no Visual Basic.
No entanto, o exemplo do Visual Basic a seguir pode remover a ambiguidade de uma
classe chamada case da palavra-chave case , usando chaves de abertura e fechamento.
Caso contrário, o exemplo produziria a mensagem de erro "A palavra-chave não é válida
como um identificador", e não seria compilado.

VB

Public Class [case]


Private _id As Guid
Private name As String

Public Sub New(name As String)


_id = Guid.NewGuid()
Me.name = name
End Sub

Public ReadOnly Property ClientName As String


Get
Return name
End Get
End Property
End Class

O exemplo do C# a seguir pode criar uma instância da classe case usando o símbolo @
para remover a ambiguidade do identificador da palavras-chave da linguagem. Sem ele,
o compilador do C# exibiria duas mensagens de erro, "Tipo esperado" e "'Maiúsculas e
minúsculas' do termo de expressão inválido".

C#

using System;

public class Example


{
public static void Main()
{
@case c = new @case("John");
Console.WriteLine(c.ClientName);
}
}

Conversão de tipos
A Common Language Specification define dois operadores de conversão:

op_Implicit , que é usado para conversões de ampliação que não resultam em


perda de dados ou precisão. Por exemplo, a estrutura Decimal inclui um operador
op_Implicit sobrecarregado para converter valores de tipos integrais e valores
Char em valores Decimal.

op_Explicit , que é usado para conversões de redução que possam resultar em


perda de magnitude (um valor é convertido em um valor com um intervalo menor)
ou precisão. Por exemplo, a estrutura Decimal inclui um operador op_Explicit
sobrecarregado para converter Double e valores Single em Decimal e para
converter valores Decimal em valores inteiros, o Double, Single e Char.

No entanto, nem todas as linguagens dão suporte à sobrecarga de operador ou à


definição de operadores personalizados. Se optar por implementar esses operadores de
conversão, você também deverá fornecer uma maneira alternativa para realizar a
conversão. Recomendamos que você forneça os métodos From Xxx e To Xxx.

O exemplo a seguir define conversões explícitas e implícitas em conformidade com CLS.


Ele cria uma classe UDouble que representa um número de ponto flutuante de precisão
dupla sem sinal. Ele fornece conversões implícitas de UDouble em Double e conversões
explícitas de UDouble em Single, de Double em UDouble e de Single em UDouble . Ele
também define um método ToDouble como uma alternativa ao operador de conversão
implícita e os métodos ToSingle , FromDouble e FromSingle como alternativas aos
operadores de conversão explícita.
C#

using System;

public struct UDouble


{
private double number;

public UDouble(double value)


{
if (value < 0)
throw new InvalidCastException("A negative value cannot be
converted to a UDouble.");

number = value;
}

public UDouble(float value)


{
if (value < 0)
throw new InvalidCastException("A negative value cannot be
converted to a UDouble.");

number = value;
}

public static readonly UDouble MinValue = (UDouble) 0.0;


public static readonly UDouble MaxValue = (UDouble) Double.MaxValue;

public static explicit operator Double(UDouble value)


{
return value.number;
}

public static implicit operator Single(UDouble value)


{
if (value.number > (double) Single.MaxValue)
throw new InvalidCastException("A UDouble value is out of range of
the Single type.");

return (float) value.number;


}

public static explicit operator UDouble(double value)


{
if (value < 0)
throw new InvalidCastException("A negative value cannot be
converted to a UDouble.");

return new UDouble(value);


}

public static implicit operator UDouble(float value)


{
if (value < 0)
throw new InvalidCastException("A negative value cannot be
converted to a UDouble.");

return new UDouble(value);


}

public static Double ToDouble(UDouble value)


{
return (Double) value;
}

public static float ToSingle(UDouble value)


{
return (float) value;
}

public static UDouble FromDouble(double value)


{
return new UDouble(value);
}

public static UDouble FromSingle(float value)


{
return new UDouble(value);
}
}

Matrizes
As matrizes compatíveis com CLS estão em conformidade com as seguintes regras:

Todas as dimensões de uma matriz devem ter um limite inferior igual a zero. O
exemplo a seguir cria uma matriz não compatível com CLS com um limite inferior
de um. Independentemente da presença do atributo CLSCompliantAttribute, o
compilador não detecta se a matriz retornada pelo método Numbers.GetTenPrimes
não está em conformidade com CLS.

C#

[assembly: CLSCompliant(true)]

public class Numbers


{
public static Array GetTenPrimes()
{
Array arr = Array.CreateInstance(typeof(Int32), new int[] {10},
new int[] {1});
arr.SetValue(1, 1);
arr.SetValue(2, 2);
arr.SetValue(3, 3);
arr.SetValue(5, 4);
arr.SetValue(7, 5);
arr.SetValue(11, 6);
arr.SetValue(13, 7);
arr.SetValue(17, 8);
arr.SetValue(19, 9);
arr.SetValue(23, 10);

return arr;
}
}

Todos os elementos de matriz devem consistir em tipos compatíveis com CLS. O


exemplo a seguir define dois métodos que retornam matrizes não em
conformidade com CLS. O primeiro retorna uma matriz de valores UInt32. O
segundo retorna uma matriz Object que inclui valores Int32 e UInt32. Embora o
compilador identifique a primeira matriz como não em conformidade devido ao
seu tipo UInt32, ele não reconhece que a segunda matriz inclui elementos não em
conformidade com CLS.

C#

using System;

[assembly: CLSCompliant(true)]

public class Numbers


{
public static UInt32[] GetTenPrimes()
{
uint[] arr = { 1u, 2u, 3u, 5u, 7u, 11u, 13u, 17u, 19u };
return arr;
}

public static Object[] GetFivePrimes()


{
Object[] arr = { 1, 2, 3, 5u, 7u };
return arr;
}
}
// Compilation produces a compiler warning like the following:
// Array2.cs(8,27): warning CS3002: Return type of
'Numbers.GetTenPrimes()' is not
// CLS-compliant

A resolução de sobrecarga para métodos que tenham parâmetros de matriz se


baseia no fato de que são matrizes e em seu tipo de elemento. Por esse motivo, a
seguinte definição de um método GetSquares sobrecarregado é compatível com
CLS.

C#

using System;
using System.Numerics;

[assembly: CLSCompliant(true)]

public class Numbers


{
public static byte[] GetSquares(byte[] numbers)
{
byte[] numbersOut = new byte[numbers.Length];
for (int ctr = 0; ctr < numbers.Length; ctr++) {
int square = ((int) numbers[ctr]) * ((int) numbers[ctr]);
if (square <= Byte.MaxValue)
numbersOut[ctr] = (byte) square;
// If there's an overflow, assign MaxValue to the
corresponding
// element.
else
numbersOut[ctr] = Byte.MaxValue;
}
return numbersOut;
}

public static BigInteger[] GetSquares(BigInteger[] numbers)


{
BigInteger[] numbersOut = new BigInteger[numbers.Length];
for (int ctr = 0; ctr < numbers.Length; ctr++)
numbersOut[ctr] = numbers[ctr] * numbers[ctr];

return numbersOut;
}
}

Interfaces
Interfaces compatíveis com CLS podem definir propriedades, eventos e métodos virtuais
(métodos sem implementação). Uma interface compatível com CLS não pode ter
nenhum dos seguintes itens:

Métodos estáticos ou campos estáticos. Os compiladores do C# e do Visual Basic


gerarão erros de compilador se você definir um membro estático em uma
interface.
Campos. Os compiladores do C# e do Visual Basic gerarão erros de compilador se
você definir um campo em uma interface.

Métodos que não são compatíveis com CLS. Por exemplo, a definição a seguir da
interface inclui um método, INumber.GetUnsigned , que está marcado como não
compatível com CLS. Este exemplo gera um aviso do compilador.

C#

using System;

[assembly:CLSCompliant(true)]

public interface INumber


{
int Length();
[CLSCompliant(false)] ulong GetUnsigned();
}
// Attempting to compile the example displays output like the
following:
// Interface2.cs(8,32): warning CS3010: 'INumber.GetUnsigned()':
CLS-compliant interfaces
// must have only CLS-compliant members

Devido a essa regra, os tipos compatíveis com CLS não são necessários para
implementar membros não compatíveis com CLS. Se uma estrutura compatível
com CLS expuser uma classe que implementa uma interface não compatível com
CLS, ela também deverá fornecer implementações concretas de todos os membros
não compatíveis com CLS.

Compiladores de linguagem compatíveis com CLS também devem permitir que uma
classe forneça implementações separadas dos membros com o mesmo nome e a
assinatura em várias interfaces. O C# e o Visual Basic dão suporte a implementações
explícitas de interface para fornecer implementações diferentes de métodos com nomes
idênticos. O Visual Basic também dá suporte à palavra-chave Implements , que permite
que você designe explicitamente qual interface e membro um determinado membro
implementa. O exemplo a seguir ilustra esse cenário, definindo uma classe Temperature
que implementa as interfaces ICelsius e IFahrenheit como implementações explícitas
de interface.

C#

using System;

[assembly: CLSCompliant(true)]
public interface IFahrenheit
{
decimal GetTemperature();
}

public interface ICelsius


{
decimal GetTemperature();
}

public class Temperature : ICelsius, IFahrenheit


{
private decimal _value;

public Temperature(decimal value)


{
// We assume that this is the Celsius value.
_value = value;
}

decimal IFahrenheit.GetTemperature()
{
return _value * 9 / 5 + 32;
}

decimal ICelsius.GetTemperature()
{
return _value;
}
}
public class Example
{
public static void Main()
{
Temperature temp = new Temperature(100.0m);
ICelsius cTemp = temp;
IFahrenheit fTemp = temp;
Console.WriteLine("Temperature in Celsius: {0} degrees",
cTemp.GetTemperature());
Console.WriteLine("Temperature in Fahrenheit: {0} degrees",
fTemp.GetTemperature());
}
}
// The example displays the following output:
// Temperature in Celsius: 100.0 degrees
// Temperature in Fahrenheit: 212.0 degrees

Enumerações
Enumerações compatíveis com CLS devem seguir estas regras:
O tipo subjacente da enumeração deve ser um inteiro intrínseco compatível com
CLS (Byte, Int16, Int32 ou Int64). Por exemplo, o código a seguir tenta definir uma
enumeração cujo tipo subjacente é UInt32 e gera um aviso do compilador.

C#

using System;

[assembly: CLSCompliant(true)]

public enum Size : uint {


Unspecified = 0,
XSmall = 1,
Small = 2,
Medium = 3,
Large = 4,
XLarge = 5
};

public class Clothing


{
public string Name;
public string Type;
public string Size;
}
// The attempt to compile the example displays a compiler warning like
the following:
// Enum3.cs(6,13): warning CS3009: 'Size': base type 'uint' is not
CLS-compliant

Um tipo de enumeração deve ter um campo de instância única chamado Value__


que foi marcado com o atributo FieldAttributes.RTSpecialName. Isso permite que
você referencie o valor do campo implicitamente.

Uma enumeração inclui campos estáticos literais, cujos tipos correspondem ao


tipo da própria enumeração. Por exemplo, se você definir uma enumeração State
com valores de State.On e State.Off , State.On e State.Off serão campos literais
estáticos cujo tipo será State .

Há dois tipos de enumeração:

Uma enumeração que representa um conjunto de valores mutuamente


excludentes, valores inteiros nomeados. Esse tipo de enumeração é indicado
pela ausência do atributo personalizado System.FlagsAttribute.

Uma enumeração que representa um conjunto de sinalizadores de bit que


podem ser combinados para produzir um valor sem nome. Esse tipo de
enumeração é indicado pela presença do atributo personalizado
System.FlagsAttribute.

Para obter mais informações, consulte a documentação da estrutura Enum.

O valor de uma enumeração não está limitado ao intervalo de seus valores


especificados. Em outras palavras, o intervalo de valores em uma enumeração é o
intervalo de seu valor subjacente. Você pode usar o método Enum.IsDefined para
determinar se um valor especificado é membro de uma enumeração.

Membros de tipo em geral


O Common Language Specification requer que todos os campos e métodos sejam
acessados como membros de uma classe específica. Portanto, campos e métodos
estáticos globais (ou seja, campos ou métodos estáticos que são definidos
independentemente de um tipo) não são compatíveis com CLS. Se você tentar incluir
um campo ou um método global em seu código fonte, os compiladores do C# e do
Visual Basic gerarão um erro de compilador.

A Common Language Specification dá suporte somente à convenção de chamada


gerenciada padrão. Ela não dá suporte a convenções e métodos de chamada não
gerenciados com listas de argumentos de variável marcadas com a palavra-chave
varargs . Para as listas de argumentos variáveis que são compatíveis com a convenção

de chamada gerenciada padrão, use o atributo ParamArrayAttribute ou a


implementação da linguagem individual, como a palavra-chave params no C# e a
palavra-chave ParamArray no Visual Basic.

Acessibilidade de membro
A substituição de um membro herdado não pode alterar a acessibilidade desse
membro. Por exemplo, um método público em uma classe base não pode ser
substituído por um método privado em uma classe derivada. Há uma exceção: um
membro protected internal (em C#) ou Protected Friend (em Visual Basic) em um
assembly que é substituído por um tipo em um assembly diferente. Nesse caso, a
acessibilidade da substituição é Protected .

O exemplo a seguir ilustra o erro que é gerado quando o atributo


CLSCompliantAttribute é definido como true e Human , que é uma classe derivada de
Animal , tenta alterar a acessibilidade da propriedade Species de pública para privada. O

exemplo será compilado com êxito se sua acessibilidade for alterada para pública.

C#
using System;

[assembly: CLSCompliant(true)]

public class Animal


{
private string _species;

public Animal(string species)


{
_species = species;
}

public virtual string Species


{
get { return _species; }
}

public override string ToString()


{
return _species;
}
}

public class Human : Animal


{
private string _name;

public Human(string name) : base("Homo Sapiens")


{
_name = name;
}

public string Name


{
get { return _name; }
}

private override string Species


{
get { return base.Species; }
}

public override string ToString()


{
return _name;
}
}

public class Example


{
public static void Main()
{
Human p = new Human("John");
Console.WriteLine(p.Species);
Console.WriteLine(p.ToString());
}
}
// The example displays the following output:
// error CS0621: 'Human.Species': virtual or abstract members cannot be
private

Os tipos na assinatura de um membro deverão ser acessíveis sempre que o membro for
acessível. Por exemplo, isso significa que um membro público não pode incluir um
parâmetro cujo tipo seja privado, protegido ou interno. O exemplo a seguir ilustra o
erro do compilador que resulta quando um construtor de classe StringWrapper expõe
um valor de enumeração StringOperationType interno que determina como um valor de
cadeia de caracteres deve ser encapsulado.

C#

using System;
using System.Text;

public class StringWrapper


{
string internalString;
StringBuilder internalSB = null;
bool useSB = false;

public StringWrapper(StringOperationType type)


{
if (type == StringOperationType.Normal) {
useSB = false;
}
else {
useSB = true;
internalSB = new StringBuilder();
}
}

// The remaining source code...


}

internal enum StringOperationType { Normal, Dynamic }


// The attempt to compile the example displays the following output:
// error CS0051: Inconsistent accessibility: parameter type
// 'StringOperationType' is less accessible than method
// 'StringWrapper.StringWrapper(StringOperationType)'

Tipos e membros genéricos


Tipos aninhados sempre têm pelo menos o mesmo número de parâmetros genéricos do
tipo delimitador. Eles correspondem por posição aos parâmetros genéricos no tipo
delimitador. O tipo genérico também pode incluir novos parâmetros genéricos.

A relação entre os parâmetros de tipo genérico de um tipo de contenção e seus tipos


aninhados pode ser ocultada pela sintaxe de linguagens individuais. No exemplo a
seguir, um tipo genérico Outer<T> contém duas classes aninhadas, Inner1A e
Inner1B<U> . As chamadas para o método ToString , que cada classe herda de
Object.ToString(), mostram que cada classe aninhada inclui parâmetros de tipo de sua
classe de contenção.

C#

using System;

[assembly:CLSCompliant(true)]

public class Outer<T>


{
T value;

public Outer(T value)


{
this.value = value;
}

public class Inner1A : Outer<T>


{
public Inner1A(T value) : base(value)
{ }
}

public class Inner1B<U> : Outer<T>


{
U value2;

public Inner1B(T value1, U value2) : base(value1)


{
this.value2 = value2;
}
}
}

public class Example


{
public static void Main()
{
var inst1 = new Outer<String>("This");
Console.WriteLine(inst1);

var inst2 = new Outer<String>.Inner1A("Another");


Console.WriteLine(inst2);

var inst3 = new Outer<String>.Inner1B<int>("That", 2);


Console.WriteLine(inst3);
}
}
// The example displays the following output:
// Outer`1[System.String]
// Outer`1+Inner1A[System.String]
// Outer`1+Inner1B`1[System.String,System.Int32]

Os nomes de tipo genérico são codificados no formato name'n, em que name é o nome
do tipo, ` é um literal de caractere e n é o número de parâmetros declarados no tipo ou,
para tipos genéricos aninhados, o número de parâmetros de tipo que acabam de ser
introduzidos. Essa codificação de nomes de tipo genéricos é principalmente de interesse
de desenvolvedores que usam a reflexão para acessar tipos genéricos compatíveis com
CLS em uma biblioteca.

Se as restrições forem aplicadas a um tipo genérico, qualquer tipo usado como restrição
também deverá ser compatível com CLS. O exemplo a seguir define uma classe
chamada BaseClass que não está em conformidade com CLS e uma classe genérica
chamada BaseCollection cujo parâmetro de tipo deve derivar de BaseClass . Mas como
BaseClass não é compatível com CLS, o compilador emite um aviso.

C#

using System;

[assembly:CLSCompliant(true)]

[CLSCompliant(false)] public class BaseClass


{}

public class BaseCollection<T> where T : BaseClass


{}
// Attempting to compile the example displays the following output:
// warning CS3024: Constraint type 'BaseClass' is not CLS-compliant

Se um tipo genérico for derivado de um tipo de base genérico, ele deverá redeclarar
todas as restrições, para assegurar que as restrições no tipo de base também sejam
atendidas. O exemplo a seguir define um Number<T> que pode representar qualquer tipo
numérico. Ele também define uma classe FloatingPoint<T> que representa um valor de
ponto flutuante. No entanto, o código-fonte falha na compilação porque não aplica a
restrição em Number<T> (esse T deve ser um tipo de valor) a FloatingPoint<T> .

C#
using System;

[assembly:CLSCompliant(true)]

public class Number<T> where T : struct


{
// use Double as the underlying type, since its range is a superset of
// the ranges of all numeric types except BigInteger.
protected double number;

public Number(T value)


{
try {
this.number = Convert.ToDouble(value);
}
catch (OverflowException e) {
throw new ArgumentException("value is too large.", e);
}
catch (InvalidCastException e) {
throw new ArgumentException("The value parameter is not numeric.",
e);
}
}

public T Add(T value)


{
return (T) Convert.ChangeType(number + Convert.ToDouble(value),
typeof(T));
}

public T Subtract(T value)


{
return (T) Convert.ChangeType(number - Convert.ToDouble(value),
typeof(T));
}
}

public class FloatingPoint<T> : Number<T>


{
public FloatingPoint(T number) : base(number)
{
if (typeof(float) == number.GetType() ||
typeof(double) == number.GetType() ||
typeof(decimal) == number.GetType())
this.number = Convert.ToDouble(number);
else
throw new ArgumentException("The number parameter is not a
floating-point number.");
}
}
// The attempt to compile the example displays the following output:
// error CS0453: The type 'T' must be a non-nullable value type in
// order to use it as parameter 'T' in the generic type or
method 'Number<T>'
O exemplo será compilado com êxito se a restrição for adicionada à classe
FloatingPoint<T> .

C#

using System;

[assembly:CLSCompliant(true)]

public class Number<T> where T : struct


{
// use Double as the underlying type, since its range is a superset of
// the ranges of all numeric types except BigInteger.
protected double number;

public Number(T value)


{
try {
this.number = Convert.ToDouble(value);
}
catch (OverflowException e) {
throw new ArgumentException("value is too large.", e);
}
catch (InvalidCastException e) {
throw new ArgumentException("The value parameter is not numeric.",
e);
}
}

public T Add(T value)


{
return (T) Convert.ChangeType(number + Convert.ToDouble(value),
typeof(T));
}

public T Subtract(T value)


{
return (T) Convert.ChangeType(number - Convert.ToDouble(value),
typeof(T));
}
}

public class FloatingPoint<T> : Number<T> where T : struct


{
public FloatingPoint(T number) : base(number)
{
if (typeof(float) == number.GetType() ||
typeof(double) == number.GetType() ||
typeof(decimal) == number.GetType())
this.number = Convert.ToDouble(number);
else
throw new ArgumentException("The number parameter is not a
floating-point number.");
}
}

A Common Language Specification impõe um modelo por instanciação conservador


para tipos aninhados e membros protegidos. Tipos genéricos abertos não podem expor
campos ou membros com assinaturas que contenham uma instanciação específica de
um tipo genérico aninhado, protegido. Tipos não genéricos que estendam uma
instanciação específica de uma interface ou classe base genérica não podem expor
campos ou membros com assinaturas que contenham uma instanciação diferente de
um tipo genérico aninhado e protegido.

O exemplo a seguir define um tipo genérico, C1<T> (ou C1(Of T) no Visual Basic) e uma
classe protegida, C1<T>.N (ou C1(Of T).N no Visual Basic). C1<T> possui dois métodos,
M1 e M2 . No entanto, M1 não está em conformidade com CLS porque tenta retornar um

objeto C1<int>.N (ou C1(Of Integer).N ) de C1<T> (ou C1(Of T) ). Uma segunda classe,
C2 , é derivada de C1<long> (ou de C1(Of Long) ). Tem dois métodos, M3 e M4 . No

entanto, M3 não é compatível com CLS porque tenta retornar um objeto C1<int>.N (ou
C1(Of Integer).N ) de uma subclasse de C1<long> . Os compiladores de linguagens
podem ser ainda mais restritivos. Neste exemplo, o Visual Basic exibe um erro ao tentar
compilar M4 .

C#

using System;

[assembly:CLSCompliant(true)]

public class C1<T>


{
protected class N { }

protected void M1(C1<int>.N n) { } // Not CLS-compliant - C1<int>.N not


// accessible from within C1<T> in all
// languages
protected void M2(C1<T>.N n) { } // CLS-compliant – C1<T>.N accessible
// inside C1<T>
}

public class C2 : C1<long>


{
protected void M3(C1<int>.N n) { } // Not CLS-compliant – C1<int>.N is
not
// accessible in C2 (extends
C1<long>)

protected void M4(C1<long>.N n) { } // CLS-compliant, C1<long>.N is


// accessible in C2 (extends
C1<long>)
}
// Attempting to compile the example displays output like the following:
// Generics4.cs(9,22): warning CS3001: Argument type 'C1<int>.N' is
not CLS-compliant
// Generics4.cs(18,22): warning CS3001: Argument type 'C1<int>.N' is
not CLS-compliant

Construtores
Os construtores em classes compatíveis com CLS e em estruturas devem seguir estas
regras:

Um construtor de uma classe derivada deve chamar o construtor de instância de


sua classe base antes de acessar quaisquer dados de instância herdados. Esse
requisito existe porque os construtores de classe base não são herdados por suas
classes derivadas. Essa regra não se aplica a estruturas que não dão suporte a
herança direta.

Normalmente, os compiladores aplicam essa regra independentemente da


conformidade com CLS, conforme mostrado no exemplo a seguir. Ele cria uma
classe Doctor que é derivada de uma classe Person , mas a classe Doctor falha ao
chamar o construtor de classe Person para inicializar campos herdados da
instância.

C#

using System;

[assembly: CLSCompliant(true)]

public class Person


{
private string fName, lName, _id;

public Person(string firstName, string lastName, string id)


{
if (String.IsNullOrEmpty(firstName + lastName))
throw new ArgumentNullException("Either a first name or a last
name must be provided.");

fName = firstName;
lName = lastName;
_id = id;
}

public string FirstName


{
get { return fName; }
}

public string LastName


{
get { return lName; }
}

public string Id
{
get { return _id; }
}

public override string ToString()


{
return String.Format("{0}{1}{2}", fName,
String.IsNullOrEmpty(fName) ? "" : " ",
lName);
}
}

public class Doctor : Person


{
public Doctor(string firstName, string lastName, string id)
{
}

public override string ToString()


{
return "Dr. " + base.ToString();
}
}
// Attempting to compile the example displays output like the
following:
// ctor1.cs(45,11): error CS1729: 'Person' does not contain a
constructor that takes 0
// arguments
// ctor1.cs(10,11): (Location of symbol related to previous error)

Um construtor de objeto não pode ser chamado, exceto para criar um objeto.
Além disso, um objeto não pode ser inicializado duas vezes. Por exemplo, isso
significa que Object.MemberwiseClone os métodos de desserialização não devem
chamar construtores.

Propriedades
As propriedades em tipos em conformidade com CLS devem seguir estas regras:

Uma propriedade deve ter um setter, um getter ou ambos. Em um assembly, eles


são implementados como métodos especiais, o que significa que aparecerão como
métodos separados (o getter é chamado de get_ propertyname e o setter é
set_ propertyname) marcados como SpecialName nos metadados do assembly. Os
compiladores do C# e do Visual Basic aplicam automaticamente essa regra, sem a
necessidade de aplicar o atributo CLSCompliantAttribute.

Um tipo de propriedade é o tipo de retorno do getter da propriedade e o último


argumento do setter. Esses tipos devem estar em conformidade com CLS e os
argumentos não podem ser atribuídos à propriedade por referência (ou seja, não
podem ser ponteiros gerenciados).

Se uma propriedade tiver um getter e um setter, ambos deverão ser virtuais,


estáticos ou instâncias. Os compiladores do C# e do Visual Basic aplicam
automaticamente essa regra por meio de sua sintaxe de definição da propriedade.

Eventos
Um evento é definido por seu nome e tipo. O tipo de evento é um delegado que é
usado para indicar o evento. Por exemplo, o evento AppDomain.AssemblyResolve é do
tipo ResolveEventHandler. Além do evento em si, três métodos com nomes com base
no nome do evento fornecem a implementação do evento e estão marcados como
SpecialName nos metadados do assembly:

Um método para adicionar um manipulador de eventos, chamado


add_ EventName. Por exemplo, o método de assinatura do evento para o evento

AppDomain.AssemblyResolve é chamado add_AssemblyResolve .

Um método para remover um manipulador de eventos, chamado


remove_ EventName. Por exemplo, o método de remoção para o evento
AppDomain.AssemblyResolve é chamado remove_AssemblyResolve .

Um método para indicar que o evento ocorreu, chamado raise_ EventName.

7 Observação

A maioria das regras da Common Language Specification em relação a eventos é


implementada por compiladores de linguagem e é transparente para
desenvolvedores de componente.

Os métodos para adicionar, remover e acionar o evento devem ter a mesma


acessibilidade. Eles também devem ser todos estáticos, instâncias ou virtuais. Os
métodos para adicionar e remover um evento têm um parâmetro cujo tipo é o tipo de
delegado do evento. Os métodos para adicionar e remover devem estar ambos
presentes ou ausentes.

O exemplo a seguir define uma classe compatível com CLS chamada Temperature que
acionará um evento TemperatureChanged se a mudança de temperatura entre as duas
leituras for igual ou exceder o valor de limite. A classe Temperature define
explicitamente um método raise_TemperatureChanged para executar seletivamente os
manipuladores de eventos.

C#

using System;
using System.Collections;
using System.Collections.Generic;

[assembly: CLSCompliant(true)]

public class TemperatureChangedEventArgs : EventArgs


{
private Decimal originalTemp;
private Decimal newTemp;
private DateTimeOffset when;

public TemperatureChangedEventArgs(Decimal original, Decimal @new,


DateTimeOffset time)
{
originalTemp = original;
newTemp = @new;
when = time;
}

public Decimal OldTemperature


{
get { return originalTemp; }
}

public Decimal CurrentTemperature


{
get { return newTemp; }
}

public DateTimeOffset Time


{
get { return when; }
}
}

public delegate void TemperatureChanged(Object sender,


TemperatureChangedEventArgs e);

public class Temperature


{
private struct TemperatureInfo
{
public Decimal Temperature;
public DateTimeOffset Recorded;
}

public event TemperatureChanged TemperatureChanged;

private Decimal previous;


private Decimal current;
private Decimal tolerance;
private List<TemperatureInfo> tis = new List<TemperatureInfo>();

public Temperature(Decimal temperature, Decimal tolerance)


{
current = temperature;
TemperatureInfo ti = new TemperatureInfo();
ti.Temperature = temperature;
tis.Add(ti);
ti.Recorded = DateTimeOffset.UtcNow;
this.tolerance = tolerance;
}

public Decimal CurrentTemperature


{
get { return current; }
set {
TemperatureInfo ti = new TemperatureInfo();
ti.Temperature = value;
ti.Recorded = DateTimeOffset.UtcNow;
previous = current;
current = value;
if (Math.Abs(current - previous) >= tolerance)
raise_TemperatureChanged(new
TemperatureChangedEventArgs(previous, current, ti.Recorded));
}
}

public void raise_TemperatureChanged(TemperatureChangedEventArgs


eventArgs)
{
if (TemperatureChanged == null)
return;

foreach (TemperatureChanged d in
TemperatureChanged.GetInvocationList()) {
if (d.Method.Name.Contains("Duplicate"))
Console.WriteLine("Duplicate event handler; event handler not
executed.");
else
d.Invoke(this, eventArgs);
}
}
}
public class Example
{
public Temperature temp;

public static void Main()


{
Example ex = new Example();
}

public Example()
{
temp = new Temperature(65, 3);
temp.TemperatureChanged += this.TemperatureNotification;
RecordTemperatures();
Example ex = new Example(temp);
ex.RecordTemperatures();
}

public Example(Temperature t)
{
temp = t;
RecordTemperatures();
}

public void RecordTemperatures()


{
temp.TemperatureChanged += this.DuplicateTemperatureNotification;
temp.CurrentTemperature = 66;
temp.CurrentTemperature = 63;
}

internal void TemperatureNotification(Object sender,


TemperatureChangedEventArgs e)
{
Console.WriteLine("Notification 1: The temperature changed from {0} to
{1}", e.OldTemperature, e.CurrentTemperature);
}

public void DuplicateTemperatureNotification(Object sender,


TemperatureChangedEventArgs e)
{
Console.WriteLine("Notification 2: The temperature changed from {0} to
{1}", e.OldTemperature, e.CurrentTemperature);
}
}

Sobrecargas
A Common Language Specification impõe os seguintes requisitos em membros
sobrecarregados:
Os membros podem ser sobrecarregados com base no número de parâmetros e
no tipo de qualquer parâmetro. A convenção de chamada, o tipo de retorno, os
modificadores personalizados aplicados ao método ou seus parâmetros e se os
parâmetros são passados por valor ou por referência não são considerados na
diferenciação entre sobrecargas. Para obter um exemplo, consulte o código para o
requisito de que os nomes devem ser exclusivos em um escopo na seção
Convenções de nomenclatura.

Somente propriedades e métodos podem ser sobrecarregados. Campos e eventos


não podem ser sobrecarregados.

Métodos genéricos podem ser sobrecarregados com base no número de seus


parâmetros genéricos.

7 Observação

Os operadores op_Explicit e op_Implicit são exceções à regra de que o valor


retornado não é considerado parte de uma assinatura de método para resolução
de sobrecarga. Esses dois operadores podem ser sobrecarregados com base nos
parâmetros e valor retornado.

Exceções
Objetos de exceção devem derivar de System.Exception ou de outro tipo derivado de
System.Exception. O exemplo a seguir ilustra o erro do compilador que resulta quando
uma classe personalizada chamada ErrorClass é usada para tratamento de exceções.

C#

using System;

[assembly: CLSCompliant(true)]

public class ErrorClass


{
string msg;

public ErrorClass(string errorMessage)


{
msg = errorMessage;
}

public string Message


{
get { return msg; }
}
}

public static class StringUtilities


{
public static string[] SplitString(this string value, int index)
{
if (index < 0 | index > value.Length) {
ErrorClass badIndex = new ErrorClass("The index is not within the
string.");
throw badIndex;
}
string[] retVal = { value.Substring(0, index - 1),
value.Substring(index) };
return retVal;
}
}
// Compilation produces a compiler error like the following:
// Exceptions1.cs(26,16): error CS0155: The type caught or thrown must be
derived from
// System.Exception

Para corrigir este erro, a classe ErrorClass deve herdar de System.Exception. Além disso,
a propriedade Message deve ser substituída. O exemplo a seguir corrige esses erros para
definir uma classe ErrorClass que seja compatível com CLS.

C#

using System;

[assembly: CLSCompliant(true)]

public class ErrorClass : Exception


{
string msg;

public ErrorClass(string errorMessage)


{
msg = errorMessage;
}

public override string Message


{
get { return msg; }
}
}

public static class StringUtilities


{
public static string[] SplitString(this string value, int index)
{
if (index < 0 | index > value.Length) {
ErrorClass badIndex = new ErrorClass("The index is not within the
string.");
throw badIndex;
}
string[] retVal = { value.Substring(0, index - 1),
value.Substring(index) };
return retVal;
}
}

Atributos
Em assemblies do .NET, atributos personalizados fornecem um mecanismo extensível
para armazenamento de atributos personalizados e recuperação de metadados sobre
objetos de programação, como assemblies, tipos, membros e parâmetros de método.
Atributos personalizados devem derivar de System.Attribute ou de um tipo derivado de
System.Attribute .

O exemplo a seguir viola essa regra. Ele define uma classe NumericAttribute que não
deriva de System.Attribute. Um erro de compilador só acontece quando o atributo que
não está em conformidade com CLS é aplicado e não quando a classe é definida.

C#

using System;

[assembly: CLSCompliant(true)]

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct)]
public class NumericAttribute
{
private bool _isNumeric;

public NumericAttribute(bool isNumeric)


{
_isNumeric = isNumeric;
}

public bool IsNumeric


{
get { return _isNumeric; }
}
}

[Numeric(true)] public struct UDouble


{
double Value;
}
// Compilation produces a compiler error like the following:
// Attribute1.cs(22,2): error CS0616: 'NumericAttribute' is not an
attribute class
// Attribute1.cs(7,14): (Location of symbol related to previous error)

O construtor ou as propriedades de um atributo compatível com CLS podem expor


somente os seguintes tipos:

Boolean

Byte

Char

Double

Int16

Int32

Int64

Single

String

Type

Qualquer tipo de enumeração cujo tipo subjacente seja Byte, Int16, Int32 ou Int64.

O exemplo a seguir define uma classe DescriptionAttribute que deriva de Atributo. O


construtor de classe tem um parâmetro do tipo Descriptor , portanto a classe não é
compatível com CLS. O compilador do C# emite um aviso, mas compila com êxito,
enquanto o compilador do Visual Basic não emite um aviso nem um erro.

C#

using System;

[assembly:CLSCompliantAttribute(true)]

public enum DescriptorType { type, member };

public class Descriptor


{
public DescriptorType Type;
public String Description;
}

[AttributeUsage(AttributeTargets.All)]
public class DescriptionAttribute : Attribute
{
private Descriptor desc;

public DescriptionAttribute(Descriptor d)
{
desc = d;
}

public Descriptor Descriptor


{ get { return desc; } }
}
// Attempting to compile the example displays output like the following:
// warning CS3015: 'DescriptionAttribute' has no accessible
// constructors which use only CLS-compliant types

O atributo CLSCompliantAttribute
O atributo CLSCompliantAttribute é usado para indicar se um elemento do programa
está em conformidade com a Common Language Specification. O construtor
CLSCompliantAttribute(Boolean) inclui um único parâmetro necessário, isCompliant, que
indica se o elemento de programa é compatível com CLS.

No tempo de compilação, o compilador detecta os elementos não compatíveis que


provavelmente são compatíveis com CLS e emite um aviso. O compilador não emite
avisos para tipos ou membros explicitamente declarados como não compatíveis.

Os desenvolvedores de componentes podem usar o atributo CLSCompliantAttribute de


duas maneiras:

Para definir as partes da interface pública exposta por um componente que são
compatíveis com CLS e as partes que não são compatíveis com CLS. Quando o
atributo é usado para marcar elementos de programa específicos como em
conformidade com CLS, seu uso garante que os elementos sejam acessíveis em
todas as linguagens e ferramentas direcionadas ao .NET.

Para garantir que a interface pública da biblioteca de componentes exponha


apenas elementos de programa que são compatíveis com CLS. Se os elementos
não forem compatíveis com CLS, os compiladores geralmente emitirão um aviso.

2 Aviso

Em alguns casos, os compiladores de linguagem aplicam as regras de


compatibilidade com CLS independentemente do uso ou não do atributo
CLSCompliantAttribute . Por exemplo, definir um membro estático em uma
interface viola uma regra CLS. Neste sentido, se você definir um membro static
(no C#) ou Shared (no Visual Basic) em uma interface, os compiladores do C# e do
Visual Basic exibirão uma mensagem de erro e não compilarão o aplicativo.

O atributo CLSCompliantAttribute é marcado com um atributo AttributeUsageAttribute


que tem um valor de AttributeTargets.All. Esse valor permite que você aplique o atributo
CLSCompliantAttribute a qualquer elemento de programa, incluindo assemblies,
módulos, tipos (classes, estruturas, interfaces, enumerações e delegados), membros de
tipo (construtores, métodos, propriedades, campos e eventos), parâmetros, parâmetros
genéricos e valores de retorno. No entanto, na prática, você deve aplicar o atributo
somente a assemblies, tipos e membros de tipo. Caso contrário, os compiladores
ignoram o atributo e continuam gerando avisos do compilador sempre que
encontrarem um parâmetro não compatível, parâmetro genérico ou valor retornado na
interface pública da biblioteca.

O valor do atributo CLSCompliantAttribute é herdado pelos elementos contidos no


programa. Por exemplo, se um assembly for marcado como compatível com CLS, seus
tipos também serão compatíveis com CLS. Se um tipo for marcado como em
conformidade com CLS, seus membros e tipos aninhados também tem conformidade
com CLS.

Você pode substituir explicitamente a compatibilidade herdada aplicando o atributo


CLSCompliantAttribute a um elemento contido no programa. Por exemplo, é possível
usar o atributo CLSCompliantAttribute com um valor isCompliant de false para definir
um tipo não compatível em um assembly compatível, e é possível usar o atributo com
um valor isCompliant de true para definir um tipo compatível em um assembly não
compatível. Você também pode definir membros não compatíveis em um tipo
compatível. No entanto, um tipo não compatível não pode ter membros compatíveis.
Portanto, você não pode usar o atributo com um valor isCompliant de true para
substituir a herança de um tipo não compatível.

Ao desenvolver componentes, você sempre deve usar o atributo CLSCompliantAttribute


para indicar se o assembly, seus tipos e membros são compatíveis com CLS.

Para criar componentes compatíveis com CLS:

1. Use CLSCompliantAttribute para marcar o assembly como em conformidade com


CLS.

2. Marque qualquer tipo exposto publicamente no assembly que não esteja em


conformidade com CLS como não em conformidade.
3. Marque qualquer membro publicamente exposto em tipos compatíveis com CLS
como não compatíveis.

4. Forneça uma alternativa em conformidade com CLS para membros não em


conformidade com CLS.

Se você marcou com êxito todos os tipos e membros não em conformidade, o


compilador não deverá emitir avisos de não conformidade. Entretanto, você deve indicar
quais membros não são compatíveis com CLS e listar suas alternativas compatíveis com
CLS na documentação do produto.

O exemplo a seguir usa o atributo CLSCompliantAttribute para definir um assembly


compatível com CLS e um tipo, CharacterUtilities , que tem dois membros não
compatíveis com CLS. Como ambos os membros são marcados com o atributo
CLSCompliant(false) , o compilador não produz avisos. A classe também fornece uma
alternativa compatível com CLS para ambos os métodos. Normalmente, nós
adicionaríamos apenas duas sobrecargas ao método ToUTF16 para fornecer alternativas
compatíveis com CLS. Entretanto, como os métodos não podem ser sobrecarregados
com base no valor retornado, os nomes dos métodos em conformidade com CLS são
diferentes dos nomes dos métodos não em conformidade.

C#

using System;
using System.Text;

[assembly:CLSCompliant(true)]

public class CharacterUtilities


{
[CLSCompliant(false)] public static ushort ToUTF16(String s)
{
s = s.Normalize(NormalizationForm.FormC);
return Convert.ToUInt16(s[0]);
}

[CLSCompliant(false)] public static ushort ToUTF16(Char ch)


{
return Convert.ToUInt16(ch);
}

// CLS-compliant alternative for ToUTF16(String).


public static int ToUTF16CodeUnit(String s)
{
s = s.Normalize(NormalizationForm.FormC);
return (int) Convert.ToUInt16(s[0]);
}

// CLS-compliant alternative for ToUTF16(Char).


public static int ToUTF16CodeUnit(Char ch)
{
return Convert.ToInt32(ch);
}

public bool HasMultipleRepresentations(String s)


{
String s1 = s.Normalize(NormalizationForm.FormC);
return s.Equals(s1);
}

public int GetUnicodeCodePoint(Char ch)


{
if (Char.IsSurrogate(ch))
throw new ArgumentException("ch cannot be a high or low
surrogate.");

return Char.ConvertToUtf32(ch.ToString(), 0);


}

public int GetUnicodeCodePoint(Char[] chars)


{
if (chars.Length > 2)
throw new ArgumentException("The array has too many characters.");

if (chars.Length == 2) {
if (! Char.IsSurrogatePair(chars[0], chars[1]))
throw new ArgumentException("The array must contain a low and a
high surrogate.");
else
return Char.ConvertToUtf32(chars[0], chars[1]);
}
else {
return Char.ConvertToUtf32(chars.ToString(), 0);
}
}
}

Se você estiver desenvolvendo um aplicativo em vez de uma biblioteca (ou seja, se não
estiver expondo tipos ou membros que possam ser consumidos por outros
desenvolvedores de aplicativos), a conformidade com CLS dos elementos do programa
que seu aplicativo consome só serão de interesse se sua linguagem não der suporte a
eles. Nesse caso, seu compilador de linguagem gerará um erro quando você tentar usar
um elemento não compatível com CLS.

Interoperabilidade em qualquer linguagem


A independência de linguagem tem alguns significados possíveis. Um significado
envolve o consumo contínuo de tipos gravados em uma linguagem de um aplicativo
gravado em outra linguagem. Um segundo significado, que é o enfoque deste artigo,
envolve combinar o código gravado em várias linguagens em um único assembly do
.NET.

O exemplo a seguir ilustra a interoperabilidade em qualquer idioma com a criação de


uma biblioteca de classes chamada Utilities.dll que inclui duas classes, NumericLib e
StringLib . A classe NumericLib é gravada em C#, e a classe StringLib é gravada em

Visual Basic. Aqui está o código-fonte de StringUtil.vb , que inclui um único membro,
ToTitleCase , em sua classe StringLib .

VB

Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Public Module StringLib


Private exclusions As List(Of String)

Sub New()
Dim words() As String = {"a", "an", "and", "of", "the"}
exclusions = New List(Of String)
exclusions.AddRange(words)
End Sub

<Extension()> _
Public Function ToTitleCase(title As String) As String
Dim words() As String = title.Split()
Dim result As String = String.Empty

For ctr As Integer = 0 To words.Length - 1


Dim word As String = words(ctr)
If ctr = 0 OrElse Not exclusions.Contains(word.ToLower()) Then
result += word.Substring(0, 1).ToUpper() + _
word.Substring(1).ToLower()
Else
result += word.ToLower()
End If
If ctr <= words.Length - 1 Then
result += " "
End If
Next
Return result
End Function
End Module

Aqui está o código-fonte de NumberUtil.cs, que define uma classe NumericLib com dois
membros, IsEven e NearZero .

C#
using System;

public static class NumericLib


{
public static bool IsEven(this IConvertible number)
{
if (number is Byte ||
number is SByte ||
number is Int16 ||
number is UInt16 ||
number is Int32 ||
number is UInt32 ||
number is Int64)
return Convert.ToInt64(number) % 2 == 0;
else if (number is UInt64)
return ((ulong) number) % 2 == 0;
else
throw new NotSupportedException("IsEven called for a non-integer
value.");
}

public static bool NearZero(double number)


{
return Math.Abs(number) < .00001;
}
}

Para empacotar as duas classes em um único assembly, você deve compilá-las em


módulos. Para compilar o arquivo de código-fonte do Visual Basic em um módulo, use
este comando:

Console

vbc /t:module StringUtil.vb

Para obter mais informações sobre a sintaxe de linha de comando do compilador do


Visual Basic, consulte Compilando através da linha de comando.

Para compilar o arquivo de código-fonte do C# em um módulo, use este comando:

Console

csc /t:module NumberUtil.cs

Então você usa as Opções do vinculador para compilar os dois módulos em um


assembly:

Console
link numberutil.netmodule stringutil.netmodule /out:UtilityLib.dll /dll

O exemplo a seguir chama os métodos NumericLib.NearZero e StringLib.ToTitleCase .


O código do Visual Basic e o código do C# podem acessar os métodos em ambas as
classes.

C#

using System;

public class Example


{
public static void Main()
{
Double dbl = 0.0 - Double.Epsilon;
Console.WriteLine(NumericLib.NearZero(dbl));

string s = "war and peace";


Console.WriteLine(s.ToTitleCase());
}
}
// The example displays the following output:
// True
// War and Peace

Para compilar o código do Visual Basic, use este comando:

Console

vbc example.vb /r:UtilityLib.dll

Para compilar usando C#, altere o nome do compilador de vbc para csc , e altere a
extensão de arquivo de .vb para .cs:

Console

csc example.cs /r:UtilityLib.dll


Conversão de tipo no .NET
Artigo • 06/04/2023

Cada valor tem um tipo associado, o qual define atributos como a quantidade de
espaço alocado para o valor, o intervalo de valores possíveis que ele pode assumir e os
membros que ele disponibiliza. Muitos valores podem ser expressos por mais de um
tipo. Por exemplo, o valor 4 pode ser expresso como um número inteiro ou como um
valor de ponto flutuante. A conversão de tipo cria um valor em um novo tipo que é
equivalente ao valor de um tipo antigo, mas que não necessariamente preserva a
identidade (ou o valor exato) do objeto original.

O .NET oferece suporte automaticamente às seguintes conversões:

Conversão de uma classe derivada em uma classe base. Isso significa, por exemplo,
que uma instância de qualquer classe ou estrutura pode ser convertida em uma
instância de Object. Essa conversão não exige um operador de conversão.

Conversão de uma classe base para a classe derivada original. No C#, essa
conversão exige um operador de conversão. No Visual Basic, ela exige o operador
CType quando a Option Strict está ativada.

Conversão de um tipo que implementa uma interface para um objeto de interface


que representa essa interface. Essa conversão não exige um operador de
conversão.

Conversão de um objeto de interface de volta para o tipo original que implementa


essa interface. No C#, essa conversão exige um operador de conversão. No Visual
Basic, ela exige o operador CType quando a Option Strict está ativada.

Além dessas conversões automáticas, o .NET fornece várias funcionalidades que


oferecem suporte à conversão personalizada de tipo. Elas incluem o seguinte:

O operador Implicit , que define as conversões de ampliação disponíveis entre


tipos. Para obter mais informações, consulte a seção Conversão implícita com o
operador implícito.

O operador Explicit , que define as conversões de redução disponíveis entre


tipos. Para obter mais informações, consulte a seção Conversão explícita com o
operador explícito.

A interface IConvertible, que define conversões para cada um dos tipos de dados
base do .NET. Para obter mais informações, consulte a seção A interface
IConvertible.
A classe Convert, que fornece um conjunto de métodos que implementam os
métodos da interface IConvertible. Para obter mais informações, consulte a seção
A classe Convert.

A classe TypeConverter, que é uma classe base que pode ser estendida para
oferecer suporte à conversão de um tipo especificado para qualquer outro tipo.
Para obter mais informações, consulte a seção A classe TypeConverter.

Conversão implícita com o operador implícito


As conversões de ampliação envolvem a criação de um novo valor do valor de um tipo
existente que tem um intervalo mais restritivo ou uma lista de membros mais restrita
que o tipo de destino. As conversões de ampliação não podem resultar na perda de
dados (mas podem resultar em uma perda de precisão). Como os dados não podem ser
perdidos, os compiladores podem tratar a conversão de forma implícita ou
transparente, sem exigir o uso de um método de conversão explícita ou de um operador
de conversão.

7 Observação

Embora o código que executa uma conversão implícita possa chamar um método
de conversão ou usar um operador de conversão, seu uso não é exigido pelos
compiladores que oferecem suporte a conversões implícitas.

Por exemplo, o tipo Decimal oferece suporte a conversões implícitas de valores Byte,
Char, Int16, Int32, Int64, SByte, UInt16, UInt32 e UInt64. O exemplo a seguir ilustra
algumas dessas conversões implícitas ao atribuir valores a uma variável Decimal.

C#

byte byteValue = 16;


short shortValue = -1024;
int intValue = -1034000;
long longValue = 1152921504606846976;
ulong ulongValue = UInt64.MaxValue;

decimal decimalValue;

decimalValue = byteValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is
{1}.",
byteValue.GetType().Name, decimalValue);

decimalValue = shortValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is
{1}.",
shortValue.GetType().Name, decimalValue);

decimalValue = intValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is
{1}.",
intValue.GetType().Name, decimalValue);

decimalValue = longValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is
{1}.",
longValue.GetType().Name, decimalValue);

decimalValue = ulongValue;
Console.WriteLine("After assigning a {0} value, the Decimal value is {1}.",
ulongValue.GetType().Name, decimalValue);
// The example displays the following output:
// After assigning a Byte value, the Decimal value is 16.
// After assigning a Int16 value, the Decimal value is -1024.
// After assigning a Int32 value, the Decimal value is -1034000.
// After assigning a Int64 value, the Decimal value is
1152921504606846976.
// After assigning a UInt64 value, the Decimal value is
18446744073709551615.

Se o compilador de uma linguagem específica oferecer suporte a operadores


personalizados, você também poderá definir conversões implícitas em seus próprios
tipos personalizados. O exemplo a seguir oferece uma implementação parcial de um
tipo de dados de byte assinado chamado ByteWithSign que usa a representação de
sinal e magnitude. Ele oferece suporte à conversão implícita dos valores Byte e SByte
para valores ByteWithSign .

C#

public struct ByteWithSign


{
private SByte signValue;
private Byte value;

public static implicit operator ByteWithSign(SByte value)


{
ByteWithSign newValue;
newValue.signValue = (SByte)Math.Sign(value);
newValue.value = (byte)Math.Abs(value);
return newValue;
}

public static implicit operator ByteWithSign(Byte value)


{
ByteWithSign newValue;
newValue.signValue = 1;
newValue.value = value;
return newValue;
}

public override string ToString()


{
return (signValue * value).ToString();
}
}

O código cliente pode então declarar uma variável ByteWithSign e atribuir a ela valores
Byte e SByte sem executar nenhuma conversão explícita ou usar operadores de
conversão, conforme mostrado no exemplo a seguir.

C#

SByte sbyteValue = -120;


ByteWithSign value = sbyteValue;
Console.WriteLine(value);
value = Byte.MaxValue;
Console.WriteLine(value);
// The example displays the following output:
// -120
// 255

Conversão explícita com o operador explícito


As conversões de redução envolvem a criação de um novo valor com o valor de um tipo
existente que tem um intervalo mais amplo ou uma lista de membros maior do que o
tipo de destino. Como uma conversão de redução pode resultar em uma perda de
dados, os compiladores geralmente exigem que a conversão se torne explícita por meio
da chamada de um método de conversão ou de um operador de conversão. Ou seja, a
conversão deve ser tratada explicitamente no código do desenvolvedor.

7 Observação

A finalidade principal de requerer um método de conversão ou de conversão do


operador para reduzir conversões é fazer com que o desenvolvedor saiba sobre a
possibilidade de perda de dados ou de OverflowException para que ela possa ser
tratada no código. No entanto, alguns compiladores podem amenizar esse
requisito. Por exemplo, no Visual Basic, se a Option Strict estiver desativada (sua
configuração padrão), o compilador do Visual Basic tentará executar as conversões
de redução de forma implícita.
Por exemplo, os tipos de dados UInt32, Int64 e UInt64 possuem intervalos que excedem
o do tipo de dados Int32, conforme mostrado na tabela a seguir.

Tipo Comparação com intervalo de Int32

Int64 Int64.MaxValue é maior que Int32.MaxValue, e Int64.MinValue é menor que (possui um


intervalo negativo maior que) Int32.MinValue.

UInt32 UInt32.MaxValue é maior que Int32.MaxValue.

UInt64 UInt64.MaxValue é maior que Int32.MaxValue.

Para tratar essas conversões de redução, o .NET permite que os tipos definam um
operador Explicit . Os compiladores de linguagens individuais podem então
implementar esse operador que usa sua própria sintaxe, ou um membro da classe
Convert pode ser chamado para executar a conversão. (Para obter mais informações
sobre a classe Convert, confira A classe Convert mais adiante neste tópico.) O exemplo a
seguir ilustra o uso de recursos da linguagem para lidar com a conversão explícita
destes valores inteiros potencialmente fora do intervalo para valores Int32.

C#

long number1 = int.MaxValue + 20L;


uint number2 = int.MaxValue - 1000;
ulong number3 = int.MaxValue;

int intNumber;

try
{
intNumber = checked((int)number1);
Console.WriteLine("After assigning a {0} value, the Integer value is
{1}.",
number1.GetType().Name, intNumber);
}
catch (OverflowException)
{
if (number1 > int.MaxValue)
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number1, int.MaxValue);
else
Console.WriteLine("Conversion failed: {0} is less than {1}.",
number1, int.MinValue);
}

try
{
intNumber = checked((int)number2);
Console.WriteLine("After assigning a {0} value, the Integer value is
{1}.",
number2.GetType().Name, intNumber);
}
catch (OverflowException)
{
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number2, int.MaxValue);
}

try
{
intNumber = checked((int)number3);
Console.WriteLine("After assigning a {0} value, the Integer value is
{1}.",
number3.GetType().Name, intNumber);
}
catch (OverflowException)
{
Console.WriteLine("Conversion failed: {0} exceeds {1}.",
number1, int.MaxValue);
}

// The example displays the following output:


// Conversion failed: 2147483667 exceeds 2147483647.
// After assigning a UInt32 value, the Integer value is 2147482647.
// After assigning a UInt64 value, the Integer value is 2147483647.

As conversões explícitas podem produzir resultados diferentes em linguagens


diferentes, e esses resultados podem diferir do valor retornado pelo método Convert
correspondente. Por exemplo, se o valor 12,63251 de Double for convertido em Int32, o
método CInt do Visual Basic e o método Convert.ToInt32(Double) do .NET arredondam
o valor de Double para retornar um valor 13, mas o operador (int) do C# trunca
Double para retornar um valor 12. Da mesma forma, o operador (int) do C# não
oferece suporte à conversão de booliano para inteiro, mas o método CInt do Visual
Basic converte um valor true para -1. Por outro lado, o método
Convert.ToInt32(Boolean) converte um valor true para 1.

A maioria dos compiladores permite que as conversões explícitas sejam executadas de


forma verificada ou não verificada. Quando uma conversão verificada é executada, uma
OverflowException é gerada quando o valor do tipo a ser convertido está fora do
intervalo do tipo de destino. Quando uma conversão não verificada é executada nas
mesmas condições, a conversão pode não gerar uma exceção, mas o comportamento
exato se tornará indefinido e um valor incorreto poderá ser produzido.

7 Observação

No C#, as conversões verificadas podem ser executadas usando a palavra-chave


checked juntamente com um operador de conversão ou por meio da especificação
da opção do compilador /checked+ . Por outro lado, as conversões não verificadas
podem ser executadas usando a palavra-chave unchecked juntamente com o
operador de conversão ou especificando a opção do compilador /checked- . Por
padrão, as conversões explícitas são do tipo não verificadas. No Visual Basic, as
conversões verificadas podem ser executadas desmarcando-se a caixa de seleção
Remover Verificação de Estouro de Inteiro na caixa de diálogo Configurações
Avançadas do Compilador do projeto ou ao especificar a opção /removeintchecks-
do compilador. Do mesmo modo, as conversões não verificadas podem ser
executadas desmarcando-se a caixa de seleção Remover Verificação de Estouro de
Inteiro na caixa de diálogo Configurações Avançadas do Compilador do projeto
ou ao especificar a opção /removeintchecks+ do compilador. Por padrão, as
conversões explícitas são do tipo verificadas.

O exemplo em C# a seguir usa as palavras-chave checked e de unchecked para ilustrar a


diferença no comportamento quando um valor fora do intervalo de Byte é convertido
em Byte. A conversão verificada gera uma exceção, mas a conversão não verificada
atribui Byte.MaxValue à variável Byte.

C#

int largeValue = Int32.MaxValue;


byte newValue;

try
{
newValue = unchecked((byte)largeValue);
Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.",
largeValue.GetType().Name, largeValue,
newValue.GetType().Name, newValue);
}
catch (OverflowException)
{
Console.WriteLine("{0} is outside the range of the Byte data type.",
largeValue);
}

try
{
newValue = checked((byte)largeValue);
Console.WriteLine("Converted the {0} value {1} to the {2} value {3}.",
largeValue.GetType().Name, largeValue,
newValue.GetType().Name, newValue);
}
catch (OverflowException)
{
Console.WriteLine("{0} is outside the range of the Byte data type.",
largeValue);
}
// The example displays the following output:
// Converted the Int32 value 2147483647 to the Byte value 255.
// 2147483647 is outside the range of the Byte data type.

Se o compilador de uma linguagem específica der suporte a operadores


sobrecarregados personalizados, você também poderá definir as conversões explícitas
em seus próprios tipos personalizados. O exemplo a seguir oferece uma implementação
parcial de um tipo de dados de byte assinado chamado ByteWithSign que usa a
representação de sinal e magnitude. Ele oferece suporte à conversão explícita dos
valores Int32 e UInt32 para valores ByteWithSign .

C#

public struct ByteWithSignE


{
private SByte signValue;
private Byte value;

private const byte MaxValue = byte.MaxValue;


private const int MinValue = -1 * byte.MaxValue;

public static explicit operator ByteWithSignE(int value)


{
// Check for overflow.
if (value > ByteWithSignE.MaxValue || value <
ByteWithSignE.MinValue)
throw new OverflowException(String.Format("'{0}' is out of range
of the ByteWithSignE data type.",
value));

ByteWithSignE newValue;
newValue.signValue = (SByte)Math.Sign(value);
newValue.value = (byte)Math.Abs(value);
return newValue;
}

public static explicit operator ByteWithSignE(uint value)


{
if (value > ByteWithSignE.MaxValue)
throw new OverflowException(String.Format("'{0}' is out of range
of the ByteWithSignE data type.",
value));

ByteWithSignE newValue;
newValue.signValue = 1;
newValue.value = (byte)value;
return newValue;
}

public override string ToString()


{
return (signValue * value).ToString();
}
}

O código cliente pode então declarar uma variável ByteWithSign e atribuir a ela valores
Int32 e UInt32 quando as atribuições incluem um operador de conversão ou um
método de conversão, conforme mostrado no exemplo a seguir.

C#

ByteWithSignE value;

try
{
int intValue = -120;
value = (ByteWithSignE)intValue;
Console.WriteLine(value);
}
catch (OverflowException e)
{
Console.WriteLine(e.Message);
}

try
{
uint uintValue = 1024;
value = (ByteWithSignE)uintValue;
Console.WriteLine(value);
}
catch (OverflowException e)
{
Console.WriteLine(e.Message);
}
// The example displays the following output:
// -120
// '1024' is out of range of the ByteWithSignE data type.

A interface IConvertible
Para oferecer suporte à conversão de qualquer tipo em um tipo base do Common
Language Runtime, o .NET oferece a interface IConvertible. O tipo de implementação é
necessário para fornecer o seguinte:

Um método que retorne o TypeCode do tipo implementando.

Métodos para converter o tipo implementando em cada tipo base do Common


Language Runtime (Boolean, Byte, DateTime, Decimal, Double e assim por diante).
Um método de conversão generalizado para converter uma instância do tipo de
implementação em outro tipo especificado. As conversões sem suporte devem
gerar uma InvalidCastException.

Cada tipo base do Common Language Runtime (ou seja, Boolean, Byte, Char, DateTime,
Decimal, Double, Int16, Int32, Int64, SByte, Single, String, UInt16, UInt32, e UInt64), bem
como os tipos DBNull e Enum, implementam a interface IConvertible. No entanto, essas
são implementações de interfaces explícitas; o método de conversão pode ser chamado
somente com uma variável da interface IConvertible, conforme mostrado no exemplo a
seguir. Este exemplo converte um valor Int32 para seu valor Char equivalente.

C#

int codePoint = 1067;


IConvertible iConv = codePoint;
char ch = iConv.ToChar(null);
Console.WriteLine("Converted {0} to {1}.", codePoint, ch);

O requisito para chamar o método de conversão em sua interface, em vez de no tipo de


implementação, torna as implementações de interfaces explícitas relativamente
dispendiosas. Em vez disso, recomendamos chamar o membro apropriado da classe
Convert para fazer a conversão entre tipos base do Common Language Runtime. Para
obter mais informações, consulte a próxima seção A classe Convert.

7 Observação

Além da interface IConvertible e da classe Convert fornecidas pelo .NET, as


linguagens individuais também podem fornecer formas de executar conversões.
Por exemplo, o C# usa operadores de conversão e Visual Basic usa funções de
conversão implementadas no compilador, como CType , CInt e DirectCast .

Em geral, a interface IConvertible é criada para dar suporte à conversão entre os tipos
base no .NET. No entanto, a interface também pode ser implementada por um tipo
personalizado para oferecer suporte à conversão desse tipo em outros tipos
personalizados. Para obter mais informações, consulte a seção Conversões
personalizadas com o método ChangeType mais adiante neste tópico.

A classe Convert
Embora a implementação da interface IConvertible de cada tipo de base possa ser
chamada para executar uma conversão de tipo, chamar os métodos da classe
System.Convert é a maneira recomendada independente de linguagem de converter de
um tipo base para outro. Além disso, o método Convert.ChangeType(Object, Type,
IFormatProvider) pode ser usado para converter de um tipo personalizado especificado
para outro tipo.

Conversões entre tipos base


A classe Convert fornece uma maneira independente de linguagem para realizar
conversões entre tipos base e está disponível para todas as linguagens que visam o
Common Language Runtime. Ele fornece um conjunto completo de métodos para
ampliar e reduzir conversões, e gera uma InvalidCastException para as conversões que
não têm suporte (como a conversão de um valor DateTime para um valor inteiro). As
conversões de redução são executadas em um contexto verificado, e uma
OverflowException será gerada se a conversão falhar.

) Importante

Como a classe Convert inclui métodos para converter de/para cada tipo base, ela
elimina a necessidade de chamar a implementação de interface explicita
IConvertible de cada tipo base.

O exemplo a seguir ilustra o uso da classe System.Convert para executar várias


conversões de ampliação e redução entre os tipos base do .NET.

C#

// Convert an Int32 value to a Decimal (a widening conversion).


int integralValue = 12534;
decimal decimalValue = Convert.ToDecimal(integralValue);
Console.WriteLine("Converted the {0} value {1} to " +
"the {2} value {3:N2}.",
integralValue.GetType().Name,
integralValue,
decimalValue.GetType().Name,
decimalValue);
// Convert a Byte value to an Int32 value (a widening conversion).
byte byteValue = Byte.MaxValue;
int integralValue2 = Convert.ToInt32(byteValue);
Console.WriteLine("Converted the {0} value {1} to " +
"the {2} value {3:G}.",
byteValue.GetType().Name,
byteValue,
integralValue2.GetType().Name,
integralValue2);

// Convert a Double value to an Int32 value (a narrowing conversion).


double doubleValue = 16.32513e12;
try
{
long longValue = Convert.ToInt64(doubleValue);
Console.WriteLine("Converted the {0} value {1:E} to " +
"the {2} value {3:N0}.",
doubleValue.GetType().Name,
doubleValue,
longValue.GetType().Name,
longValue);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert the {0:E} value {1}.",
doubleValue.GetType().Name,
doubleValue);
}

// Convert a signed byte to a byte (a narrowing conversion).


sbyte sbyteValue = -16;
try
{
byte byteValue2 = Convert.ToByte(sbyteValue);
Console.WriteLine("Converted the {0} value {1} to " +
"the {2} value {3:G}.",
sbyteValue.GetType().Name,
sbyteValue,
byteValue2.GetType().Name,
byteValue2);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert the {0} value {1}.",
sbyteValue.GetType().Name,
sbyteValue);
}
// The example displays the following output:
// Converted the Int32 value 12534 to the Decimal value 12,534.00.
// Converted the Byte value 255 to the Int32 value 255.
// Converted the Double value 1.632513E+013 to the Int64 value
16,325,130,000,000.
// Unable to convert the SByte value -16.

Em alguns casos, particularmente na conversão de/para valores de ponto flutuante, uma


conversão pode envolver uma perda de precisão, mesmo que não gere uma
OverflowException. O exemplo a seguir ilustra essa perda de precisão. No primeiro caso,
um valor Decimal tem menos precisão (menos dígitos significativos) quando é
convertida em Double. No segundo caso, um valor Double é arredondado de 42,72 para
43 para concluir a conversão.

C#
double doubleValue;

// Convert a Double to a Decimal.


decimal decimalValue = 13956810.96702888123451471211m;
doubleValue = Convert.ToDouble(decimalValue);
Console.WriteLine("{0} converted to {1}.", decimalValue, doubleValue);

doubleValue = 42.72;
try
{
int integerValue = Convert.ToInt32(doubleValue);
Console.WriteLine("{0} converted to {1}.",
doubleValue, integerValue);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to an integer.",
doubleValue);
}
// The example displays the following output:
// 13956810.96702888123451471211 converted to 13956810.9670289.
// 42.72 converted to 43.

Para uma tabela que lista as conversões de ampliação e redução aceitas pela classe
Convert, confira Tabelas de conversão de tipos.

Conversões personalizadas com o método ChangeType


Além de oferecer suporte a conversões para cada um dos tipos base, a classe Convert
pode ser usada para converter um tipo personalizado em um ou mais tipos
predefinidos. Essa conversão é executada pelo método Convert.ChangeType(Object,
Type, IFormatProvider), que por sua vez, envolve uma chamada ao método
IConvertible.ToType do parâmetro value . Isso significa que o objeto representado pelo
parâmetro value deve fornecer uma implementação da interface IConvertible.

7 Observação

Como os métodos Convert.ChangeType(Object, Type) e


Convert.ChangeType(Object, Type, IFormatProvider) usam um objeto Type para
especificar o tipo de destino no qual value é convertido, eles podem ser usados
para executar uma conversão dinâmica para um objeto cujo tipo não é conhecido
em tempo de compilação. No entanto, observe que a implementação IConvertible
de value deve oferecer suporte a essa conversão.
O exemplo a seguir ilustra uma possível implementação da interface IConvertible que
permite que um objeto TemperatureCelsius seja convertido para um objeto
TemperatureFahrenheit e vice-versa. O exemplo define uma classe base, Temperature , a

qual implementa a interface IConvertible e substitui o método Object.ToString. As


classes TemperatureCelsius e TemperatureFahrenheit derivadas substituem os métodos
ToType e ToString da classe base.

C#

using System;

public abstract class Temperature : IConvertible


{
protected decimal temp;

public Temperature(decimal temperature)


{
this.temp = temperature;
}

public decimal Value


{
get { return this.temp; }
set { this.temp = value; }
}

public override string ToString()


{
return temp.ToString(null as IFormatProvider) + "º";
}

// IConvertible implementations.
public TypeCode GetTypeCode()
{
return TypeCode.Object;
}

public bool ToBoolean(IFormatProvider provider)


{
throw new InvalidCastException(String.Format("Temperature-to-Boolean
conversion is not supported."));
}

public byte ToByte(IFormatProvider provider)


{
if (temp < Byte.MinValue || temp > Byte.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the Byte data type.", temp));
else
return (byte)temp;
}
public char ToChar(IFormatProvider provider)
{
throw new InvalidCastException("Temperature-to-Char conversion is
not supported.");
}

public DateTime ToDateTime(IFormatProvider provider)


{
throw new InvalidCastException("Temperature-to-DateTime conversion
is not supported.");
}

public decimal ToDecimal(IFormatProvider provider)


{
return temp;
}

public double ToDouble(IFormatProvider provider)


{
return (double)temp;
}

public short ToInt16(IFormatProvider provider)


{
if (temp < Int16.MinValue || temp > Int16.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the Int16 data type.", temp));
else
return (short)Math.Round(temp);
}

public int ToInt32(IFormatProvider provider)


{
if (temp < Int32.MinValue || temp > Int32.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the Int32 data type.", temp));
else
return (int)Math.Round(temp);
}

public long ToInt64(IFormatProvider provider)


{
if (temp < Int64.MinValue || temp > Int64.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the Int64 data type.", temp));
else
return (long)Math.Round(temp);
}

public sbyte ToSByte(IFormatProvider provider)


{
if (temp < SByte.MinValue || temp > SByte.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the SByte data type.", temp));
else
return (sbyte)temp;
}

public float ToSingle(IFormatProvider provider)


{
return (float)temp;
}

public virtual string ToString(IFormatProvider provider)


{
return temp.ToString(provider) + "°";
}

// If conversionType is implemented by another IConvertible method, call


it.
public virtual object ToType(Type conversionType, IFormatProvider
provider)
{
switch (Type.GetTypeCode(conversionType))
{
case TypeCode.Boolean:
return this.ToBoolean(provider);
case TypeCode.Byte:
return this.ToByte(provider);
case TypeCode.Char:
return this.ToChar(provider);
case TypeCode.DateTime:
return this.ToDateTime(provider);
case TypeCode.Decimal:
return this.ToDecimal(provider);
case TypeCode.Double:
return this.ToDouble(provider);
case TypeCode.Empty:
throw new NullReferenceException("The target type is
null.");
case TypeCode.Int16:
return this.ToInt16(provider);
case TypeCode.Int32:
return this.ToInt32(provider);
case TypeCode.Int64:
return this.ToInt64(provider);
case TypeCode.Object:
// Leave conversion of non-base types to derived classes.
throw new InvalidCastException(String.Format("Cannot convert
from Temperature to {0}.",
conversionType.Name));
case TypeCode.SByte:
return this.ToSByte(provider);
case TypeCode.Single:
return this.ToSingle(provider);
case TypeCode.String:
IConvertible iconv = this;
return iconv.ToString(provider);
case TypeCode.UInt16:
return this.ToUInt16(provider);
case TypeCode.UInt32:
return this.ToUInt32(provider);
case TypeCode.UInt64:
return this.ToUInt64(provider);
default:
throw new InvalidCastException("Conversion not supported.");
}
}

public ushort ToUInt16(IFormatProvider provider)


{
if (temp < UInt16.MinValue || temp > UInt16.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the UInt16 data type.", temp));
else
return (ushort)Math.Round(temp);
}

public uint ToUInt32(IFormatProvider provider)


{
if (temp < UInt32.MinValue || temp > UInt32.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the UInt32 data type.", temp));
else
return (uint)Math.Round(temp);
}

public ulong ToUInt64(IFormatProvider provider)


{
if (temp < UInt64.MinValue || temp > UInt64.MaxValue)
throw new OverflowException(String.Format("{0} is out of range
of the UInt64 data type.", temp));
else
return (ulong)Math.Round(temp);
}
}

public class TemperatureCelsius : Temperature, IConvertible


{
public TemperatureCelsius(decimal value) : base(value)
{
}

// Override ToString methods.


public override string ToString()
{
return this.ToString(null);
}

public override string ToString(IFormatProvider provider)


{
return temp.ToString(provider) + "°C";
}

// If conversionType is a implemented by another IConvertible method,


call it.
public override object ToType(Type conversionType, IFormatProvider
provider)
{
// For non-objects, call base method.
if (Type.GetTypeCode(conversionType) != TypeCode.Object)
{
return base.ToType(conversionType, provider);
}
else
{
if (conversionType.Equals(typeof(TemperatureCelsius)))
return this;
else if (conversionType.Equals(typeof(TemperatureFahrenheit)))
return new TemperatureFahrenheit((decimal)this.temp * 9 / 5
+ 32);
else
throw new InvalidCastException(String.Format("Cannot convert
from Temperature to {0}.",
conversionType.Name));
}
}
}

public class TemperatureFahrenheit : Temperature, IConvertible


{
public TemperatureFahrenheit(decimal value) : base(value)
{
}

// Override ToString methods.


public override string ToString()
{
return this.ToString(null);
}

public override string ToString(IFormatProvider provider)


{
return temp.ToString(provider) + "°F";
}

public override object ToType(Type conversionType, IFormatProvider


provider)
{
// For non-objects, call base method.
if (Type.GetTypeCode(conversionType) != TypeCode.Object)
{
return base.ToType(conversionType, provider);
}
else
{
// Handle conversion between derived classes.
if (conversionType.Equals(typeof(TemperatureFahrenheit)))
return this;
else if (conversionType.Equals(typeof(TemperatureCelsius)))
return new TemperatureCelsius((decimal)(this.temp - 32) * 5
/ 9);
// Unspecified object type: throw an InvalidCastException.
else
throw new InvalidCastException(String.Format("Cannot convert
from Temperature to {0}.",
conversionType.Name));
}
}
}

O exemplo a seguir ilustra várias chamadas às implementações de IConvertible para


converter objetos TemperatureCelsius para objetos TemperatureFahrenheit e vice-versa.

C#

TemperatureCelsius tempC1 = new TemperatureCelsius(0);


TemperatureFahrenheit tempF1 =
(TemperatureFahrenheit)Convert.ChangeType(tempC1,
typeof(TemperatureFahrenheit), null);
Console.WriteLine("{0} equals {1}.", tempC1, tempF1);
TemperatureCelsius tempC2 = (TemperatureCelsius)Convert.ChangeType(tempC1,
typeof(TemperatureCelsius), null);
Console.WriteLine("{0} equals {1}.", tempC1, tempC2);
TemperatureFahrenheit tempF2 = new TemperatureFahrenheit(212);
TemperatureCelsius tempC3 = (TemperatureCelsius)Convert.ChangeType(tempF2,
typeof(TemperatureCelsius), null);
Console.WriteLine("{0} equals {1}.", tempF2, tempC3);
TemperatureFahrenheit tempF3 =
(TemperatureFahrenheit)Convert.ChangeType(tempF2,
typeof(TemperatureFahrenheit), null);
Console.WriteLine("{0} equals {1}.", tempF2, tempF3);
// The example displays the following output:
// 0°C equals 32°F.
// 0°C equals 0°C.
// 212°F equals 100°C.
// 212°F equals 212°F.

A classe TypeConverter
O .NET também permite que você defina um conversor de tipo para um tipo
personalizado ao estender a classe System.ComponentModel.TypeConverter e associar
o conversor de tipo ao tipo por meio de um atributo
System.ComponentModel.TypeConverterAttribute. A tabela a seguir realça as diferenças
entre esta abordagem e a implementação da interface IConvertible para um tipo
personalizado.
7 Observação

O suporte no tempo de design somente poderá ser fornecido para um tipo


personalizado se houver um conversor de tipo definido para ele.

Conversão usando TypeConverter Conversão usando


IConvertible

É implementado para um tipo personalizado por meio da É implementada por um tipo


derivação de uma classe separada de TypeConverter. Essa personalizado para executar a
classe derivada é associada ao tipo personalizado aplicando- conversão. Um usuário do tipo
se um atributo TypeConverterAttribute. invoca um método de conversão
IConvertible no tipo.

Pode ser usada tanto no tempo de design quanto no tempo Somente pode ser usada no
de execução. tempo de execução.

Usa reflexão; consequentemente, é mais lento do que a Não usa reflexão.


conversão habilitada por IConvertible.

Permite conversões de tipo bidirecionais do tipo Permite a conversão de um tipo


personalizado para outros tipos de dados e de outros tipos de personalizado em outros tipos
dados para o tipo personalizado. Por exemplo, um de dados, mas não de outros
TypeConverter definido para MyType permite conversões de tipos de dados em um tipo
MyType para String e de String para MyType . personalizado.

Para obter mais informações sobre como usar conversores de tipo para executar
conversões, consulte System.ComponentModel.TypeConverter.

Confira também
System.Convert
IConvertible
Tabelas de conversão de tipos
Tabelas de conversão de tipos em .NET
Artigo • 10/05/2023

Conversões de expansão ocorrem quando um valor de um tipo é convertido em outro


tipo de tamanho igual ou maior. Conversões de redução ocorrem quando um valor de
um tipo é convertido em um valor de outro tipo de tamanho menor. As tabelas neste
tópico ilustram os comportamentos exibidos por ambos os tipos de conversões.

Conversões de expansão
A tabela a seguir descreve as conversões de expansão que podem ser executadas sem
perda de informações.

Tipo Pode ser convertido sem perda de dados para

Byte UInt16, Int16, UInt32, Int32, UInt64, Int64, Single, Double, Decimal

SByte Int16, Int32, Int64, Single, Double, Decimal

Int16 Int32, Int64, Single, Double, Decimal

UInt16 UInt32, Int32, UInt64, Int64, Single, Double, Decimal

Char UInt16, UInt32, Int32, UInt64, Int64, Single, Double, Decimal

Int32 Int64, Double, Decimal

UInt32 Int64, UInt64, Double, Decimal

Int64 Decimal

UInt64 Decimal

Single Double

Algumas conversões de expansão para Single ou Double podem causar perda de


precisão. A tabela a seguir descreve as conversões de expansão que, às vezes, resultam
em perda de informações.

Tipo Pode ser convertido para

Int32 Single

UInt32 Single

Int64 Single, Double


Tipo Pode ser convertido para

UInt64 Single, Double

Decimal Single, Double

Conversões de redução
Uma conversão de redução para Single ou Double pode causar perda de informações.
Se o tipo de destino não puder expressar corretamente a magnitude da origem, o tipo
resultante será definido como a constante PositiveInfinity ou NegativeInfinity .
PositiveInfinity resulta da divisão de um número positivo por zero, e também é
retornado quando o valor de um Single ou Double ultrapassar o valor do campo
MaxValue . NegativeInfinity resulta da divisão de um número negativo por zero, e

também é retornado quando o valor de um Single ou Double fica abaixo do valor do


campo MinValue . Uma conversão de um Double em um Single pode resultar em
PositiveInfinity ou NegativeInfinity .

Uma conversão de redução também pode resultar em perda de informações para


outros tipos de dados. No entanto, OverflowException será lançada se o valor de um
tipo que está sendo convertido ficar fora do intervalo especificado pelos campos
MaxValue e MinValue do tipo de destino, e a conversão será verificada pelo runtime para

garantir que o valor do tipo de destino não ultrapasse MaxValue ou MinValue .


Conversões executadas com a classe System.Convert sempre são verificadas dessa
maneira.

A tabela a seguir lista conversões que lançam OverflowException usando


System.Convert ou qualquer conversão selecionada se o valor do tipo que está sendo
convertido estiver fora do intervalo definido pelo tipo resultante.

Tipo Pode ser convertido para

Byte SByte

SByte Byte, UInt16, UInt32, UInt64

Int16 Byte, SByte, UInt16

UInt16 Byte, SByte, Int16

Int32 Byte, SByte, Int16, UInt16,UInt32

UInt32 Byte, SByte, Int16, UInt16, Int32


Tipo Pode ser convertido para

Int64 Byte, SByte, Int16, UInt16, Int32,UInt32,UInt64

UInt64 Byte, SByte, Int16, UInt16, Int32, UInt32, Int64

Decimal Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64

Single Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64

Double Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64

Confira também
System.Convert
Conversão de tipo no .NET
System.Convert classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A classe estática Convert contém métodos que são usados principalmente para oferecer
suporte à conversão de e para os tipos de dados base no .NET. Os tipos base
suportados são Boolean, , , , , , ,
UInt32SByteInt32Int64Int16UInt16UInt64ByteCharDoubleDecimalSingleDateTime e
.String Além disso, a classe inclui métodos para oferecer suporte a Convert outros tipos
de conversões.

Conversões de e para tipos de base


Existe um método de conversão para converter cada tipo base em qualquer outro tipo
base. No entanto, a chamada real para um método de conversão específico pode
produzir um dos cinco resultados, dependendo do valor do tipo base em tempo de
execução e do tipo base de destino. Esses cinco resultados são:

Sem conversão. Isso ocorre quando uma tentativa é feita para converter de um
tipo para si mesmo (por exemplo, chamando Convert.ToInt32(Int32) com um
argumento do tipo Int32). Nesse caso, o método simplesmente retorna uma
instância do tipo original.

Um InvalidCastException. Isso ocorre quando uma conversão específica não é


suportada. Um InvalidCastException é lançado para as seguintes conversões:
Conversões de Char para Boolean, , , Decimal, SingleDoubleou DateTime.
Conversões de Boolean, , , Decimal, SingleDoubleou DateTime para Char.
Conversões de DateTime para qualquer outro tipo, exceto String.
Conversões de qualquer outro tipo, exceto String, para DateTime.

Um FormatException. Isso ocorre quando a tentativa de converter um valor de


cadeia de caracteres para qualquer outro tipo base falha porque a cadeia de
caracteres não está no formato adequado. A exceção é lançada para as seguintes
conversões:
Uma cadeia de caracteres a ser convertida em um Boolean valor não é igual
Boolean.TrueString a ou Boolean.FalseString.
Uma cadeia de caracteres a ser convertida em um Char valor consiste em vários
caracteres.
Uma cadeia de caracteres a ser convertida em qualquer tipo numérico não é
reconhecida como um número válido.
Uma cadeia de caracteres a ser convertida em um não é reconhecida como um
DateTime valor de data e hora válido.

Uma conversão bem-sucedida. Para conversões entre dois tipos de base diferentes
não listados nos resultados anteriores, todas as conversões de ampliação, bem
como todas as conversões de estreitamento que não resultam em perda de dados,
serão bem-sucedidas e o método retornará um valor do tipo base de destino.

Um OverflowException. Isso ocorre quando uma conversão de estreitamento


resulta em uma perda de dados. Por exemplo, tentar converter uma Int32 instância
cujo valor é 10000 em um tipo lança um ByteOverflowException porque 10000
está fora do intervalo do tipo de Byte dados.

Uma exceção não será lançada se a conversão de um tipo numérico resultar em uma
perda de precisão (ou seja, a perda de alguns dígitos menos significativos). No entanto,
uma exceção será lançada se o resultado for maior do que pode ser representado pelo
tipo de valor de retorno do método de conversão específico.

Por exemplo, quando um é convertido em um DoubleSingle, pode ocorrer uma perda


de precisão, mas nenhuma exceção é lançada. No entanto, se a magnitude do for muito
grande para ser representada por um Single, uma exceção de Double estouro é lançada.

Números não decimais


A Convert classe inclui métodos estáticos que você pode chamar para converter valores
integrais em representações de cadeia de caracteres não decimais e para converter
cadeias de caracteres que representam números não decimais em valores integrais.
Cada um desses métodos de conversão inclui um base argumento que permite
especificar o sistema numérico: binário (base 2), octal (base 8) e hexadecimal (base 16),
bem como decimal (base 10). Há um conjunto de métodos para converter cada um dos
tipos integrais primitivos compatíveis com CLS em uma cadeia de caracteres e um para
converter uma cadeia de caracteres em cada um dos tipos integrais primitivos:

ToString(Byte, Int32) e , para converter um valor de byte de e ToByte(String,


Int32)para uma cadeia de caracteres em uma base especificada.

ToString(Int16, Int32) e , para converter um inteiro assinado de 16 bits de e


ToInt16(String, Int32)para uma cadeia de caracteres em uma base especificada.

ToString(Int32, Int32) e , para converter um inteiro assinado de 32 bits de e


ToInt32(String, Int32)para uma cadeia de caracteres em uma base especificada.
ToString(Int64, Int32) e , para converter um inteiro assinado de 64 bits de e
ToInt64(String, Int32)para uma cadeia de caracteres em uma base especificada.

ToSByte(String, Int32), para converter a representação de cadeia de caracteres de


um valor de byte em um formato especificado em um byte assinado.

ToUInt16(String, Int32), para converter a representação de cadeia de caracteres de


um inteiro em um formato especificado em um inteiro de 16 bits não assinado.

ToUInt32(String, Int32), para converter a representação de cadeia de caracteres de


um inteiro em um formato especificado em um inteiro de 32 bits não assinado.

ToUInt64(String, Int32), para converter a representação de cadeia de caracteres de


um inteiro em um formato especificado em um inteiro de 64 bits não assinado.

O exemplo a seguir converte o valor de em uma cadeia de Int16.MaxValue caracteres


em todos os formatos numéricos com suporte. Em seguida, converte o valor da cadeia
de caracteres de volta em um Int16 valor.

C#

using System;

public class Example


{
public static void Main()
{
int[] baseValues = { 2, 8, 10, 16 };
short value = Int16.MaxValue;
foreach (var baseValue in baseValues) {
String s = Convert.ToString(value, baseValue);
short value2 = Convert.ToInt16(s, baseValue);

Console.WriteLine("{0} --> {1} (base {2}) --> {3}",


value, s, baseValue, value2);
}
}
}
// The example displays the following output:
// 32767 --> 111111111111111 (base 2) --> 32767
// 32767 --> 77777 (base 8) --> 32767
// 32767 --> 32767 (base 10) --> 32767
// 32767 --> 7fff (base 16) --> 32767

Conversões de objetos personalizados para


tipos base
Além de oferecer suporte a conversões entre os tipos base, o Convert método oferece
suporte à conversão de qualquer tipo personalizado em qualquer tipo base. Para fazer
isso, o tipo personalizado deve implementar a IConvertible interface, que define
métodos para converter o tipo de implementação para cada um dos tipos base. As
conversões que não são suportadas por um tipo específico devem lançar um
InvalidCastExceptionarquivo .

Quando o método é passado um tipo personalizado como seu primeiro parâmetro, ou


quando o método Type (como ou Convert.ToDouble(Object, IFormatProvider) é chamado
e passado uma instância de um tipo personalizado como Convert.ToInt32(Object) seu
primeiro parâmetro, o ConvertChangeType Convert.To método, por sua vez, chama a
implementação do IConvertible tipo personalizado para executar a conversão. Para
obter mais informações, confira Conversão de tipo no .NET.

Informações de formatação específicas da


cultura
Todos os métodos de conversão de tipo base e o ChangeType método incluem
sobrecargas que têm um parâmetro do tipo IFormatProvider. Por exemplo, o
Convert.ToBoolean método tem as duas sobrecargas a seguir:

Convert.ToBoolean(Object, IFormatProvider)
Convert.ToBoolean(String, IFormatProvider)

O IFormatProvider parâmetro pode fornecer informações de formatação específicas da


cultura para auxiliar o processo de conversão. No entanto, ele é ignorado pela maioria
dos métodos de conversão de tipo base. Ele é usado apenas pelos seguintes métodos
de conversão de tipo base. Se um null IFormatProvider argumento for passado para
esses métodos, o CultureInfo objeto que representa a cultura atual será usado.

Por métodos que convertem um valor em um tipo numérico. O IFormatProvider


parâmetro é usado pela sobrecarga que tem parâmetros do tipo String e
IFormatProvider. Ele também é usado pela sobrecarga que tem parâmetros do tipo
e IFormatProvider se o tipo Object de tempo de execução do objeto for um
Stringarquivo .

Por métodos que convertem um valor em data e hora. O IFormatProvider


parâmetro é usado pela sobrecarga que tem parâmetros do tipo String e
IFormatProvider. Ele também é usado pela sobrecarga que tem parâmetros do tipo
e IFormatProvider se o tipo Object de tempo de execução do objeto for um
Stringarquivo .
Pelas Convert.ToString sobrecargas que incluem um parâmetro e que convertem
um valor numérico em uma cadeia de caracteres ou um DateTimeIFormatProvider
valor em uma cadeia de caracteres.

No entanto, qualquer tipo definido pelo usuário que implementa IConvertible pode
fazer uso do IFormatProvider parâmetro.

Codificação Base64
A codificação Base64 converte dados binários em uma cadeia de caracteres. Os dados
expressos como dígitos de base 64 podem ser facilmente transmitidos através de canais
de dados que só podem transmitir caracteres de 7 bits. A Convert classe inclui os
seguintes métodos para oferecer suporte à codificação base64: Um conjunto de
métodos oferece suporte à conversão de uma matriz de bytes de e para um String ou
para e de uma matriz de caracteres Unicode que consiste em caracteres de dígitos de
base 64.

ToBase64String, que converte uma matriz de bytes em uma cadeia de caracteres


codificada em base64.
ToBase64CharArray, que converte uma matriz de bytes em uma matriz de
caracteres codificada em base64.
FromBase64String, que converte uma cadeia de caracteres codificada em base64
em uma matriz de bytes.
FromBase64CharArray, que converte uma matriz de caracteres codificada em
base64 em uma matriz de bytes.

Outras conversões comuns


Você pode usar outras classes .NET para executar algumas conversões que não são
suportadas pelos métodos estáticos da Convert classe. Estão incluídos:

Conversão em matrizes de bytes

A BitConverter classe fornece métodos que convertem os tipos numéricos


primitivos (incluindo Boolean) em matrizes de bytes e de matrizes de bytes de
volta para tipos de dados primitivos.

Codificação e decodificação de caracteres

A Encoding classe e suas classes derivadas (como UnicodeEncoding e ) fornecem


métodos para codificar uma matriz de caracteres ou uma cadeia de caracteres (ou
seja, convertê-los em uma matriz de bytes em uma codificação específica) e
UTF8Encodingdecodificar uma matriz de bytes codificada (ou seja, converter uma
matriz de bytes de volta para caracteres Unicode codificados em UTF16). Para
obter mais informações, consulte Codificação de caracteres no .NET.

Exemplos
O exemplo a seguir demonstra alguns dos métodos de conversão na Convert classe,
incluindo ToInt32, ToBooleane ToString.

C#

double dNumber = 23.15;

try {
// Returns 23
int iNumber = System.Convert.ToInt32(dNumber);
}
catch (System.OverflowException) {
System.Console.WriteLine(
"Overflow in double to int conversion.");
}
// Returns True
bool bNumber = System.Convert.ToBoolean(dNumber);

// Returns "23.15"
string strNumber = System.Convert.ToString(dNumber);

try {
// Returns '2'
char chrNumber = System.Convert.ToChar(strNumber[0]);
}
catch (System.ArgumentNullException) {
System.Console.WriteLine("String is null");
}
catch (System.FormatException) {
System.Console.WriteLine("String length is greater than 1.");
}

// System.Console.ReadLine() returns a string and it


// must be converted.
int newInteger = 0;
try {
System.Console.WriteLine("Enter an integer:");
newInteger = System.Convert.ToInt32(
System.Console.ReadLine());
}
catch (System.ArgumentNullException) {
System.Console.WriteLine("String is null.");
}
catch (System.FormatException) {
System.Console.WriteLine("String does not consist of an " +
"optional sign followed by a series of digits.");
}
catch (System.OverflowException) {
System.Console.WriteLine(
"Overflow in string to int conversion.");
}

System.Console.WriteLine("Your integer as a double is {0}",


System.Convert.ToDouble(newInteger));

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como escolher entre tipos anônimos e
tipos de tupla
Artigo • 09/03/2023

Escolher o tipo apropriado passa por considerar sua usabilidade, desempenho e


compensações em comparação com outros tipos. Tipos anônimos estão disponíveis
desde o C# 3.0, enquanto tipos genéricos System.Tuple<T1,T2> foram introduzidos com
.NET Framework 4.0. Desde então, novas opções foram introduzidas com suporte ao
nível da linguagem, como System.ValueTuple<T1,T2> - que, como o nome indica,
fornecem um tipo de valor com a flexibilidade de tipos anônimos. Neste artigo, você
aprenderá quando é apropriado escolher um tipo ou outro.

Usabilidade e funcionalidade
Tipos anônimos foram introduzidos no C# 3.0 com expressões LINQ (consulta integrada
à linguagem). Com o LINQ, os desenvolvedores geralmente projetam resultados de
consultas em tipos anônimos que contêm algumas propriedades selecionadas dos
objetos com os quais estão trabalhando. Considere o exemplo a seguir, que cria uma
instância de uma matriz de objetos DateTime e itera por meio deles, projetando em um
tipo anônimo com duas propriedades.

C#

var dates = new[]


{
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
DateTime.UtcNow.AddHours(1),
};

foreach (var anonymous in


dates.Select(
date => new { Formatted = $"{date:MMM dd, yyyy hh:mm zzz}",
date.Ticks }))
{
Console.WriteLine($"Ticks: {anonymous.Ticks}, formatted:
{anonymous.Formatted}");
}

Tipos anônimos são instanciados usando o operador new e os nomes e tipos de


propriedade são inferidos da declaração. Se dois ou mais inicializadores de objeto
anônimos em um assembly especificarem uma sequência de propriedades que estão na
mesma ordem e que têm os mesmos nomes e tipos, o compilador tratará os objetos
como instâncias do mesmo tipo. Eles compartilham o mesmo tipo de informação
gerado pelo compilador.

O snippet de C# anterior projeta um tipo anônimo com duas propriedades, assim como
a seguinte classe C# gerada pelo compilador:

C#

internal sealed class f__AnonymousType0


{
public string Formatted { get; }
public long Ticks { get; }

public f__AnonymousType0(string formatted, long ticks)


{
Formatted = formatted;
Ticks = ticks;
}
}

Para obter mais informações, consulte Tipos anônimos. A mesma funcionalidade existe
com tuplas ao projetar em consultas LINQ. Você pode selecionar propriedades em
tuplas. Essas tuplas fluem pela consulta, assim como os tipos anônimos o fazem.
Considere agora o seguinte exemplo usando System.Tuple<string, long> .

C#

var dates = new[]


{
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
DateTime.UtcNow.AddHours(1),
};

foreach (var tuple in


dates.Select(
date => new Tuple<string, long>($"{date:MMM dd, yyyy hh:mm
zzz}", date.Ticks)))
{
Console.WriteLine($"Ticks: {tuple.Item2}, formatted: {tuple.Item1}");
}

Com System.Tuple<T1,T2>, a instância expõe propriedades de item numerados, como


Item1 e Item2 . Esses nomes de propriedade podem tornar mais difícil entender a
intenção dos valores da propriedade, pois o nome da propriedade fornece apenas o
ordinal. Além disso, os tipos System.Tuple são tipos de referência class . No entanto,
System.ValueTuple<T1,T2> é um tipo de valor struct . O snippet C# a seguir usa
ValueTuple<string, long> para projetar. Ao fazer isso, ele atribui o uso de uma sintaxe
literal.

C#

var dates = new[]


{
DateTime.UtcNow.AddHours(-1),
DateTime.UtcNow,
DateTime.UtcNow.AddHours(1),
};

foreach (var (formatted, ticks) in


dates.Select(
date => (Formatted: $"{date:MMM dd, yyyy at hh:mm zzz}",
date.Ticks)))
{
Console.WriteLine($"Ticks: {ticks}, formatted: {formatted}");
}

Para obter mais informações sobre tuplas, consulte Tipos de tupla (referência de C#) ou
Tuplas (Visual Basic).

Os exemplos anteriores são todos funcionalmente equivalentes; no entanto, há


pequenas diferenças em sua usabilidade e suas implementações subjacentes.

Compensações
Você pode querer usar sempre ValueTuple em vez de Tuple e tipos anônimos, mas há
compensações que você deve considerar. Os tipos ValueTuple são mutáveis, enquanto
Tuple são somente leitura. Tipos anônimos podem ser usados em árvores de expressão,
enquanto tuplas não podem. A tabela a seguir é uma visão geral de algumas das
principais diferenças.

Principais diferenças

Nome Modificador Tipo Nome do membro Suporte à Suporte à


de acesso personalizado desconstrução árvore de
expressão

Tipos internal class ✔️ ❌ ✔️


anônimos

Tuple public class ❌ ❌ ✔️


Nome Modificador Tipo Nome do membro Suporte à Suporte à
de acesso personalizado desconstrução árvore de
expressão

ValueTuple public struct ✔️ ✔️ ❌

Serialização
Uma consideração importante ao escolher um tipo é se ele precisará ou não ser
serializado. A serialização é o processo de conversão do estado de um objeto em um
formulário que possa ser persistido ou transportado. Para obter mais informações,
consulte serialização. Quando a serialização é importante, a criação de um class ou
struct é preferível em relação a tipos anônimos ou tipos de tupla.

Desempenho
O desempenho entre esses tipos depende do cenário. O maior impacto envolve a
compensação entre alocações e cópia. Na maioria dos cenários, o impacto é pequeno.
Quando grandes impactos podem surgir, devem ser tomadas medidas para informar a
decisão.

Conclusão
Como um desenvolvedor escolhendo entre tuplas e tipos anônimos, há vários fatores a
serem considerados. De modo geral, se você não estiver trabalhando com árvores de
expressão e estiver confortável com a sintaxe de tupla, escolha ValueTuple, pois elas
fornecem um tipo de valor com flexibilidade para nomear propriedades. Se você estiver
trabalhando com árvores de expressão e preferir nomear propriedades, escolha tipos
anônimos. Caso contrário, use Tuple.

Confira também
Tipos anônimos
Árvores de expressão
Tipos de tupla – (referência do C#)
Tuplas (Visual Basic)
Diretrizes de design de tipo
Visão geral da biblioteca de classes do
.NET
Artigo • 27/04/2023

As APIs do .NET incluem classes, interfaces, delegados e tipos de valor que agilizam e
otimizam o processo de desenvolvimento e dão acesso à funcionalidade do sistema.
Para facilitar a interoperabilidade entre linguagens, os tipos do .NET têm conformidade
com CLS e, assim, podem ser usados por qualquer linguagem de programação cujo
compilador esteja de acordo com a CLS (Common Language Specification).

Os tipos do .NET são a base na qual os aplicativos, os componentes e os controles do


.NET são criados. O .NET inclui tipos que executam as seguintes funções:

Representam tipos de dados base e exceções.


Encapsulam estruturas de dados.
Executam E/S.
Acessam informações sobre tipos carregados.
Invocam verificações de segurança do .NET.
Fornecem acesso a dados, uma GUI detalhada do lado do cliente e uma GUI do
lado do cliente controlada pelo servidor.

O .NET fornece um conjunto avançado de interfaces, bem como classes abstratas e


concretas (não abstratas). Você pode usar as classes concretas no estado em que se
encontram ou, em muitos casos, pode derivar suas próprias classes delas. Para usar a
funcionalidade de uma interface, você pode criar uma classe que implementa a interface
ou derivar uma classe de uma das classes do .NET que implementa a interface.

Convenções de nomenclatura
Os tipos do .NET usam um esquema de nomenclatura de sintaxe por ponto que denota
uma hierarquia. Essa técnica agrupa tipos relacionados em namespaces para que eles
possam ser pesquisados e referenciados com mais facilidade. A primeira parte do nome
completo (até o ponto mais à direita) é o nome do namespace. A última parte do nome
é o nome do tipo. Por exemplo, System.Collections.Generic.List<T> representa o tipo
List<T> , que pertence ao namespace System.Collections.Generic . Os tipos em

System.Collections.Generic podem ser usados para trabalhar com coleções genéricas.

Esse esquema de nomenclatura facilita para desenvolvedores de bibliotecas estender o


.NET para criar grupos hierárquicos de tipos e nomeá-los de maneira consistente e
informativa. Ele também permite que tipos sejam identificados sem ambiguidades por
seu nome completo (ou seja, pelo namespace e pelo nome de tipo), o que impede
conflitos de nomes de tipo. Os desenvolvedores de bibliotecas devem usar a seguinte
convenção para criar nomes para seus namespaces:

CompanyName.TechnologyName

Por exemplo, o namespace Microsoft.Word segue esta diretriz.

O uso de padrões de nomenclatura para agrupar tipos relacionados em namespaces é


um modo útil de compilar e documentar bibliotecas de classes. No entanto, esse
esquema de nomenclatura não afeta visibilidade, acesso a membro, herança, segurança
ou associação. Um namespace pode ser particionado em vários assemblies e um único
assembly pode conter tipos de vários namespaces. O assembly fornece a estrutura
formal para criação de versão, implantação, segurança, carregamento e visibilidade no
Common Language Runtime.

Para obter mais informações sobre namespaces e nomes de tipos, consulte Common
Type System.

namespace System
O System é o namespace raiz para tipos fundamentais no.NET. Esse namespace contém
classes que representam os tipos de dados base usados por todos os aplicativos, por
exemplo, Object (a raiz da hierarquia de herança), Byte, Char, Array, Int32 e String.
Muitos desses tipos correspondem aos tipos de dados primitivos que sua linguagem de
programação usa. Ao gravar códigos usando tipos .NET, você pode usar a palavra-chave
correspondente da sua linguagem quando um tipo de dados base do .NET é esperado.

A tabela a seguir lista os tipos base que o .NET fornece, descreve resumidamente cada
tipo e indica o tipo correspondente em Visual Basic, C#, C++ e F#.

Categoria Nome Descrição Tipo de Tipo de Tipo de Tipo de


da dados do dados dados de dados de
classe Visual em C# C++/CLI F#
Basic

Integer Byte Um inteiro de 8 bits Byte byte unsigned byte


sem sinal. char

SByte Um inteiro com sinal SByte sbyte char ou sbyte


de 8 bits. signed
char
Não compatível com
CLS.
Categoria Nome Descrição Tipo de Tipo de Tipo de Tipo de
da dados do dados dados de dados de
classe Visual em C# C++/CLI F#
Basic

Int16 Um inteiro de 16 bits Short short short int16


com sinal.

Int32 Um inteiro com sinal Integer int int ou int


de 32 bits. long

Int64 Um inteiro com sinal Long long __int64 int64


de 64 bits.

UInt16 Um inteiro sem sinal UShort ushort unsigned uint16


de 16 bits. short

Não compatível com


CLS.

UInt32 Um inteiro sem sinal UInteger uint unsigned uint32


de 32 bits. int ou
unsigned
Não compatível com long
CLS.

UInt64 Um inteiro sem sinal ULong ulong unsigned uint64


de 64 bits. __int64

Não compatível com


CLS.

Ponto Half Um número de ponto


flutuante flutuante de meia
precisão (16 bits).

Single Um número de ponto Single float float float32 ou


flutuante de precisão single
simples (32 bits).

Double Um número de ponto Double double double float ou


flutuante de precisão double
dupla (64 bits).

Lógico Boolean Um valor booliano Boolean bool bool bool


(verdadeiro ou falso).

Outro Char Um caractere Char char wchar_t char


Unicode (16 bits).
Categoria Nome Descrição Tipo de Tipo de Tipo de Tipo de
da dados do dados dados de dados de
classe Visual em C# C++/CLI F#
Basic

Decimal Um valor decimal Decimal decimal Decimal decimal


(128 bits).

IntPtr Um inteiro com sinal nint unativeint


cujo tamanho
dependa da
plataforma
subjacente (um valor
de 32 bits em uma
plataforma de 32 bits
e um valor de 64 bits
em uma plataforma
de 64 bits).

UIntPtr Um inteiro sem sinal nuint unativeint


cujo tamanho
dependa da
plataforma
subjacente (um valor
de 32 bits em uma
plataforma de 32 bits
e um valor de 64 bits
em uma plataforma
de 64 bits).

Não compatível com


CLS.

Object A raiz da hierarquia Object object Object^ obj


do objeto.

String Uma cadeia de String string String^ string


caracteres Unicode
imutável e de
comprimento fixo.

Além dos tipos de dados base, o namespace System contém mais de 100 classes, que
vão desde classes que identificam exceções até classes que lidam com os conceitos de
runtime, como domínios de aplicativo e o coletor de lixo. O namespace System também
contém vários namespaces de segundo nível.

Para obter mais informações sobre namespaces, use o Navegador de API do .NET para
procurar a Biblioteca de classes do .NET. A documentação de referência de API fornece
informações sobre cada namespace, seus tipos e cada um dos seus membros.

Estruturas de dados
O .NET inclui um conjunto de estruturas de dados que são a base de muitos aplicativos
.NET. Elas são em sua maioria coleções, mas também incluem outros tipos.

Array - Representa uma matriz de objetos fortemente tipados que podem ser
acessados por índice. Tem um tamanho fixo, de acordo com sua construção.
List<T> – representa uma lista fortemente tipada de objetos que podem ser
acessados por índice. É redimensionado automaticamente conforme necessário.
Dictionary<TKey,TValue> – representa uma coleção de valores que são indexados
por uma chave. Os valores podem ser acessados via chave. É redimensionado
automaticamente conforme necessário.
Uri – fornece uma representação de objeto de um URI (Uniform Resource
Identifier) e fácil acesso às partes do URI.
DateTime – representa um momento no tempo, geralmente expresso como uma
data e hora do dia.

APIs utilitárias
O .NET inclui um conjunto de APIs utilitárias que fornecem funcionalidade para várias
tarefas importantes.

HttpClient – uma API para enviar solicitações HTTP e receber respostas HTTP de
um recurso identificado por um URI.
XDocument - Uma API para carregar e consultar documentos XML com o LINQ.
StreamReader – uma API para ler arquivos.
StreamWriter – uma API para gravar arquivos.

APIs do modelo de aplicativo


Muitos modelos de aplicativo podem ser usados com o .NET, por exemplo:

ASP.NET – uma estrutura da Web para a criação de sites e serviços Web. Suporte
para Windows, Linux e macOS (depende de versão do ASP.NET).
.NET MAUI – uma plataforma de aplicativo para a criação de aplicativos nativos
executados no Windows, macOS, iOS e Android usando C#.
Área de Trabalho do Windows – Inclui Windows Presentation Foundation (WPF)
e Windows Forms.
Confira também
Visão geral das bibliotecas de runtime
Common Type System
Navegador de API do .NET

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Classe System.Object
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A Object classe é a classe base final de todas as classes .NET, é a raiz da hierarquia de
tipos.

Como todas as classes no .NET são derivadas do Object, cada método definido na
Object classe está disponível em todos os objetos no sistema. As classes derivadas
podem e substituem alguns desses métodos, incluindo:

Equals: Suporta comparações entre objetos.


Finalize: Executa operações de limpeza antes que um objeto seja recuperado
automaticamente.
GetHashCode: Gera um número correspondente ao valor do objeto para dar
suporte ao uso de uma tabela de hash.
ToString: Fabrica uma cadeia de caracteres de texto legível por humanos que
descreve uma instância da classe.

Os idiomas normalmente não exigem uma classe para declarar herança Object porque a
herança está implícita.

Considerações sobre o desempenho


Se você estiver criando uma classe, como uma coleção, que deve manipular qualquer
tipo de objeto, poderá criar membros de classe que aceitem instâncias da Object classe.
No entanto, o processo de boxe e unboxing de um tipo tem um custo de desempenho.
Se você sabe que sua nova classe frequentemente lida com certos tipos de valor, você
pode usar uma das duas táticas para minimizar o custo do boxe.

Crie um método geral que aceite um tipo e um Object conjunto de sobrecargas de


método específico de tipo que aceite cada tipo de valor que você espera que sua
classe manipule com frequência. Se existir um método específico de tipo que
aceite o tipo de parâmetro de chamada, não ocorrerá boxing e o método
específico do tipo será chamado. Se não houver nenhum argumento de método
que corresponda ao tipo de parâmetro de chamada, o parâmetro será colocado
em caixa e o método geral será chamado.
Projete seu tipo e seus membros para usar genéricos. O Common Language
Runtime cria um tipo genérico fechado quando você cria uma instância de sua
classe e especifica um argumento de tipo genérico. O método genérico é
específico do tipo e pode ser invocado sem encaixotar o parâmetro de chamada.

Embora às vezes seja necessário desenvolver classes de propósito geral que aceitem e
retornem Object tipos, você pode melhorar o desempenho fornecendo também uma
classe específica de tipo para lidar com um tipo usado com frequência. Por exemplo,
fornecer uma classe específica para definir e obter valores booleanos elimina o custo de
boxing e unboxing de valores booleanos.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.Object.Equals
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Este artigo refere-se ao Object.Equals(Object) método.

O tipo de comparação entre a instância atual e o obj parâmetro depende se a instância


atual é um tipo de referência ou um tipo de valor.

Se a instância atual for um tipo de referência, o método testará a igualdade de


referência e uma chamada para o método será equivalente a uma chamada para o
Equals(Object)Equals(Object)ReferenceEquals método. Igualdade de referência
significa que as variáveis de objeto que são comparadas se referem ao mesmo
objeto. O exemplo a seguir ilustra o resultado dessa comparação. Ele define uma
Person classe, que é um tipo de referência, e chama o construtor de classe para

instanciar dois novos Person objetos person1a e person2 , que têm o Person
mesmo valor. Ele também atribui person1a a outra variável de objeto, person1b .
Como mostra a saída do exemplo, person1a e person1b são iguais porque fazem
referência ao mesmo objeto. No entanto, e person2 não são iguais, person1a
embora tenham o mesmo valor.

C#

using System;

// Define a reference type that does not override Equals.


public class Person
{
private string personName;

public Person(string name)


{
this.personName = name;
}

public override string ToString()


{
return this.personName;
}
}

public class Example1


{
public static void Main()
{
Person person1a = new Person("John");
Person person1b = person1a;
Person person2 = new Person(person1a.ToString());

Console.WriteLine("Calling Equals:");
Console.WriteLine("person1a and person1b: {0}",
person1a.Equals(person1b));
Console.WriteLine("person1a and person2: {0}",
person1a.Equals(person2));

Console.WriteLine("\nCasting to an Object and calling Equals:");


Console.WriteLine("person1a and person1b: {0}", ((object)
person1a).Equals((object) person1b));
Console.WriteLine("person1a and person2: {0}", ((object)
person1a).Equals((object) person2));
}
}
// The example displays the following output:
// person1a and person1b: True
// person1a and person2: False
//
// Casting to an Object and calling Equals:
// person1a and person1b: True
// person1a and person2: False

Se a instância atual for um tipo de valor, o método testará a igualdade de


Equals(Object) valor. Igualdade de valor significa o seguinte:

Os dois objetos são do mesmo tipo. Como mostra o exemplo a seguir, um


objeto que tem um valor de 12 não é igual a um objeto que tem um ByteInt32
valor de 12, porque os dois objetos têm tipos de tempo de execução diferentes.

C#

byte value1 = 12;


int value2 = 12;

object object1 = value1;


object object2 = value2;

Console.WriteLine("{0} ({1}) = {2} ({3}): {4}",


object1, object1.GetType().Name,
object2, object2.GetType().Name,
object1.Equals(object2));

// The example displays the following output:


// 12 (Byte) = 12 (Int32): False
Os valores dos campos público e privado dos dois objetos são iguais. O
exemplo a seguir testa a igualdade de valor. Ele define uma Person estrutura,
que é um tipo de valor, e chama o construtor de classe para instanciar dois
novos Person objetos person1 e person2 , que têm o Person mesmo valor.
Como mostra a saída do exemplo, embora as duas variáveis de objeto se
refiram a objetos person1 diferentes e person2 sejam iguais porque têm o
mesmo valor para o campo privado personName .

C#

using System;

// Define a value type that does not override Equals.


public struct Person3
{
private string personName;

public Person3(string name)


{
this.personName = name;
}

public override string ToString()


{
return this.personName;
}
}

public struct Example3


{
public static void Main()
{
Person3 person1 = new Person3("John");
Person3 person2 = new Person3("John");

Console.WriteLine("Calling Equals:");
Console.WriteLine(person1.Equals(person2));

Console.WriteLine("\nCasting to an Object and calling


Equals:");
Console.WriteLine(((object) person1).Equals((object)
person2));
}
}
// The example displays the following output:
// Calling Equals:
// True
//
// Casting to an Object and calling Equals:
// True
Como a classe é a classe base para todos os tipos no .NET, o Object.Equals(Object)
método fornece a Object comparação de igualdade padrão para todos os outros tipos.
No entanto, os tipos geralmente substituem o método para implementar a igualdade de
Equals valor. Para obter mais informações, consulte as seções Notas para chamadores e
Notas para herdeiros.

Observações para o Tempo de Execução do


Windows
Quando você chama a sobrecarga de Equals(Object) método em uma classe no Tempo
de Execução do Windows, ela fornece o comportamento padrão para classes que não
substituem Equals(Object). Isso faz parte do suporte que o .NET fornece para o Tempo
de Execução do Windows (consulte Suporte do .NET para aplicativos da Windows Store
e Tempo de Execução do Windows). As classes no Tempo de Execução do Windows não
herdam e, no momento, não implementam Objectum Equals(Object) método. No
entanto, eles parecem ter ToStringmétodos , Equals(Object)e quando você usá-los em
seu código C# ou Visual Basic e GetHashCode .NET fornece o comportamento padrão
para esses métodos.

7 Observação

As classes do Tempo de Execução do Windows escritas em C# ou Visual Basic


podem substituir a sobrecarga do Equals(Object) método.

Notas para chamadores


Classes derivadas frequentemente substituem o método para implementar a igualdade
de Object.Equals(Object) valor. Além disso, os tipos também frequentemente fornecem
uma sobrecarga adicional fortemente tipada para o Equals método, normalmente
implementando a IEquatable<T> interface. Ao chamar o método para testar a Equals
igualdade, você deve saber se a instância atual substitui Object.Equals e entender como
uma chamada específica para um Equals método é resolvida. Caso contrário, você pode
estar executando um teste de igualdade que é diferente do que você pretendia e o
método pode retornar um valor inesperado.

O exemplo a seguir ilustra esse cenário. Ele instancia três StringBuilder objetos com
cadeias de caracteres idênticas e, em seguida, faz quatro chamadas para Equals
métodos. A primeira chamada de método retorna true e os três restantes retornam
false .
C#

using System;
using System.Text;

public class Example5


{
public static void Main()
{
StringBuilder sb1 = new StringBuilder("building a string...");
StringBuilder sb2 = new StringBuilder("building a string...");

Console.WriteLine("sb1.Equals(sb2): {0}", sb1.Equals(sb2));


Console.WriteLine("((Object) sb1).Equals(sb2): {0}",
((Object) sb1).Equals(sb2));
Console.WriteLine("Object.Equals(sb1, sb2): {0}",
Object.Equals(sb1, sb2));

Object sb3 = new StringBuilder("building a string...");


Console.WriteLine("\nsb3.Equals(sb2): {0}", sb3.Equals(sb2));
}
}
// The example displays the following output:
// sb1.Equals(sb2): True
// ((Object) sb1).Equals(sb2): False
// Object.Equals(sb1, sb2): False
//
// sb3.Equals(sb2): False

No primeiro caso, a sobrecarga de método fortemente tipada


StringBuilder.Equals(StringBuilder) , que testa a igualdade de valor, é chamada. Como as
cadeias de caracteres atribuídas aos dois StringBuilder objetos são iguais, o método
retorna true . No entanto, StringBuilder não substitui Object.Equals(Object). Devido a
isso, quando o objeto é convertido em um Object, quando uma instância é atribuída a
uma StringBuilder variável do tipo Object, e quando o método é passado dois
StringBuilder objetos, o StringBuilderObject.Equals(Object, Object) método padrão
Object.Equals(Object) é chamado. Como StringBuilder é um tipo de referência, isso
equivale a passar os dois StringBuilder objetos para o ReferenceEquals método. Embora
todos os três objetos contenham cadeias de caracteres idênticas, eles se referem a três
StringBuilder objetos distintos. Como resultado, essas três chamadas de método
retornam false .

Você pode comparar o objeto atual com outro objeto para igualdade de referência
chamando o ReferenceEquals método. No Visual Basic, você também pode usar a is
palavra-chave (por exemplo, If Me Is otherObject Then ... ).
Notas para herdeiros
Quando você define seu próprio tipo, esse tipo herda a funcionalidade definida pelo
Equals método de seu tipo base. A tabela a seguir lista a Equals implementação

padrão do método para as principais categorias de tipos no .NET.

ノ Expandir a tabela

Categoria do tipo Igualdade definida por Comentários

Classe derivada Object.Equals(Object) Igualdade de referência; equivalente a chamar


diretamente de Object.ReferenceEquals.
Object

Estrutura ValueType.Equals Igualdade de valores; Comparação direta byte a


byte ou comparação campo a campo usando
reflexão.

Enumeração Enum.Equals Os valores devem ter o mesmo tipo de


enumeração e o mesmo valor subjacente.

Delegar MulticastDelegate.Equals Os delegados devem ter o mesmo tipo com


listas de invocação idênticas.

Interface Object.Equals(Object) Igualdade de referência.

Para um tipo de valor, você deve sempre substituir Equals, porque os testes de
igualdade que dependem da reflexão oferecem um desempenho ruim. Você também
pode substituir a implementação padrão de para tipos de referência para testar a
igualdade de valor em vez da igualdade de referência e definir o significado preciso de
igualdade de Equals valor. Tais implementações de Equals retorno true se os dois
objetos tiverem o mesmo valor, mesmo que não sejam a mesma instância. O
implementador do tipo decide o que constitui o valor de um objeto, mas normalmente
são alguns ou todos os dados armazenados nas variáveis de instância do objeto. Por
exemplo, o valor de um String objeto é baseado nos caracteres da cadeia de caracteres,
o método substitui o String.Equals(Object)Object.Equals(Object) método para retornar
true para quaisquer duas ocorrências de cadeia de caracteres que contenham os

mesmos caracteres na mesma ordem.

O exemplo a seguir mostra como substituir o método para testar a igualdade de


Object.Equals(Object) valor. Ele substitui o Equals método para a Person classe. Se
Person aceitasse sua implementação de classe base de igualdade, dois Person objetos

seriam iguais somente se fizessem referência a um único objeto. No entanto, nesse


caso, dois Person objetos serão iguais se tiverem o mesmo valor para a Person.Id
propriedade.

C#

public class Person6


{
private string idNumber;
private string personName;

public Person6(string name, string id)


{
this.personName = name;
this.idNumber = id;
}

public override bool Equals(Object obj)


{
Person6 personObj = obj as Person6;
if (personObj == null)
return false;
else
return idNumber.Equals(personObj.idNumber);
}

public override int GetHashCode()


{
return this.idNumber.GetHashCode();
}
}

public class Example6


{
public static void Main()
{
Person6 p1 = new Person6("John", "63412895");
Person6 p2 = new Person6("Jack", "63412895");
Console.WriteLine(p1.Equals(p2));
Console.WriteLine(Object.Equals(p1, p2));
}
}
// The example displays the following output:
// True
// True

Além de substituir Equals, você pode implementar a IEquatable<T> interface para


fornecer um teste fortemente tipado para igualdade.

As instruções a seguir devem ser verdadeiras para todas as implementações do


Equals(Object) método. Na lista, x , y e z representam referências de objeto que não
são nulas.
x.Equals(x) retorna true .

x.Equals(y) retorna o mesmo valor que y.Equals(x) .

x.Equals(y) retorna true se ambos x e y são NaN .

Se (x.Equals(y) && y.Equals(z)) retorna , então x.Equals(z) retorna true true .

Chamadas sucessivas para x.Equals(y) retornar o mesmo valor, desde que os


objetos referenciados por x e y não sejam modificados.

x.Equals(null) retorna false .

Implementações de Equals não devem lançar exceções, elas devem sempre retornar um
valor. Por exemplo, se obj for null , o Equals método deve retornar false em vez de
lançar um ArgumentNullExceptionarquivo .

Siga estas diretrizes ao substituir Equals(Object):

Os tipos que implementam IComparable devem substituir Equals(Object)o .

Os tipos que substituem Equals(Object) também devem substituir GetHashCode,


caso contrário, as tabelas de hash podem não funcionar corretamente.

Você deve considerar a implementação da interface para oferecer suporte a


IEquatable<T> testes fortemente tipados para igualdade. Sua implementação
IEquatable<T>.Equals deve retornar resultados consistentes com Equals.

Se sua linguagem de programação oferece suporte à sobrecarga do operador e


você sobrecarrega o operador equality para um determinado tipo, você também
deve substituir o método para retornar o mesmo resultado que o Equals(Object)
operador equality. Isso ajuda a garantir que o código da biblioteca de classes que
usa Equals (como e Hashtable) se comporte de maneira consistente com a maneira
como ArrayList o operador equality é usado pelo código do aplicativo.

Diretrizes para tipos de referência


As diretrizes a seguir se aplicam à substituição Equals(Object) de um tipo de referência:

Considere substituir Equals se a semântica do tipo for baseada no fato de que o


tipo representa algum valor.

A maioria dos tipos de referência não deve sobrecarregar o operador de


igualdade, mesmo que substituam Equals. No entanto, se você estiver
implementando um tipo de referência que se destina a ter semântica de valor,
como um tipo de número complexo, você deve substituir o operador de
igualdade.

Você não deve substituir Equals em um tipo de referência mutável. Isso ocorre
porque a substituição Equals requer que você também substitua o GetHashCode
método, conforme discutido na seção anterior. Isso significa que o código hash de
uma instância de um tipo de referência mutável pode mudar durante sua vida útil,
o que pode fazer com que o objeto seja perdido em uma tabela de hash.

Diretrizes para tipos de valor


As diretrizes a seguir se aplicam à substituição Equals(Object) de um tipo de valor:

Se você estiver definindo um tipo de valor que inclua um ou mais campos cujos
valores são tipos de referência, substitua Equals(Object). A Equals(Object)
implementação fornecida por ValueType executa uma comparação byte a byte
para tipos de valor cujos campos são todos os tipos de valor, mas usa a reflexão
para executar uma comparação campo a campo de tipos de valor cujos campos
incluem tipos de referência.

Se você substituir Equals e sua linguagem de desenvolvimento suportar


sobrecarga de operador, você deve sobrecarregar o operador de igualdade.

Você deve implementar a IEquatable<T> interface. Chamar o método fortemente


tipado IEquatable<T>.Equals evita encaixotar o obj argumento.

Exemplos
O exemplo a seguir mostra uma classe que substitui o método para fornecer igualdade
de valor e uma Point Point3D classe derivada de Point .Equals Como Point substitui
para testar a igualdade de Object.Equals(Object) valor, o Object.Equals(Object) método
não é chamado. No entanto, Point3D.Equals chama Point.Equals porque Point
implementa de uma maneira que fornece igualdade de Object.Equals(Object) valor.

C#

using System;

class Point2
{
protected int x, y;

public Point2() : this(0, 0)


{ }
public Point2(int x, int y)
{
this.x = x;
this.y = y;
}

public override bool Equals(Object obj)


{
//Check for null and compare run-time types.
if ((obj == null) || !this.GetType().Equals(obj.GetType()))
{
return false;
}
else
{
Point2 p = (Point2)obj;
return (x == p.x) && (y == p.y);
}
}

public override int GetHashCode()


{
return (x << 2) ^ y;
}

public override string ToString()


{
return String.Format("Point2({0}, {1})", x, y);
}
}

sealed class Point3D : Point2


{
int z;

public Point3D(int x, int y, int z) : base(x, y)


{
this.z = z;
}

public override bool Equals(Object obj)


{
Point3D pt3 = obj as Point3D;
if (pt3 == null)
return false;
else
return base.Equals((Point2)obj) && z == pt3.z;
}

public override int GetHashCode()


{
return (base.GetHashCode() << 2) ^ z;
}
public override String ToString()
{
return String.Format("Point2({0}, {1}, {2})", x, y, z);
}
}

class Example7
{
public static void Main()
{
Point2 point2D = new Point2(5, 5);
Point3D point3Da = new Point3D(5, 5, 2);
Point3D point3Db = new Point3D(5, 5, 2);
Point3D point3Dc = new Point3D(5, 5, -1);

Console.WriteLine("{0} = {1}: {2}",


point2D, point3Da, point2D.Equals(point3Da));
Console.WriteLine("{0} = {1}: {2}",
point2D, point3Db, point2D.Equals(point3Db));
Console.WriteLine("{0} = {1}: {2}",
point3Da, point3Db, point3Da.Equals(point3Db));
Console.WriteLine("{0} = {1}: {2}",
point3Da, point3Dc, point3Da.Equals(point3Dc));
}
}
// The example displays the following output:
// Point2(5, 5) = Point2(5, 5, 2): False
// Point2(5, 5) = Point2(5, 5, 2): False
// Point2(5, 5, 2) = Point2(5, 5, 2): True
// Point2(5, 5, 2) = Point2(5, 5, -1): False

O Point.Equals método verifica se o obj argumento não é nulo e se faz referência a


uma instância do mesmo tipo que esse objeto. Se uma das verificações falhar, o método
retornará false .

O Point.Equals método chama o GetType método para determinar se os tipos de


tempo de execução dos dois objetos são idênticos. Se o método usou uma verificação
do formulário obj is Point em C# ou TryCast(obj, Point) no Visual Basic, a
verificação retornaria true nos casos em que é uma instância de uma classe derivada de
, mesmo que obj obj e a instância atual não são do mesmo tipo de tempo de
Point execução. Depois de verificar que ambos os objetos são do mesmo tipo, o

método converte obj em tipo Point e retorna o resultado da comparação dos campos
de instância dos dois objetos.

No Point3D.Equals , o método herdado Point.Equals , que substitui


Object.Equals(Object), é invocado antes que qualquer outra coisa seja feita. Como
Point3D é uma classe lacrada ( NotInheritable no Visual Basic), uma verificação no

formulário obj is Point em C# ou TryCast(obj, Point) no Visual Basic é adequada


para garantir que obj seja um Point3D objeto. Se for um objeto, ele será convertido em
um Point3D Point objeto e passado para a implementação da classe base do Equals.
Somente quando o método herdado Point.Equals retorna true o método compara os
campos de z instância introduzidos na classe derivada.

O exemplo a seguir define uma Rectangle classe que implementa internamente um


retângulo como dois Point objetos. A Rectangle classe também se sobrepõe para
proporcionar igualdade de Object.Equals(Object) valor.

C#

using System;

class Rectangle
{
private Point a, b;

public Rectangle(int upLeftX, int upLeftY, int downRightX, int


downRightY)
{
this.a = new Point(upLeftX, upLeftY);
this.b = new Point(downRightX, downRightY);
}

public override bool Equals(Object obj)


{
// Perform an equality check on two rectangles (Point object pairs).
if (obj == null || GetType() != obj.GetType())
return false;
Rectangle r = (Rectangle)obj;
return a.Equals(r.a) && b.Equals(r.b);
}

public override int GetHashCode()


{
return Tuple.Create(a, b).GetHashCode();
}

public override String ToString()


{
return String.Format("Rectangle({0}, {1}, {2}, {3})",
a.x, a.y, b.x, b.y);
}
}

class Point
{
internal int x;
internal int y;

public Point(int X, int Y)


{
this.x = X;
this.y = Y;
}

public override bool Equals (Object obj)


{
// Performs an equality check on two points (integer pairs).
if (obj == null || GetType() != obj.GetType()) return false;
Point p = (Point)obj;
return (x == p.x) && (y == p.y);
}

public override int GetHashCode()


{
return Tuple.Create(x, y).GetHashCode();
}
}

class Example
{
public static void Main()
{
Rectangle r1 = new Rectangle(0, 0, 100, 200);
Rectangle r2 = new Rectangle(0, 0, 100, 200);
Rectangle r3 = new Rectangle(0, 0, 150, 200);

Console.WriteLine("{0} = {1}: {2}", r1, r2, r1.Equals(r2));


Console.WriteLine("{0} = {1}: {2}", r1, r3, r1.Equals(r3));
Console.WriteLine("{0} = {1}: {2}", r2, r3, r2.Equals(r3));
}
}
// The example displays the following output:
// Rectangle(0, 0, 100, 200) = Rectangle(0, 0, 100, 200): True
// Rectangle(0, 0, 100, 200) = Rectangle(0, 0, 150, 200): False
// Rectangle(0, 0, 100, 200) = Rectangle(0, 0, 150, 200): False

Algumas linguagens, como C# e Visual Basic, oferecem suporte à sobrecarga do


operador. Quando um tipo sobrecarrega o operador equality, ele também deve
substituir o Equals(Object) método para fornecer a mesma funcionalidade. Isso
normalmente é feito escrevendo o Equals(Object) método em termos do operador de
igualdade sobrecarregado, como no exemplo a seguir.

C#

using System;

public struct Complex


{
public double re, im;

public override bool Equals(Object obj)


{
return obj is Complex && this == (Complex)obj;
}

public override int GetHashCode()


{
return Tuple.Create(re, im).GetHashCode();
}

public static bool operator ==(Complex x, Complex y)


{
return x.re == y.re && x.im == y.im;
}

public static bool operator !=(Complex x, Complex y)


{
return !(x == y);
}

public override String ToString()


{
return String.Format("({0}, {1})", re, im);
}
}

class MyClass
{
public static void Main()
{
Complex cmplx1, cmplx2;

cmplx1.re = 4.0;
cmplx1.im = 1.0;

cmplx2.re = 2.0;
cmplx2.im = 1.0;

Console.WriteLine("{0} <> {1}: {2}", cmplx1, cmplx2, cmplx1 != cmplx2);


Console.WriteLine("{0} = {1}: {2}", cmplx1, cmplx2,
cmplx1.Equals(cmplx2));

cmplx2.re = 4.0;

Console.WriteLine("{0} = {1}: {2}", cmplx1, cmplx2, cmplx1 == cmplx2);


Console.WriteLine("{0} = {1}: {2}", cmplx1, cmplx2,
cmplx1.Equals(cmplx2));
}
}
// The example displays the following output:
// (4, 1) <> (2, 1): True
// (4, 1) = (2, 1): False
// (4, 1) = (4, 1): True
// (4, 1) = (4, 1): True
Como Complex é um tipo de valor, ele não pode ser derivado de. Portanto, a
substituição para o método não precisa chamar GetType para determinar o tipo de
tempo de execução preciso de cada objeto, mas em vez disso pode usar o operador em
C# ou o is TypeOf operador no Visual Basic para Equals(Object) verificar o obj tipo do
parâmetro.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.Object.Finalize
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Finalize método é usado para executar operações de limpeza em recursos não


gerenciados mantidos pelo objeto atual antes que o objeto seja destruído. O método é
protegido e, portanto, é acessível somente por meio dessa classe ou por meio de uma
classe derivada.

Como funciona a finalização


A Object classe não fornece nenhuma implementação para o método, e o coletor de lixo
não marca tipos derivados de Object para finalização, a menos que eles substituam o
FinalizeFinalize método.

Se um tipo substituir o método, o Finalize coletor de lixo adicionará uma entrada para
cada instância do tipo a uma estrutura interna chamada fila de finalização. A fila de
finalização contém entradas para todos os objetos no heap gerenciado cujo código de
finalização deve ser executado antes que o coletor de lixo possa recuperar sua memória.
Em seguida, o coletor de lixo chama o Finalize método automaticamente nas seguintes
condições:

Depois que o coletor de lixo tiver descoberto que um objeto está inacessível, a
menos que o objeto tenha sido isento da finalização por uma chamada para o
GC.SuppressFinalize método.
Somente no .NET Framework, durante o desligamento de um domínio de
aplicativo, a menos que o objeto esteja isento de finalização. Durante o
desligamento, até mesmo os objetos que ainda estão acessíveis são finalizados.

Finalize é chamado automaticamente apenas uma vez em uma determinada instância, a


menos que o objeto seja registrado novamente usando um mecanismo como
GC.ReRegisterForFinalize e o GC.SuppressFinalize método não tenha sido chamado
subsequentemente.

Finalize As operações têm as seguintes limitações:

A hora exata em que o finalizador é executada é indefinida. Para garantir a


liberação determinística de recursos para instâncias de sua classe, implemente um
Close método ou forneça uma IDisposable.Dispose implementação.
Os finalizadores de dois objetos não são garantidos para executar em qualquer
ordem específica, mesmo se um objeto se referir ao outro. Ou seja, se o Objeto A
tiver uma referência ao Objeto B e ambos tiverem finalizadores, o Objeto B pode já
ter sido finalizado quando o finalizador do Objeto A for iniciado.
O thread no qual o finalizador é executado não é especificado.

O Finalize método pode não ser executado até a conclusão ou pode não ser executado
nas seguintes circunstâncias excepcionais:

Se outro finalizador bloqueia indefinidamente (entra em um loop infinito, tenta


obter um bloqueio que nunca pode obter, e assim por diante). Como o tempo de
execução tenta executar finalizadores até a conclusão, outros finalizadores podem
não ser chamados se um finalizador for bloqueado indefinidamente.
Se o processo for encerrado sem dar ao tempo de execução a chance de limpeza.
Nesse caso, a primeira notificação do tempo de execução do encerramento do
processo é uma notificação DLL_PROCESS_DETACH.

O tempo de execução continua a finalizar objetos durante o desligamento somente


enquanto o número de objetos finalizáveis continua a diminuir.

Se Finalize ou uma substituição de lança uma exceção e o tempo de execução não é


hospedado por um aplicativo que substitui a política padrão, o tempo de Finalize
execução encerra o processo e nenhum bloco ativo try / finally ou finalizadores são
executados. Esse comportamento garante a integridade do processo se o finalizador
não puder liberar ou destruir recursos.

Substituindo o método Finalize


Você deve substituir Finalize por uma classe que usa recursos não gerenciados, como
identificadores de arquivo ou conexões de banco de dados que devem ser liberados
quando o objeto gerenciado que os usa é descartado durante a coleta de lixo. Você não
deve implementar um Finalize método para objetos gerenciados porque o coletor de
lixo libera recursos gerenciados automaticamente.

) Importante

Se estiver disponível um objeto que encapsula seu recurso não gerenciado, a


alternativa recomendada é implementar o padrão de descarte com um SafeHandle
identificador seguro e não substituir Finalize. Para obter mais informações, consulte
A seção alternativa do SafeHandle.
O Object.Finalize método não faz nada por padrão, mas você deve substituir Finalize
somente se necessário e somente para liberar recursos não gerenciados. A recuperação
de memória tende a levar muito mais tempo se uma operação de finalização for
executada, porque requer pelo menos duas coletas de lixo. Além disso, você deve
substituir o Finalize método apenas para tipos de referência. O Common Language
Runtime apenas finaliza os tipos de referência. Ele ignora finalizadores em tipos de
valor.

O escopo do Object.Finalize método é protected . Você deve manter esse escopo


limitado ao substituir o método em sua classe. Ao manter um método protegido, você
impede que os usuários do seu aplicativo chamem o Finalize método de um Finalize
objeto diretamente.

Toda implementação de em um tipo derivado deve chamar a implementação de seu


tipo base de FinalizeFinalize. Este é o único caso em que o código do aplicativo tem
permissão para chamar Finalize. O método de Finalize um objeto não deve chamar um
método em nenhum objeto que não seja o de sua classe base. Isso ocorre porque os
outros objetos que estão sendo chamados podem ser coletados ao mesmo tempo que
o objeto de chamada, como no caso de um desligamento do Common Language
Runtime.

7 Observação

O compilador C# não permite que você substitua o Finalize método. Em vez disso,
você fornece um finalizador implementando um destruidor para sua classe. Um
destruidor C# chama automaticamente o destruidor de sua classe base.

Visual C++ também fornece sua própria sintaxe para implementar o Finalize
método. Para obter mais informações, consulte a seção "Destruidores e
finalizadores" de Como definir e consumir classes e estruturas (C++/CLI).

Como a coleta de lixo não é determinística, você não sabe exatamente quando o coletor
de lixo realiza a finalização. Para liberar recursos imediatamente, você também pode
optar por implementar o padrão de descarte e a IDisposable interface. A
IDisposable.Dispose implementação pode ser chamada pelos consumidores de sua
classe para liberar recursos não gerenciados, e você pode usar o método para liberar
recursos não gerenciados no caso de o FinalizeDispose método não ser chamado.

Finalize pode executar quase qualquer ação, incluindo ressuscitar um objeto (ou seja,
torná-lo acessível novamente) depois que ele tiver sido limpo durante a coleta de lixo.
No entanto, o objeto só pode ser ressuscitado uma vez; Finalize não pode ser chamado
em objetos ressuscitados durante a coleta de lixo.
A alternativa do SafeHandle
A criação de finalizadores confiáveis geralmente é difícil, porque você não pode fazer
suposições sobre o estado do seu aplicativo e porque exceções do sistema não tratadas,
como OutOfMemoryException e StackOverflowException encerram o finalizador. Em vez
de implementar um finalizador para sua classe liberar recursos não gerenciados, você
pode usar um objeto derivado da classe para encapsular seus recursos não gerenciados
e, em seguida, implementar o padrão de System.Runtime.InteropServices.SafeHandle
descarte sem um finalizador. O .NET Framework fornece as seguintes classes no
Microsoft.Win32 namespace que são derivadas de
System.Runtime.InteropServices.SafeHandle:

SafeFileHandle é uma classe wrapper para um identificador de arquivo.


SafeMemoryMappedFileHandle é uma classe wrapper para identificadores de
arquivo mapeados para memória.
SafeMemoryMappedViewHandle é uma classe wrapper de um ponteiro para um
bloco de memória não gerenciada.
SafeNCryptKeyHandle, SafeNCryptProviderHandlee SafeNCryptSecretHandle são
classes wrapper para identificadores criptográficos.
SafePipeHandle é uma classe wrapper para alças de tubo.
SafeRegistryHandle é uma classe wrapper para um identificador para uma chave
do Registro.
SafeWaitHandle é uma classe wrapper para um identificador de espera.

O exemplo a seguir usa o padrão de descarte com alças seguras em vez de substituir o
Finalize método. Ele define uma classe que encapsula informações do Registro sobre o
aplicativo que manipula arquivos com uma FileAssociation extensão de arquivo
específica. Os dois identificadores do Registro retornados como out parâmetros pelas
chamadas de função RegOpenKeyEx do Windows são passados para o
SafeRegistryHandle construtor. O método protegido Dispose do tipo, em seguida,
chama o SafeRegistryHandle.Dispose método para liberar esses dois identificadores.

C#

using Microsoft.Win32.SafeHandles;
using System;
using System.ComponentModel;
using System.IO;
using System.Runtime.InteropServices;

public class FileAssociationInfo : IDisposable


{
// Private variables.
private String ext;
private String openCmd;
private String args;
private SafeRegistryHandle hExtHandle, hAppIdHandle;

// Windows API calls.


[DllImport("advapi32.dll", CharSet= CharSet.Auto, SetLastError=true)]
private static extern int RegOpenKeyEx(IntPtr hKey,
String lpSubKey, int ulOptions, int samDesired,
out IntPtr phkResult);
[DllImport("advapi32.dll", CharSet= CharSet.Unicode, EntryPoint =
"RegQueryValueExW",
SetLastError=true)]
private static extern int RegQueryValueEx(IntPtr hKey,
string lpValueName, int lpReserved, out uint lpType,
string lpData, ref uint lpcbData);
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegSetValueEx(IntPtr hKey,
[MarshalAs(UnmanagedType.LPStr)] string lpValueName,
int Reserved, uint dwType,
[MarshalAs(UnmanagedType.LPStr)] string lpData,
int cpData);
[DllImport("advapi32.dll", SetLastError=true)]
private static extern int RegCloseKey(UIntPtr hKey);

// Windows API constants.


private const int HKEY_CLASSES_ROOT = unchecked((int) 0x80000000);
private const int ERROR_SUCCESS = 0;

private const int KEY_QUERY_VALUE = 1;


private const int KEY_SET_VALUE = 0x2;

private const uint REG_SZ = 1;

private const int MAX_PATH = 260;

public FileAssociationInfo(String fileExtension)


{
int retVal = 0;
uint lpType = 0;

if (!fileExtension.StartsWith("."))
fileExtension = "." + fileExtension;
ext = fileExtension;

IntPtr hExtension = IntPtr.Zero;


// Get the file extension value.
retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), fileExtension, 0,
KEY_QUERY_VALUE, out hExtension);
if (retVal != ERROR_SUCCESS)
throw new Win32Exception(retVal);
// Instantiate the first SafeRegistryHandle.
hExtHandle = new SafeRegistryHandle(hExtension, true);

string appId = new string(' ', MAX_PATH);


uint appIdLength = (uint) appId.Length;
retVal = RegQueryValueEx(hExtHandle.DangerousGetHandle(),
String.Empty, 0, out lpType, appId, ref appIdLength);
if (retVal != ERROR_SUCCESS)
throw new Win32Exception(retVal);
// We no longer need the hExtension handle.
hExtHandle.Dispose();

// Determine the number of characters without the terminating null.


appId = appId.Substring(0, (int) appIdLength / 2 - 1) +
@"\shell\open\Command";

// Open the application identifier key.


string exeName = new string(' ', MAX_PATH);
uint exeNameLength = (uint) exeName.Length;
IntPtr hAppId;
retVal = RegOpenKeyEx(new IntPtr(HKEY_CLASSES_ROOT), appId, 0,
KEY_QUERY_VALUE | KEY_SET_VALUE,
out hAppId);
if (retVal != ERROR_SUCCESS)
throw new Win32Exception(retVal);

// Instantiate the second SafeRegistryHandle.


hAppIdHandle = new SafeRegistryHandle(hAppId, true);

// Get the executable name for this file type.


string exePath = new string(' ', MAX_PATH);
uint exePathLength = (uint) exePath.Length;
retVal = RegQueryValueEx(hAppIdHandle.DangerousGetHandle(),
String.Empty, 0, out lpType, exePath, ref exePathLength);
if (retVal != ERROR_SUCCESS)
throw new Win32Exception(retVal);

// Determine the number of characters without the terminating null.


exePath = exePath.Substring(0, (int) exePathLength / 2 - 1);
// Remove any environment strings.
exePath = Environment.ExpandEnvironmentVariables(exePath);

int position = exePath.IndexOf('%');


if (position >= 0) {
args = exePath.Substring(position);
// Remove command line parameters ('%0', etc.).
exePath = exePath.Substring(0, position).Trim();
}
openCmd = exePath;
}

public String Extension


{ get { return ext; } }

public String Open


{ get { return openCmd; }
set {
if (hAppIdHandle.IsInvalid | hAppIdHandle.IsClosed)
throw new InvalidOperationException("Cannot write to registry
key.");
if (! File.Exists(value)) {
string message = String.Format("'{0}' does not exist", value);
throw new FileNotFoundException(message);
}
string cmd = value + " %1";
int retVal = RegSetValueEx(hAppIdHandle.DangerousGetHandle(),
String.Empty, 0,
REG_SZ, value, value.Length + 1);
if (retVal != ERROR_SUCCESS)
throw new Win32Exception(retVal);
} }

public void Dispose()


{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}

protected void Dispose(bool disposing)


{
// Ordinarily, we release unmanaged resources here;
// but all are wrapped by safe handles.

// Release disposable objects.


if (disposing) {
if (hExtHandle != null) hExtHandle.Dispose();
if (hAppIdHandle != null) hAppIdHandle.Dispose();
}
}
}

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.Object.GetHashCode
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O GetHashCode método fornece um código hash para algoritmos que precisam de


verificações rápidas de igualdade de objeto. Um código hash é um valor numérico
usado para inserir e identificar um objeto em uma coleção baseada em hash, como a
classe, a Dictionary<TKey,TValue>Hashtable classe ou um tipo derivado da
DictionaryBase classe.

7 Observação

Para obter informações sobre como os códigos hash são usados em tabelas de
hash e para alguns algoritmos de código hash adicionais, consulte a entrada
Função de hash na Wikipedia.

Dois objetos que são iguais retornam códigos de hash que são iguais. No entanto, o
inverso não é verdadeiro: códigos hash iguais não implicam igualdade de objeto,
porque objetos diferentes (desiguais) podem ter códigos hash idênticos. Além disso, o
GetHashCode .NET não garante a implementação padrão do método, e o valor que esse
método retorna pode diferir entre implementações do .NET, como versões diferentes do
.NET Framework e do .NET Core, e plataformas, como plataformas de 32 bits e 64 bits.
Por esses motivos, não use a implementação padrão desse método como um
identificador de objeto exclusivo para fins de hash. Daqui decorrem duas
consequências:

Você não deve assumir que códigos hash iguais implicam igualdade de objeto.
Você nunca deve persistir ou usar um código hash fora do domínio do aplicativo
no qual ele foi criado, porque o mesmo objeto pode fazer hash entre domínios,
processos e plataformas de aplicativo.

2 Aviso

Um código hash destina-se à inserção e pesquisa eficientes em coleções baseadas


em uma tabela de hash. Um código hash não é um valor permanente. Por esta
razão:

Não serialize valores de código hash nem os armazene em bancos de dados.


Não use o código hash como a chave para recuperar um objeto de uma
coleção com chave.
Não envie códigos hash entre domínios ou processos de aplicativos. Em
alguns casos, os códigos hash podem ser computados por processo ou por
domínio de aplicativo.
Não use o código hash em vez de um valor retornado por uma função de
hash criptográfico se precisar de um hash criptograficamente forte. Para
hashes criptográficos, use uma classe derivada da
System.Security.Cryptography.HashAlgorithm classe ou
System.Security.Cryptography.KeyedHashAlgorithm .
Não teste a igualdade de códigos hash para determinar se dois objetos são
iguais. (Objetos desiguais podem ter códigos hash idênticos.) Para testar a
igualdade, chame o ReferenceEquals método ou Equals .

O GetHashCode método pode ser substituído por um tipo derivado. Se GetHashCode


não for substituído, os códigos de hash para tipos de referência serão calculados
chamando o Object.GetHashCode método da classe base, que calcula um código hash
com base na referência de um objeto; para obter mais informações, consulte
RuntimeHelpers.GetHashCode. Em outras palavras, dois objetos para os quais o
ReferenceEquals método retorna true têm códigos hash idênticos. Se os tipos de valor
não substituírem GetHashCode, o método da classe base usará reflexão para calcular o
ValueType.GetHashCode código de hash com base nos valores dos campos do tipo. Em
outras palavras, tipos de valor cujos campos têm valores iguais têm códigos de hash
iguais. Para obter mais informações sobre substituição GetHashCode, consulte a seção
"Notas para herdeiros".

2 Aviso

Se você substituir o método, você também deve substituir Equals, GetHashCode e


vice-versa. Se o método substituído Equals retornar quando dois objetos forem
testados quanto à igualdade, o método substituído GetHashCode deverá retornar
true o mesmo valor para os dois objetos.

Se um objeto usado como chave em uma tabela de hash não fornecer uma
implementação útil do , você poderá especificar um provedor de código hash
fornecendo uma implementação para uma IEqualityComparer das sobrecargas do
construtor de GetHashCodeHashtable classe.
Observações para o Tempo de Execução do
Windows
Quando você chama o método em uma classe no Tempo de Execução do Windows, ele
fornece o GetHashCode comportamento padrão para classes que não substituem
GetHashCode. Isso faz parte do suporte que o .NET fornece para o Tempo de Execução
do Windows (consulte Suporte do .NET para aplicativos da Windows Store e Tempo de
Execução do Windows). As classes no Tempo de Execução do Windows não herdam e,
no momento, não implementam Objectum GetHashCodearquivo . No entanto, eles
parecem ter ToString, Equals(Object)e métodos quando você usá-los em seu código C#
ou Visual Basic e GetHashCode o.NET Framework fornece o comportamento padrão
para esses métodos.

7 Observação

As classes do Tempo de Execução do Windows escritas em C# ou Visual Basic


podem substituir o GetHashCode método.

Exemplos
Uma das maneiras mais simples de calcular um código hash para um valor numérico
que tem o mesmo intervalo ou um intervalo menor do que o Int32 tipo é simplesmente
retornar esse valor. O exemplo a seguir mostra essa implementação para uma Number
estrutura.

C#

using System;

public struct Number


{
private int n;

public Number(int value)


{
n = value;
}

public int Value


{
get { return n; }
}

public override bool Equals(Object obj)


{
if (obj == null || ! (obj is Number))
return false;
else
return n == ((Number) obj).n;
}

public override int GetHashCode()


{
return n;
}

public override string ToString()


{
return n.ToString();
}
}

public class Example1


{
public static void Main()
{
Random rnd = new Random();
for (int ctr = 0; ctr <= 9; ctr++) {
int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
Number n = new Number(randomN);
Console.WriteLine("n = {0,12}, hash code = {1,12}", n,
n.GetHashCode());
}
}
}
// The example displays output like the following:
// n = -634398368, hash code = -634398368
// n = 2136747730, hash code = 2136747730
// n = -1973417279, hash code = -1973417279
// n = 1101478715, hash code = 1101478715
// n = 2078057429, hash code = 2078057429
// n = -334489950, hash code = -334489950
// n = -68958230, hash code = -68958230
// n = -379951485, hash code = -379951485
// n = -31553685, hash code = -31553685
// n = 2105429592, hash code = 2105429592

Frequentemente, um tipo tem vários campos de dados que podem participar da


geração do código hash. Uma maneira de gerar um código hash é combinar esses
campos usando uma XOR (eXclusive OR) operação, conforme mostrado no exemplo a
seguir.

C#

using System;
// A type that represents a 2-D point.
public struct Point2
{
private int x;
private int y;

public Point2(int x, int y)


{
this.x = x;
this.y = y;
}

public override bool Equals(Object obj)


{
if (! (obj is Point2)) return false;

Point2 p = (Point2) obj;


return x == p.x & y == p.y;
}

public override int GetHashCode()


{
return x ^ y;
}
}

public class Example3


{
public static void Main()
{
Point2 pt = new Point2(5, 8);
Console.WriteLine(pt.GetHashCode());

pt = new Point2(8, 5);


Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 13
// 13

O exemplo anterior retorna o mesmo código hash para (n1, n2) e (n2, n1) e, portanto,
pode gerar mais colisões do que o desejável. Várias soluções estão disponíveis para que
os códigos hash nesses casos não sejam idênticos. Uma delas é retornar o código hash
de um Tuple objeto que reflete a ordem de cada campo. O exemplo a seguir mostra
uma possível implementação que usa a Tuple<T1,T2> classe. Observe, no entanto, que
a sobrecarga de desempenho de instanciar um objeto pode afetar significativamente o
desempenho geral de um aplicativo que armazena um Tuple grande número de
objetos em tabelas de hash.

C#
using System;

public struct Point3


{
private int x;
private int y;

public Point3(int x, int y)


{
this.x = x;
this.y = y;
}

public override bool Equals(Object obj)


{
if (obj is Point3)
{
Point3 p = (Point3) obj;
return x == p.x & y == p.y;
}
else
{
return false;
}
}

public override int GetHashCode()


{
return Tuple.Create(x, y).GetHashCode();
}
}

public class Example


{
public static void Main()
{
Point3 pt = new Point3(5, 8);
Console.WriteLine(pt.GetHashCode());

pt = new Point3(8, 5);


Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 173
// 269

Uma segunda solução alternativa envolve a ponderação dos códigos hash individuais
deslocando para a esquerda os códigos hash de campos sucessivos em dois ou mais
bits. Idealmente, os bits deslocados além do bit 31 devem ser enrolados em vez de
serem descartados. Como os bits são descartados pelos operadores de deslocamento à
esquerda em C# e Visual Basic, isso requer a criação de um método de deslocamento e
encapsulamento à esquerda como o seguinte:

C#

public int ShiftAndWrap(int value, int positions)


{
positions = positions & 0x1F;

// Save the existing bit pattern, but interpret it as an unsigned


integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions)
| wrapped), 0);
}

O exemplo a seguir usa esse método shift-and-wrap para calcular o Point código hash
da estrutura usada nos exemplos anteriores.

C#

using System;

public struct Point


{
private int x;
private int y;

public Point(int x, int y)


{
this.x = x;
this.y = y;
}

public override bool Equals(Object obj)


{
if (!(obj is Point)) return false;

Point p = (Point) obj;


return x == p.x & y == p.y;
}

public override int GetHashCode()


{
return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
}

private int ShiftAndWrap(int value, int positions)


{
positions = positions & 0x1F;

// Save the existing bit pattern, but interpret it as an unsigned


integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value),
0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number <<
positions) | wrapped), 0);
}
}

public class Example2


{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());

pt = new Point(8, 5);


Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 28
// 37

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.Object.ToString
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Object.ToString é um método de formatação comum no .NET. Ele converte um objeto


em sua representação de cadeia de caracteres para que ele seja adequado para
exibição. (Para obter informações sobre o suporte à formatação no .NET, consulte Tipos
de formatação.) As implementações padrão do método retornam o nome totalmente
qualificado do tipo do Object.ToString objeto.

) Importante

Você pode ter acessado esta página seguindo o link da lista de membros de outro
tipo. Isso porque esse tipo não substitui Object.ToString. Em vez disso, ele herda a
Object.ToString funcionalidade do método.

Os tipos frequentemente substituem o Object.ToString método para fornecer uma


representação de cadeia de caracteres mais adequada de um tipo específico. Os tipos
também sobrecarregam frequentemente o Object.ToString método para fornecer
suporte para cadeias de caracteres de formato ou formatação sensível à cultura.

O método padrão Object.ToString()


A implementação padrão do ToString método retorna o nome totalmente qualificado
do tipo do Object, como mostra o exemplo a seguir.

C#

Object obj = new Object();


Console.WriteLine(obj.ToString());

// The example displays the following output:


// System.Object

Como Object é a classe base de todos os tipos de referência no .NET, esse


comportamento é herdado por tipos de referência que não substituem o ToString
método. O exemplo a seguir ilustra essa situação. Ele define uma classe chamada
Object1 que aceita a implementação padrão de todos os Object membros. Seu ToString

método retorna o nome do tipo totalmente qualificado do objeto.


C#

using System;
using Examples;

namespace Examples
{
public class Object1
{
}
}

public class Example5


{
public static void Main()
{
object obj1 = new Object1();
Console.WriteLine(obj1.ToString());
}
}
// The example displays the following output:
// Examples.Object1

Substitua o método Object.ToString()


Os tipos geralmente substituem o Object.ToString método para retornar uma cadeia de
caracteres que representa a instância do objeto. Por exemplo, os tipos base como Char,
Int32e String fornecem ToString implementações que retornam a forma de cadeia de
caracteres do valor que o objeto representa. O exemplo a seguir define uma classe, ,
Object2 que substitui o método para retornar o ToString nome do tipo junto com seu

valor.

C#

using System;

public class Object2


{
private object value;

public Object2(object value)


{
this.value = value;
}

public override string ToString()


{
return base.ToString() + ": " + value.ToString();
}
}

public class Example6


{
public static void Main()
{
Object2 obj2 = new Object2('a');
Console.WriteLine(obj2.ToString());
}
}
// The example displays the following output:
// Object2: a

A tabela a seguir lista as categorias de tipo no .NET e indica se elas substituem ou não o
Object.ToString método.

ノ Expandir a tabela

Categoria do tipo Substitui Object.ToString() Comportamental

Classe N/D N/D

Estrutura Sim (ValueType.ToString) Mesmo que Object.ToString()

Enumeração Sim (Enum.ToString()) O nome do membro

Interface Não N/D

Delegar Não N/D

Consulte a seção Notas aos herdeiros para obter informações adicionais sobre a
substituição ToStringdo .

Sobrecarregar o método ToString


Além de substituir o método sem Object.ToString() parâmetros, muitos tipos
sobrecarregam o ToString método para fornecer versões do método que aceitam
parâmetros. Mais comumente, isso é feito para fornecer suporte para formatação
variável e formatação sensível à cultura.

O exemplo a seguir sobrecarrega o método para retornar uma cadeia de caracteres de


resultado que inclui o ToString valor de vários campos de uma Automobile classe. Ele
define quatro cadeias de caracteres de formato: G, que retorna o nome do modelo e o
ano; D, que retorna o nome do modelo, ano e número de portas; C, que retorna o nome
do modelo, o ano e o número de cilindros; e A, que retorna uma cadeia de caracteres
com todos os quatro valores de campo.
C#

using System;

public class Automobile


{
private int _doors;
private string _cylinders;
private int _year;
private string _model;

public Automobile(string model, int year , int doors,


string cylinders)
{
_model = model;
_year = year;
_doors = doors;
_cylinders = cylinders;
}

public int Doors


{ get { return _doors; } }

public string Model


{ get { return _model; } }

public int Year


{ get { return _year; } }

public string Cylinders


{ get { return _cylinders; } }

public override string ToString()


{
return ToString("G");
}

public string ToString(string fmt)


{
if (string.IsNullOrEmpty(fmt))
fmt = "G";

switch (fmt.ToUpperInvariant())
{
case "G":
return string.Format("{0} {1}", _year, _model);
case "D":
return string.Format("{0} {1}, {2} dr.",
_year, _model, _doors);
case "C":
return string.Format("{0} {1}, {2}",
_year, _model, _cylinders);
case "A":
return string.Format("{0} {1}, {2} dr. {3}",
_year, _model, _doors, _cylinders);
default:
string msg = string.Format("'{0}' is an invalid format string",
fmt);
throw new ArgumentException(msg);
}
}
}

public class Example7


{
public static void Main()
{
var auto = new Automobile("Lynx", 2016, 4, "V8");
Console.WriteLine(auto.ToString());
Console.WriteLine(auto.ToString("A"));
}
}
// The example displays the following output:
// 2016 Lynx
// 2016 Lynx, 4 dr. V8

O exemplo a seguir chama o método sobrecarregado Decimal.ToString(String,


IFormatProvider) para exibir a formatação sensível à cultura de um valor de moeda.

C#

using System;
using System.Globalization;

public class Example8


{
public static void Main()
{
string[] cultureNames = { "en-US", "en-GB", "fr-FR",
"hr-HR", "ja-JP" };
Decimal value = 1603.49m;
foreach (var cultureName in cultureNames) {
CultureInfo culture = new CultureInfo(cultureName);
Console.WriteLine("{0}: {1}", culture.Name,
value.ToString("C2", culture));
}
}
}
// The example displays the following output:
// en-US: $1,603.49
// en-GB: £1,603.49
// fr-FR: 1 603,49 €
// hr-HR: 1.603,49 kn
// ja-JP: ¥1,603.49
Para obter mais informações sobre cadeias de caracteres de formato e formatação
sensível à cultura, consulte Tipos de formatação. Para obter as cadeias de caracteres de
formato suportadas por valores numéricos, consulte Cadeias de caracteres de formato
numérico padrão e Cadeias de caracteres de formato numérico personalizado. Para
obter as cadeias de caracteres de formato suportadas por valores de data e hora,
consulte Cadeias de caracteres de formato de data e hora padrão e Cadeias de
caracteres de formato de data e hora personalizadas.

Estender o método Object.ToString


Como um tipo herda o método padrão Object.ToString , você pode achar seu
comportamento indesejável e deseja alterá-lo. Isso é particularmente verdadeiro para
matrizes e classes de coleção. Embora você possa esperar que o método de uma matriz
ou classe de coleção exiba os valores de seus membros, ele exibe o nome do tipo
totalmente qualificado, como mostra o ToString exemplo a seguir.

C#

int[] values = { 1, 2, 4, 8, 16, 32, 64, 128 };


Console.WriteLine(values.ToString());

List<int> list = new List<int>(values);


Console.WriteLine(list.ToString());

// The example displays the following output:


// System.Int32[]
// System.Collections.Generic.List`1[System.Int32]

Você tem várias opções para produzir a cadeia de caracteres de resultado desejada.

Se o tipo for uma matriz, um objeto de coleção ou um objeto que implementa as


IEnumerable interfaces ou , você pode enumerar seus elementos usando a
instrução em C# ou IEnumerable<T> a foreach For Each...Next construção no
Visual Basic.

Se a classe não sealed for (em C#) ou NotInheritable (em Visual Basic), você
poderá desenvolver uma classe wrapper que herda da classe base cujo
Object.ToString método você deseja personalizar. No mínimo, isso requer que você
faça o seguinte:

1. Implemente todos os construtores necessários. As classes derivadas não


herdam seus construtores de classe base.
2. Substitua o Object.ToString método para retornar a cadeia de caracteres de
resultado desejada.

O exemplo a seguir define uma classe wrapper para a List<T> classe. Ele substitui
o método para exibir o Object.ToString valor de cada método da coleção em vez
do nome do tipo totalmente qualificado.

C#

using System;
using System.Collections.Generic;

public class CList<T> : List<T>


{
public CList(IEnumerable<T> collection) : base(collection)
{ }

public CList() : base()


{}

public override string ToString()


{
string retVal = string.Empty;
foreach (T item in this) {
if (string.IsNullOrEmpty(retVal))
retVal += item.ToString();
else
retVal += string.Format(", {0}", item);
}
return retVal;
}
}

public class Example2


{
public static void Main()
{
var list2 = new CList<int>();
list2.Add(1000);
list2.Add(2000);
Console.WriteLine(list2.ToString());
}
}
// The example displays the following output:
// 1000, 2000

Desenvolva um método de extensão que retorne a cadeia de caracteres de


resultado desejada. Observe que você não pode substituir o método padrão
Object.ToString dessa maneira, ou seja, sua classe de extensão (em C#) ou módulo
(em Visual Basic) não pode ter um método sem parâmetros chamado que é
chamado ToString no lugar do método do ToString tipo original. Você terá que
fornecer algum outro nome para sua substituição sem ToString parâmetros.

O exemplo a seguir define dois métodos que estendem a List<T> classe: um


método sem ToString2 parâmetros e um método com um ToString String
parâmetro que representa uma cadeia de caracteres de formato.

C#

using System;
using System.Collections.Generic;

public static class StringExtensions


{
public static string ToString2<T>(this List<T> l)
{
string retVal = string.Empty;
foreach (T item in l)
retVal += string.Format("{0}{1}", string.IsNullOrEmpty(retVal)
?
"" : ", ",
item);
return string.IsNullOrEmpty(retVal) ? "{}" : "{ " + retVal + "
}";
}

public static string ToString<T>(this List<T> l, string fmt)


{
string retVal = string.Empty;
foreach (T item in l) {
IFormattable ifmt = item as IFormattable;
if (ifmt != null)
retVal += string.Format("{0}{1}",
string.IsNullOrEmpty(retVal) ?
"" : ", ", ifmt.ToString(fmt,
null));
else
retVal += ToString2(l);
}
return string.IsNullOrEmpty(retVal) ? "{}" : "{ " + retVal + "
}";
}
}

public class Example3


{
public static void Main()
{
List<int> list = new List<int>();
list.Add(1000);
list.Add(2000);
Console.WriteLine(list.ToString2());
Console.WriteLine(list.ToString("N0"));
}
}
// The example displays the following output:
// { 1000, 2000 }
// { 1,000, 2,000 }

Observações para o Tempo de Execução do


Windows
Quando você chama o método em uma classe no Tempo de Execução do Windows, ele
fornece o ToString comportamento padrão para classes que não substituem ToString.
Isso faz parte do suporte que o .NET fornece para o Tempo de Execução do Windows
(consulte Suporte do .NET para aplicativos da Windows Store e Tempo de Execução do
Windows). As classes no Tempo de Execução do Windows não herdam Objecte nem
sempre implementam um ToStringarquivo . No entanto, eles sempre parecem ter
ToString, Equals(Object)e métodos quando você usá-los em seu código C# ou Visual
Basic e GetHashCode .NET fornece um comportamento padrão para esses métodos.

O Common Language Runtime usa IStringable.ToString em um objeto do Tempo de


Execução do Windows antes de retornar à implementação padrão do Object.ToString.

7 Observação

As classes do Tempo de Execução do Windows escritas em C# ou Visual Basic


podem substituir o ToString método.

O Tempo de Execução do Windows e a Interface


IStringable
O Tempo de Execução do Windows inclui uma interface IStringable cujo método único,
IStringable.ToString, fornece suporte básico de formatação comparável ao fornecido
pelo Object.ToString. Para evitar ambiguidade, você não deve implementar IStringable
em tipos gerenciados.

Quando objetos gerenciados são chamados por código nativo ou por código escrito em
linguagens como JavaScript ou C++/CX, eles parecem implementar IStringable. O
Common Language Runtime roteia automaticamente chamadas de IStringable.ToString
para Object.ToString se IStringable não estiver implementado no objeto gerenciado.
2 Aviso

Como o Common Language Runtime implementa automaticamente o IStringable


para todos os tipos gerenciados em aplicativos da Windows Store, recomendamos
que você não forneça sua própria IStringable implementação. A implementação
IStringable pode resultar em comportamento não intencional ao chamar

ToString do Tempo de Execução do Windows, C++/CX ou JavaScript.

Se você optar por implementar IStringable em um tipo gerenciado público exportado


em um componente do Tempo de Execução do Windows, as seguintes restrições serão
aplicadas:

Você pode definir a interface IStringable somente em uma relação "implementa


classe", da seguinte maneira:

C#

public class NewClass : IStringable

Não é possível implementar IStringable em uma interface.

Não é possível declarar um parâmetro do tipo IStringable.

IStringable não pode ser o tipo de retorno de um método, propriedade ou campo.

Você não pode ocultar sua implementação IStringable de classes base usando uma
definição de método como a seguinte:

C#

public class NewClass : IStringable


{
public new string ToString()
{
return "New ToString in NewClass";
}
}

Em vez disso, a implementação IStringable.ToString deve sempre substituir a


implementação de classe base. Você pode ocultar uma implementação de
ToString invocando-a apenas em uma instância da classe fortemente tipada.

Sob uma variedade de condições, chamadas de código nativo para um tipo gerenciado
que implementa IStringable ou oculta sua implementação ToString podem produzir um
comportamento inesperado.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Nullable classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A Nullable classe oferece suporte a tipos de valor que podem ser atribuídos null .

Um tipo é dito ser anulável se ele pode ser atribuído um valor ou pode ser atribuído
null , o que significa que o tipo não tem nenhum valor. Por padrão, todos os tipos de

referência, como , são anuláveis, mas todos os tipos de valor, como StringInt32, não são.

Em C# e Visual Basic, você marca um tipo de valor como anulável usando a ? notação
após o tipo de valor. Por exemplo, int? em C# ou Integer? no Visual Basic declara um
tipo de valor inteiro que pode ser atribuído null .

A Nullable turma dá suporte complementar para a Nullable<T> estrutura. A Nullable


classe oferece suporte à obtenção do tipo subjacente de um tipo anulável e operações
de comparação e igualdade em pares de tipos anuláveis cujo tipo de valor subjacente
não oferece suporte a operações genéricas de comparação e igualdade.

Conversão boxing e unboxing


Quando um tipo anulável é encaixotado, o Common Language Runtime encaixota
automaticamente o valor subjacente do Nullable<T> objeto, não o Nullable<T> objeto
em si. Ou seja, se a HasValue propriedade for true , o conteúdo da Value propriedade
será encaixotado.

Se a HasValue propriedade de um tipo anulável for false , o resultado da operação de


boxe será null . Quando o valor subjacente de um tipo anulável é unboxed, o Common
Language Runtime cria uma nova Nullable<T> estrutura inicializada para o valor
subjacente.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações
de pull. Para obter mais  Abrir um problema de
informações, confira o nosso documentação
guia para colaboradores.
 Fornecer comentários sobre o
produto
Generics in .NET (Genéricos no .NET)
Artigo • 27/01/2024

Os genéricos permitem que você personalize um método, uma classe, uma estrutura ou
uma interface para o tipo exato de dados no qual ele atua. Por exemplo, em vez de usar
a classe Hashtable, que permite que as chaves e os valores sejam de qualquer tipo, você
pode usar a classe genérica Dictionary<TKey,TValue> e especificar os tipos permitidos
para a chave e o valor. Entre os benefícios de genéricos estão maior reutilização de
códigos e segurança de tipos.

Definindo e usando genéricos


Genéricos são classes, estruturas, interfaces e métodos que possuem espaços
reservados (parâmetros de tipo) para um ou mais dos tipos que eles armazenam ou
usam. Uma classe de coleção genérica pode usar um parâmetro de tipo como um
espaço reservado para o tipo de objetos que ela armazena; os parâmetros de tipo
aparecem como os tipos de seus campos e os tipos de parâmetro de seus métodos. Um
método genérico pode usar seu parâmetro de tipo como o tipo de seu valor de retorno
ou como o tipo de um de seus parâmetros formais. O código a seguir ilustra uma
definição de classe genérica simples.

C#

public class Generic<T>


{
public T Field;
}

Quando você cria uma instância de uma classe genérica, pode especificar os tipos reais
para substituir os parâmetros de tipo. Isso estabelece uma nova classe genérica,
conhecida como uma classe genérica construída, com seus tipos escolhidos substituídos
em todos os locais em que aparecem os parâmetros de tipo. O resultado é uma classe
fortemente tipada que é personalizada para sua escolha de tipos, como mostra o
código a seguir.

C#

public static void Main()


{
Generic<string> g = new Generic<string>();
g.Field = "A string";
//...
Console.WriteLine("Generic.Field = \"{0}\"", g.Field);
Console.WriteLine("Generic.Field.GetType() = {0}",
g.Field.GetType().FullName);
}

Terminologia de genéricos
Os seguintes termos são usados para discutir genéricos no .NET:

A definição de tipo genérico é uma classe, estrutura ou declaração de interface que


funciona como um modelo, com espaços reservados para os tipos que ela pode
conter ou usar. Por exemplo, a classe
System.Collections.Generic.Dictionary<TKey,TValue> pode conter dois tipos:
chaves e valores. Como uma definição de tipo genérico é apenas um modelo, não
é possível criar instâncias de uma classe, estrutura ou interface que seja uma
definição de tipo genérico.

Parâmetros de tipo genérico ou parâmetros de tipo são os espaços reservados em


uma definição de método ou de tipo genérico. O tipo genérico
System.Collections.Generic.Dictionary<TKey,TValue> tem dois parâmetros de tipo,
TKey e TValue , que representam os tipos de suas chaves e valores.

Um tipo genérico construído ou um tipo construído é o resultado de especificação


de tipos para os parâmetros de tipo genérico de uma definição de tipo genérico.

Um argumento de tipo genérico é qualquer tipo que seja substituído por um


parâmetro de tipo genérico.

O termo geral tipo genérico inclui definições de tipo genérico e de tipos


construídos.

Covariância e contravariância de parâmetros de tipo genérico permitem que você


use tipos genéricos construídos cujos argumentos de tipo sejam mais derivados
(covariância) ou menos derivados (contravariância) de um tipo construído de
destino. A covariância e a contravariância são referidas coletivamente como
variância. Para obter mais informações, consulte Covariância e contravariância.

Restrições são limites colocados em parâmetros de tipo genérico. Por exemplo,


você pode limitar um parâmetro de tipo a tipos que implementam a interface
genérica System.Collections.Generic.IComparer<T>, para garantir que instâncias
do tipo possam ser classificadas. Você também pode restringir parâmetros de tipo
a tipos que tenham uma determinada classe base, que tenham um construtor sem
parâmetros ou que sejam tipos de referência ou tipos de valor. Os usuários do tipo
genérico não podem substituir argumentos de tipo que não satisfaçam as
restrições.

A definição de método genérico é um método com duas listas de parâmetros: uma


lista de parâmetros de tipo genérico e uma lista de parâmetros formais. Os
parâmetros de tipo podem aparecer como o tipo de retorno ou como os tipos de
parâmetros formais, como mostra o código a seguir.

C#

T Generic<T>(T arg)
{
T temp = arg;
//...
return temp;
}

Métodos genéricos podem aparecer em tipos genéricos ou não genéricos. É importante


observar que um método não é genérico apenas porque ele pertence a um tipo
genérico ou até mesmo porque possui parâmetros formais cujos tipos sejam os
parâmetros genéricos do tipo de delimitador. Um método é genérico somente se ele
tiver sua própria lista de parâmetros de tipo. No código a seguir, somente o método G
é genérico.

C#

class A
{
T G<T>(T arg)
{
T temp = arg;
//...
return temp;
}
}
class Generic<T>
{
T M(T arg)
{
T temp = arg;
//...
return temp;
}
}

Vantagens e desvantagens de genéricos


Há muitas vantagens no uso de coleções e delegados genéricos:

Segurança de tipo. Genéricos deslocam a carga de segurança de tipos do seu local


até o compilador. Não é necessário escrever código para testar o tipo de dados
correto, pois isso é aplicado no tempo de compilação. A necessidade de conversão
de tipos e a possibilidade de erros de tempo de execução são reduzidas.

Menos código e código que seja reutilizado com mais facilidade. Não é necessário
herdar de um tipo base e substituir membros. Por exemplo, o LinkedList<T> está
pronto para uso imediato. Por exemplo, você pode criar uma lista vinculada de
cadeias de caracteres com a seguinte declaração de variável:

C#

LinkedList<string> llist = new LinkedList<string>();

Melhor desempenho. Tipos de coleções genéricas geralmente executam melhor


para armazenar e manipular tipos de valor, pois não é necessário colocar os tipos
de valor em caixa.

Delegados genéricos permitem retornos fortemente tipados sem a necessidade de


criar múltiplas classes de delegados. Por exemplo, o delegado genérico
Predicate<T> permite criar um método que implementa seus próprios critérios de
pesquisa para um tipo específico e usar seu método com método do tipo Array,
tais como Find, FindLast e FindAll.

Genéricos simplificam o código gerado dinamicamente. Quando você usa


genéricos com código gerado dinamicamente, não é necessário gerar o tipo. Isso
aumenta o número de cenários nos quais você pode usar métodos dinâmicos
leves, em vez de gerar assemblies inteiros. Para obter mais informações, confira
Como definir e executar métodos dinâmicos e DynamicMethod.

Veja a seguir algumas limitações de genéricos:

Os tipos genéricos podem ser derivados da maioria das classes base, tais como
MarshalByRefObject (e restrições podem ser usadas para exigir que parâmetros do
tipo genérico derivem de classes base como MarshalByRefObject). No entanto, o
.NET não dá suporte a tipos genéricos vinculados a um contexto. Um tipo genérico
pode ser derivado de ContextBoundObject, mas tentar criar uma instância desse
tipo causa uma TypeLoadException.

Enumerações não podem ter parâmetros do tipo genérico. Uma enumeração pode
ser genérica somente incidentalmente (por exemplo, porque ela está aninhada em
um tipo genérico que é definido usando Visual Basic, C# ou C++). Para saber mais,
confira "Enumerações" em Common Type System.

Métodos dinâmicos leves não podem ser genéricos.

No Visual Basic, C# e C++, um tipo aninhado que está embutido em um tipo


genérico não pode ser instanciado, a menos que os tipos tenham sido atribuídos
aos parâmetros de tipo de todos os tipos de delimitadores. Outra maneira de dizer
isso é que em reflexão, um tipo aninhado que é definido usando essas linguagens
inclui os parâmetros de tipo de todos os seus tipos de delimitadores. Isso permite
que os parâmetros de tipos de delimitadores sejam usados nas definições de
membro de um tipo aninhado. Para obter mais informações, consulte "Tipos
aninhados" em MakeGenericType.

7 Observação

Um tipo aninhado que é definido pela emissão do código em um assembly


dinâmico ou usando o Ilasm.exe (IL Assembler) não é necessário para incluir
parâmetros de tipo de seus tipos de delimitadores; no entanto, se ele não os
incluir, os parâmetros de tipo não estarão no escopo na classe aninhada.

Para obter mais informações, consulte "Tipos aninhados" em MakeGenericType.

Biblioteca de classes e suporte ao idioma


O .NET fornece várias classes de coleção genérica nos seguintes namespaces:

O namespace System.Collections.Generic contém a maioria dos tipos de coleção


genérica fornecida pelo .NET, tal como as classes genéricas List<T> e
Dictionary<TKey,TValue>.

O namespace System.Collections.ObjectModel contém tipos adicionais de coleção


genérica, tais como a classe genérica ReadOnlyCollection<T>, que são úteis para
expor modelos de objeto para usuários de suas classes.

Interfaces genéricas para a implementação de comparações de classificação e de


igualdade são fornecidas no namespace System, juntamente com tipos de delegados
genéricos para manipuladores de eventos, conversões e predicados de pesquisa.

Foi adicionado suporte para genéricos para o namespace System.Reflection para


examinar tipos genéricos e métodos genéricos, para System.Reflection.Emit para emitir
assemblies dinâmicos que contenham tipos e métodos genéricos e para
System.CodeDom para gerar gráficos de origem que incluem genéricos.

O Common Language Runtime fornece novos opcodes e prefixos para oferecer suporte
a tipos genéricos na Microsoft Intermediate Language (MSIL), incluindo Stelem, Ldelem,
Unbox_Any, Constrained e Readonly.

Visual C++, C# e Visual Basic todos oferecem suporte completo para definir e usar
genéricos. Para saber mais sobre suporte à linguagem, confira Tipos genéricos no Visual
Basic, Introdução aos genéricos e Visão geral dos genéricos no Visual C++.

Tipos e genéricos aninhados


Um tipo que é aninhado em um tipo genérico pode depender dos parâmetros de tipo
do tipo genérico de delimitador. O Common Language Runtime considera tipos
aninhados como genéricos, mesmo que eles não tenham seus próprios parâmetros de
tipo genérico. Quando você cria uma instância de um tipo aninhado, deverá especificar
argumentos de tipo para todos os tipos de delimitadores.

Tópicos Relacionados
ノ Expandir a tabela

Título Descrição

Coleções genéricas no .NET Descreve as classes de coleção genérica e outros tipos


genéricos no .NET.

Delegados genéricos para Descreve delegados genéricos para conversões, predicados de


manipulação de matrizes e pesquisa e ações a serem tomadas nos elementos de uma
listas matriz ou coleção.

Interfaces genéricas Descreve interfaces genéricas que fornecem funcionalidade


comum entre famílias de tipos genéricos.

Covariância e Contravariância Descreve covariância e contravariância em parâmetros de tipo


genérico.

Tipos de Coleção de Uso Fornece informações de resumo sobre as características e os


Comum cenários de uso dos tipos de coleção no .NET, incluindo tipos
genéricos.

Quando Usar Coleções Descreve regras gerais para determinar quando usar tipos de
Genéricas coleção genérica.
Título Descrição

Como: Definir um tipo genérico Explica como gerar assemblies dinâmicos que incluem tipos e
com a emissão de reflexão métodos genéricos.

Tipos genéricos no Visual Basic Descreve o recurso genérico para usuários do Visual Basic,
incluindo tópicos de instruções para uso e definição de tipos
genéricos.

Introdução aos genéricos Fornece uma visão geral da definição e do uso de tipos
genéricos para usuários do C#.

Visão geral de genéricos no Descreve o recurso de genéricos para usuários do C++,


Visual C++ incluindo as diferenças entre genéricos e modelos.

Referência
System.Collections.Generic

System.Collections.ObjectModel

System.Reflection.Emit.OpCodes

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Visão geral de tipos genéricos
Artigo • 09/05/2023

Desenvolvedores usam genéricos o tempo todo no .NET, seja implícita ou


explicitamente. Ao usar o LINQ no .NET, você já percebeu que você está trabalhando
com IEnumerable<T>? Ou, no caso de você já ter visto um exemplo de um "repositório
genérico" online para conversar com bancos de dados usando o Entity Framework, você
já viu que a maioria dos métodos retornam IQueryable<T> ? Talvez você tenha se
perguntado o que é o T nesses exemplos e por que ele está lá.

Introduzidos pela primeira vez no .NET Framework 2.0, os genéricos são essencialmente
um "modelo de código" que permite aos desenvolvedores definir estruturas de dados
fortemente tipadas sem se comprometer com um tipo de dados real. Por exemplo,
List<T> é uma coleção de genéricos que pode ser declarada e usada com qualquer tipo,
como List<int> , List<string> ou List<Person> .

Para entender por que os genéricos são úteis, vamos dar uma olhada em uma classe
específica antes e depois adicionar os genéricos: ArrayList. No .NET Framework 1.0, os
elementos ArrayList eram do tipo Object. Qualquer elemento adicionado à coleção foi
silenciosamente convertido em um Object . O mesmo aconteceria ao ler os elementos
da lista. Esse processo é conhecido como conversão boxing e unboxing e afeta o
desempenho. Além do desempenho, no entanto, não há como determinar o tipo de
dados na lista em tempo de compilação, o que torna algum código frágil. Genéricos
resolvem esse problema definindo o tipo de dados que cada instância de lista conterá.
Resumindo, você só pode adicionar inteiros a List<int> e só pode adicionar Pessoas a
List<Person> .

Genéricos também estão disponíveis em tempo de execução. O runtime sabe que tipo
de estrutura de dados você está usando e pode armazená-la na memória com mais
eficiência.

O exemplo a seguir é um pequeno programa que ilustra a eficiência de saber o tipo da


estrutura de dados em tempo de execução:

C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;

namespace GenericsExample {
class Program {
static void Main(string[] args) {
//generic list
List<int> ListGeneric = new List<int> { 5, 9, 1, 4 };
//non-generic list
ArrayList ListNonGeneric = new ArrayList { 5, 9, 1, 4 };
// timer for generic list sort
Stopwatch s = Stopwatch.StartNew();
ListGeneric.Sort();
s.Stop();
Console.WriteLine($"Generic Sort: {ListGeneric} \n Time taken:
{s.Elapsed.TotalMilliseconds}ms");

//timer for non-generic list sort


Stopwatch s2 = Stopwatch.StartNew();
ListNonGeneric.Sort();
s2.Stop();
Console.WriteLine($"Non-Generic Sort: {ListNonGeneric} \n Time
taken: {s2.Elapsed.TotalMilliseconds}ms");
Console.ReadLine();
}
}
}

Este programa produz uma saída semelhante à seguinte:

Console

Generic Sort: System.Collections.Generic.List`1[System.Int32]


Time taken: 0.0034ms
Non-Generic Sort: System.Collections.ArrayList
Time taken: 0.2592ms

A primeira coisa que você pode observar aqui é que classificar a lista genérica é
significativamente mais rápido do que classificar a lista não genérica. Você também
observará que o tipo de lista genérico é distinto ([System.Int32]) enquanto o tipo da lista
não genérico é generalizado. Como o runtime sabe que o genérico List<int> é do tipo
Int32, ele pode armazenar elementos de lista em uma matriz de inteiros subjacente na
memória, enquanto o ArrayList não genérico tem que converter cada elemento da lista
em um objeto. Como este exemplo mostra, as conversões extras levam tempo e
reduzem a velocidade da classificação da lista.

Uma vantagem adicional de o runtime saber o tipo de seu genérico é uma melhor
experiência de depuração. Quando você está depurando um genérico em C#, você sabe
que tipo de cada elemento está na sua estrutura de dados. Sem os genéricos, você não
faria ideia de qual o tipo de cada elemento.

Confira também
Guia de Programação em C# – Genéricos
Coleções genéricas no .NET
Artigo • 10/05/2023

A biblioteca de classes do .NET fornece várias classes de coleção genérica nos


namespaces System.Collections.Generic e System.Collections.ObjectModel. Para obter
informações detalhadas sobre essas classes, consulte Tipos de coleção comumente
usados.

System.Collections.Generic
Muitos dos tipos de coleção genéricos são diretamente análogos aos tipos não
genéricos. Dictionary<TKey,TValue> é uma versão genérica de Hashtable; ele usa a
estrutura genérica KeyValuePair<TKey,TValue> para enumeração em vez de
DictionaryEntry.

List<T> é uma versão genérica de ArrayList. Há classes Queue<T> e Stack<T> genéricas


que correspondem às versões não genéricas.

Há versões genéricas e não genéricas de SortedList<TKey,TValue>. As duas versões são


híbridas de um dicionário e de uma lista. A classe genérica
SortedDictionary<TKey,TValue> é um dicionário puro e não tem nenhum equivalente
não genérico.

A classe genérica LinkedList<T> é uma verdadeira lista vinculada. Não tem nenhum
equivalente não genérico.

System.Collections.ObjectModel
A classe genérica Collection<T> fornece uma classe base para derivar seus próprios
tipos de coleção genérica. A classe ReadOnlyCollection<T> fornece uma maneira fácil
de produzir uma coleção somente leitura de qualquer tipo que implementa a interface
genérica IList<T>. A classe genérica KeyedCollection<TKey,TItem> fornece uma maneira
de armazenar objetos que contêm suas próprias chaves.

Outros tipos genéricos


A estrutura genérica Nullable<T> permite que você use tipos de valor como se eles
pudessem ser atribuídos null . Isso pode ser útil ao trabalhar com consultas de banco
de dados, nas quais os campos que contêm tipos de valor podem estar ausentes. O
parâmetro de tipo genérico pode ser qualquer tipo de valor.
7 Observação

No C# e Visual Basic não é necessário usar Nullable<T> explicitamente, pois a


linguagem tem sintaxe para tipos que permitem valor nulo. Consulte Tipos que
permitem valor nulo (Guia de programação em C#) e Tipos de valor que
permitem valor nulo (Visual Basic).

A estrutura genérica ArraySegment<T> fornece uma maneira de delimitar um intervalo


de elementos dentro de uma matriz unidimensional baseada em zero de qualquer tipo.
O parâmetro de tipo genérico é o tipo dos elementos da matriz.

O delegado genérico EventHandler<TEventArgs> elimina a necessidade de declarar um


tipo de delegado para manipular eventos, se o evento seguir o padrão de manipulação
de eventos usado por .NET. Por exemplo, vamos supor que você tenha criado uma
classe MyEventArgs , derivada de EventArgs, para manter os dados para o evento. Em
seguida, você pode declarar o evento da seguinte maneira:

C#

public event EventHandler<MyEventArgs> MyEvent;

Confira também
System.Collections.Generic
System.Collections.ObjectModel
Genéricos
Delegados genéricos para manipulação de matrizes e listas
Interfaces genéricas
Delegados genéricos para manipulação
de matrizes e listas
Artigo • 10/05/2023

Este tópico fornece uma visão geral de delegados genéricos para conversões,
predicados de pesquisa e ações a serem tomadas nos elementos de uma matriz ou
coleção.

Delegados genéricos para manipulação de


matrizes e listas
O delegado Action<T> genérico representa um método que executa uma ação em um
elemento do tipo especificado. Você pode criar um método que executa a ação
desejada no elemento, criar uma instância do delegado Action<T> para representar
esse método e, em seguida, passar a matriz e o delegado para o método Array.ForEach
genérico estático. O método é chamado para cada elemento da matriz.

A classe genérica List<T> também fornece um método ForEach que usa o delegado
Action<T>. Este método não é genérico.

7 Observação

Isso é um argumento interessante sobre tipos e métodos genéricos. O método


Array.ForEach deve ser estático ( Shared no Visual Basic) e genérico porque Array
não é um tipo genérico; o único motivo pelo qual você pode especificar um tipo
em que o Array.ForEach deve operar é que o método tenha sua própria lista de
parâmetros de tipo. Por outro lado, o método List<T>.ForEach não genérico
pertence à classe genérica List<T>; portanto, ele simplesmente usa o parâmetro de
tipo de sua classe. A classe é fortemente tipada; portanto, o método pode ser um
método de instância.

O delegado Predicate<T> genérico representa um método que determina se um


determinado elemento atende aos critérios que você definir. Você pode usá-lo com os
seguintes métodos genéricos estáticos de Array para procurar um elemento ou um
conjunto de elementos: Exists, Find, FindAll, FindIndex, FindLast, FindLastIndex e
TrueForAll.
O Predicate<T> também funciona com os métodos de instância não genérica
correspondentes da classe genérica List<T>.

O delegado Comparison<T> genérico permite que você forneça uma ordem de


classificação para elementos de matriz ou lista que não têm uma ordem de classificação
nativa, ou para substituir a ordem de classificação nativa. Crie um método que executa a
comparação, crie uma instância do delegado Comparison<T> para representar o
método e, em seguida, passe a matriz e o delegado para o método Array.Sort<T>(T[],
Comparison<T>) genérico estático. A classe List<T> genérica fornece uma sobrecarga
do método de instância correspondente, List<T>.Sort(Comparison<T>).

O delegado Converter<TInput,TOutput> genérico permite que você defina uma


conversão entre dois tipos e para converter uma matriz de um tipo em uma matriz do
outro ou para converter uma lista de um tipo em uma lista do outro. Crie um método
que converte os elementos de uma lista existente para um novo tipo, crie uma instância
delegada para representar o método e usar o método Array.ConvertAll estático genérico
para produzir uma matriz do novo tipo da matriz original, ou o método de instância
List<T>.ConvertAll<TOutput>(Converter<T,TOutput>) genérico para produzir uma lista
do novo tipo da lista original.

Encadeando delegados
Muitos dos métodos que usam esses delegados retornam uma matriz ou lista, que pode
ser passada para outro método. Por exemplo, se você quiser selecionar determinados
elementos de uma matriz, converta esses elementos em um novo tipo e salve-os em
uma nova matriz, você pode passar a matriz retornada pelo método FindAll genérico
para o método ConvertAll genérico. Se o novo tipo de elemento não tiver uma ordem
de classificação natural, você poderá passar a matriz retornada pelo método ConvertAll
genérico para o método Sort<T>(T[], Comparison<T>) genérico.

Confira também
System.Collections.Generic
System.Collections.ObjectModel
Genéricos
Coleções genéricas no .NET
Interfaces genéricas
Covariância e Contravariância
Matemática genérica
Artigo • 10/05/2023

O .NET 7 apresenta novas interfaces genéricas relacionadas à matemática à biblioteca


de classes base. A disponibilidade dessas interfaces significa que você pode restringir
um parâmetro de tipo de um método ou tipo genérico para ser "semelhante a um
número". Além disso, o C# 11 e posterior permite definir membros da interface static
virtual. Como os operadores devem ser declarados como static , esse novo recurso de
C# permite que os operadores sejam declarados nas novas interfaces para tipos
semelhantes a números.

Juntas, essas inovações permitem executar operações matemáticas genericamente, ou


seja, sem precisar saber o tipo exato com o qual você está trabalhando. Por exemplo, se
você quisesse escrever um método que adicionasse dois números, anteriormente você
precisava adicionar uma sobrecarga do método para cada tipo (por exemplo, static
int Add(int first, int second) e static float Add(float first, float second) ).

Agora você pode escrever um único método genérico, em que o parâmetro de tipo é
restrito a ser um tipo semelhante a um número. Por exemplo:

C#

static T Add<T>(T left, T right)


where T : INumber<T>
{
return left + right;
}

Nesse método, o parâmetro de tipo T é restrito a ser um tipo que implementa a nova
interface INumber<TSelf>. INumber<TSelf> implementa a interface
IAdditionOperators<TSelf,TOther,TResult>, que contém o operador +. Isso permite que
o método adicione genericamente os dois números. O método pode ser usado com
qualquer um dos tipos numéricos internos do NET, pois todos eles foram atualizados
para implementar INumber<TSelf> no .NET 7.

Os autores da biblioteca se beneficiarão mais das interfaces matemáticas genéricas, pois


podem simplificar sua base de código removendo sobrecargas "redundantes". Outros
desenvolvedores se beneficiarão indiretamente, pois as APIs que consomem podem
começar a dar suporte a mais tipos.

As interfaces
As interfaces foram projetadas para serem refinadas o suficiente para que os usuários
possam definir as próprias interfaces sobre elas, além de serem granulares o suficiente
para serem fáceis de consumir. Nessa medida, há algumas interfaces numéricas
principais com as quais a maioria dos usuários interagirá, como INumber<TSelf> e
IBinaryInteger<TSelf>. As interfaces mais refinadas, como
IAdditionOperators<TSelf,TOther,TResult> e ITrigonometricFunctions<TSelf>, dão
suporte a esses tipos e estão disponíveis para desenvolvedores que definem as próprias
interfaces numéricas específicas do domínio.

Interfaces numéricas
Interfaces de operador
Interfaces de função
Interfaces de análise e formatação

Interfaces numéricas
Esta seção descreve as interfaces em System.Numerics que descrevem tipos
semelhantes a números e a funcionalidade disponível para eles.

Nome da interface Descrição

IBinaryFloatingPointIeee754<TSelf> Expõe APIs comuns aos tipos de ponto flutuante binário1


que implementam o padrão IEEE 754.

IBinaryInteger<TSelf> Expõe APIs comuns a inteiros binários2.

IBinaryNumber<TSelf> Expõe APIs comuns a números binários.

IFloatingPoint<TSelf> Expõe APIs comuns a tipos de ponto flutuante.

IFloatingPointIeee754<TSelf> Expõe APIs comuns aos tipos de ponto flutuante que


implementam o padrão IEEE 754.

INumber<TSelf> Expõe APIs comuns a tipos de número comparáveis


(efetivamente, o domínio numérico "real").

INumberBase<TSelf> Expõe APIs comuns a todos os tipos de número


(efetivamente, o domínio numérico "complexo").

ISignedNumber<TSelf> Expõe APIs comuns a todos os tipos de número com sinal


(como o conceito de NegativeOne ).

IUnsignedNumber<TSelf> Expõe APIs comuns a todos os tipos de número sem sinal.

IAdditiveIdentity<TSelf,TResult> Expõe o conceito de (x + T.AdditiveIdentity) == x .

IMinMaxValue<TSelf> Expõe o conceito de T.MinValue e T.MaxValue .


Nome da interface Descrição

IMultiplicativeIdentity<TSelf,TResult> Expõe o conceito de (x * T.MultiplicativeIdentity) ==


x.

1Os tipos de ponto flutuante binários são Double ( double ), Half e Single ( float ).

2
Os tipos inteiros binários são Byte ( byte ), Int16 ( short ), Int32 ( int ), Int64 ( long ),
Int128, IntPtr ( nint ), SByte ( sbyte ), UInt16 ( ushort ), UInt32 ( uint ), UInt64 ( ulong ),
UInt128 e UIntPtr ( nuint ).

A interface que você provavelmente usará diretamente é INumber<TSelf>, que


corresponde aproximadamente a um número real. Se um tipo implementar essa
interface, isso significa que um valor tem um sinal (isso inclui tipos unsigned , que são
considerados positivos) e pode ser comparado a outros valores do mesmo tipo.
INumberBase<TSelf> confere conceitos mais avançados, como números complexos e
imaginários, por exemplo, a raiz quadrada de um número negativo. Outras interfaces,
como IFloatingPointIeee754<TSelf>, foram criadas porque nem todas as operações
fazem sentido para todos os tipos de número — por exemplo, calcular o piso de um
número só faz sentido para tipos de ponto flutuante. Na biblioteca de classes base do
.NET, o tipo de ponto flutuante Double implementa IFloatingPointIeee754<TSelf>, mas
Int32 não implementa.

Várias das interfaces também são implementadas por vários outros tipos, incluindo
Char, DateOnly, DateTime, DateTimeOffset, Decimal, Guid, TimeOnly e TimeSpan.

A tabela a seguir mostra algumas das APIs principais expostas por cada interface.

Interface Nome da API Descrição

IBinaryInteger<TSelf> DivRem Calcula o quociente e o resto


simultaneamente.

LeadingZeroCount Conta o número de bits zero à esquerda na


representação binária.

PopCount Conta o número de bits definidos na


representação binária.

RotateLeft Gira bits para a esquerda, às vezes também


chamado de deslocamento circular à
esquerda.

RotateRight Gira bits para a direita, às vezes também


chamado de deslocamento circular para a
direita.
Interface Nome da API Descrição

TrailingZeroCount Conta o número de bits zero à direita na


representação binária.

IFloatingPoint<TSelf> Ceiling Arredonda o valor para o infinito positivo.


+4,5 torna-se +5 e -4,5 torna-se -4.

Floor Arredonda o valor para o infinito negativo.


+4,5 torna-se +4 e -4,5 torna-se -5.

Round Arredonda o valor usando o modo de


arredondamento especificado.

Truncate Arredonda o valor para zero. +4,5 torna-se


+4 e -4,5 torna-se -4.

IFloatingPointIeee754<TSelf> E Obtém um valor que representa o número


de Euler para o tipo.

Epsilon Obtém o menor valor representável que é


maior que zero para o tipo.

NaN Obtém um valor que representa NaN para o


tipo.

NegativeInfinity Obtém um valor que representa -Infinity


para o tipo.

NegativeZero Obtém um valor que representa -Zero


para o tipo.

Pi Obtém um valor que representa Pi para o


tipo.

PositiveInfinity Obtém um valor que representa +Infinity


para o tipo.

Tau Obtém um valor que representa Tau ( 2 *


Pi ) para o tipo.

(Outros) (Implementa o conjunto completo de


interfaces listadas em Interfaces de função.)

INumber<TSelf> Clamp Restringe um valor a nada mais e nada


menos que o valor mínimo e máximo
especificados.

CopySign Define o sinal de um valor especificado


como o mesmo que de outro valor
especificado.
Interface Nome da API Descrição

Max Retorna o maior de dois valores,


retornando NaN se uma das entradas for
NaN .

MaxNumber Retorna o maior de dois valores,


retornando o número se uma entrada for
NaN .

Min Retorna o menor de dois valores,


retornando NaN se uma das entradas for
NaN .

MinNumber Retorna o menor de dois valores,


retornando o número se uma entrada for
NaN .

Sign Retorna -1 para valores negativos, 0 para


zero e +1 para valores positivos.

INumberBase<TSelf> One Obtém o valor 1 para o tipo.

Radix Obtém a base para o tipo. Int32 retorna 2.


Decimal retorna 10.

Zero Obtém o valor 0 para o tipo.

CreateChecked Cria um valor, gerando um


OverflowException se a entrada não puder
se encaixar.1

CreateSaturating Cria um valor, fixando a T.MinValue ou


T.MaxValue se a entrada não puder se
encaixar.1

CreateTruncating Cria um valor com base em outro valor,


encapsulando se a entrada não puder se
encaixar. 1

IsComplexNumber Retornará true se o valor tiver uma parte


real diferente de zero e uma parte
imaginária diferente de zero.

IsEvenInteger Retornará true se um valor for um inteiro


par. 2.0 retorna true e 2.2 retorna false .

IsFinite Retornará true se o valor não for infinito e


não for NaN .
Interface Nome da API Descrição

IsImaginaryNumber Retornará true se o valor tiver uma parte


real zero. Isso significa que 0 é imaginário
e 1 + 1i não é.

IsInfinity Retornará true se o valor representar


infinito.

IsInteger Retornará true se um valor for um inteiro.


2.0 e 3.0 retornam true e 2.2 e 3.1
retornam false .

IsNaN Retornará true se o valor representar NaN .

IsNegative Retornará true se o valor for negativo. Isso


inclui -0.0.

IsPositive Retornará true se o valor for positivo. Isso


inclui 0 e +0.0.

IsRealNumber Retornará true se o valor tiver uma parte


imaginária zero. Isso significa que 0 é real,
assim como todos os tipos INumber<T> .

IsZero Retornará true se o valor representar zero.


Isso inclui 0, +0.0 e -0.0.

MaxMagnitude Retorna o valor com um valor absoluto


maior, retornando NaN se uma das
entradas for NaN .

MaxMagnitudeNumber Retorna o valor com um valor absoluto


maior, retornando o número se uma
entrada for NaN .

MinMagnitude Retorna o valor com um valor absoluto


menor, retornando NaN se uma das
entradas for NaN .

MinMagnitudeNumber Retorna o valor com um valor absoluto


menor, retornando o número se uma
entrada for NaN .

ISignedNumber<TSelf> NegativeOne Obtém o valor -1 para o tipo.

1
Para ajudar a entender o comportamento dos três métodos de Create* , considere os
exemplos a seguir.

Exemplo quando dado um valor muito grande:


byte.CreateChecked(384) gerará um OverflowException.
byte.CreateSaturating(384) retorna 255 porque 384 é maior que Byte.MaxValue

(que é 255).
byte.CreateTruncating(384) retorna 128 porque leva os 8 bits mais baixos (384

tem uma representação hexadecimal de 0x0180 , e os 8 bits mais baixos são 0x80 ,
que é 128).

Exemplo quando dado um valor muito pequeno:

byte.CreateChecked(-384) gerará um OverflowException.


byte.CreateSaturating(-384) retorna 0 porque -384 é menor que Byte.MinValue

(que é 0).
byte.CreateTruncating(-384) retorna 128 porque leva os 8 bits mais baixos (384

tem uma representação hexadecimal de 0xFE80 , e os 8 bits mais baixos são 0x80 ,
que é 128).

Os métodos Create* também têm algumas considerações especiais para tipos de ponto
flutuante IEEE 754, como float e double , pois eles têm os valores especiais
PositiveInfinity , NegativeInfinity e NaN . Todas as três APIs Create* se comportam

como CreateSaturating . Além disso, enquanto MinValue e MaxValue representam o


maior número "normal" negativo/positivo, os valores mínimo e máximo reais são
NegativeInfinity e PositiveInfinity , portanto, eles se prendem a esses valores.

Interfaces de operador
As interfaces do operador correspondem aos vários operadores disponíveis para a
linguagem C#.

Eles não emparelham explicitamente operações como multiplicação e divisão, pois


isso não é correto para todos os tipos. Por exemplo, Matrix4x4 * Matrix4x4 é
válido, mas Matrix4x4 / Matrix4x4 não é válido.
Normalmente, eles permitem que os tipos de entrada e de resultado sejam
diferentes para dar suporte a cenários como dividir dois inteiros para obter um
double , por exemplo, 3 / 2 = 1.5 , ou calcular a média de um conjunto de

inteiros.

Nome da interface Operadores definidos

IAdditionOperators<TSelf,TOther,TResult> x + y

IBitwiseOperators<TSelf,TOther,TResult> x & y , x | y , x ^ y e ~x
Nome da interface Operadores definidos

IComparisonOperators<TSelf,TOther,TResult> x < y , x > y , x <= y e x >= y

IDecrementOperators<TSelf> --x e x--

IDivisionOperators<TSelf,TOther,TResult> x / y

IEqualityOperators<TSelf,TOther,TResult> x == y e x != y

IIncrementOperators<TSelf> ++x e x++

IModulusOperators<TSelf,TOther,TResult> x % y

IMultiplyOperators<TSelf,TOther,TResult> x * y

IShiftOperators<TSelf,TOther,TResult> x << y e x >> y

ISubtractionOperators<TSelf,TOther,TResult> x - y

IUnaryNegationOperators<TSelf,TResult> -x

IUnaryPlusOperators<TSelf,TResult> +x

7 Observação

Algumas das interfaces definem um operador verificado, além de um operador não


verificado regular. Os operadores verificados são chamados em contextos
verificados e permitem que um tipo definido pelo usuário defina o comportamento
de estouro. Se você implementar um operador verificado, por exemplo,
CheckedSubtraction(TSelf, TOther), também deverá implementar o operador não
verificado, por exemplo, Subtraction(TSelf, TOther).

Interfaces de função
As interfaces de função definem APIs matemáticas comuns que se aplicam mais
amplamente do que a uma interface numérica específica. Essas interfaces são
implementadas por IFloatingPointIeee754<TSelf> e podem ser implementadas por
outros tipos relevantes no futuro.

Nome da interface Descrição

IExponentialFunctions<TSelf> Expõe funções exponenciais que dão suporte a e^x , e^x - 1 ,


2^x , 2^x - 1 , 10^x e 10^x - 1 .
Nome da interface Descrição

IHyperbolicFunctions<TSelf> Expõe funções hiperbólicas que dão suporte a acosh(x) ,


asinh(x) , atanh(x) , cosh(x) , sinh(x) e tanh(x) .

ILogarithmicFunctions<TSelf> Expõe funções logarítmicas com suporte a ln(x) , ln(x + 1) ,


log2(x) , log2(x + 1) , log10(x) e log10(x + 1) .

IPowerFunctions<TSelf> Expõe funções de potência que dão suporte a x^y .

IRootFunctions<TSelf> Expõe funções raiz que dão suporte a cbrt(x) e sqrt(x) .

ITrigonometricFunctions<TSelf> Expõe funções trigonométricas que dão suporte a acos(x) ,


asin(x) , atan(x) , cos(x) , sin(x) e tan(x) .

Interfaces de análise e formatação


Análise e formatação são conceitos fundamentais na programação. Eles são comumente
usados ao converter a entrada do usuário em um determinado tipo ou exibir um tipo
para o usuário. Essas interfaces estão no namespace System.

Nome da interface Descrição

IParsable<TSelf> Expõe o suporte para T.Parse(string, IFormatProvider) e


T.TryParse(string, IFormatProvider, out TSelf) .

ISpanParsable<TSelf> Expõe o suporte para T.Parse(ReadOnlySpan<char>,


IFormatProvider) e T.TryParse(ReadOnlySpan<char>,
IFormatProvider, out TSelf) .

IFormattableIFormattable Expõe o suporte para value.ToString(string,


IFormatProvider) .

ISpanFormattableISpanFormattable Expõe o suporte para value.TryFormat(Span<char>, out


int, ReadOnlySpan<char>, IFormatProvider) .

1
Essa interface não é nova nem genérica. No entanto, ela é implementada por todos os
tipos de número e representa a operação inversa de IParsable .

Por exemplo, o programa a seguir usa dois números como entrada, lendo-os do console
usando um método genérico em que o parâmetro de tipo é restrito a ser
IParsable<TSelf>. Ele calcula a média usando um método genérico em que os
parâmetros de tipo para os valores de entrada e resultado são restritos a ser
INumber<TSelf> e, em seguida, exibe o resultado para o console.

C#
using System.Globalization;
using System.Numerics;

static TResult Average<T, TResult>(T first, T second)


where T : INumber<T>
where TResult : INumber<TResult>
{
return TResult.CreateChecked( (first + second) / T.CreateChecked(2) );
}

static T ParseInvariant<T>(string s)
where T : IParsable<T>
{
return T.Parse(s, CultureInfo.InvariantCulture);
}

Console.Write("First number: ");


var left = ParseInvariant<float>(Console.ReadLine());

Console.Write("Second number: ");


var right = ParseInvariant<float>(Console.ReadLine());

Console.WriteLine($"Result: {Average<float, float>(left, right)}");

/* This code displays output similar to:

First number: 5.0


Second number: 6
Result: 5.5
*/

Confira também
Matemática genérica no .NET 7 (postagem no blog)

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Interfaces genéricas no .NET
Artigo • 10/05/2023

Este tópico fornece uma visão geral de interfaces genéricas do NET's que fornecem
funcionalidades comuns entre famílias de tipos genéricos.

As interfaces genéricas fornecem contrapartes fortemente tipadas para interfaces não


genéricas com a finalidade de realizar comparações de ordenação e de igualdade e para
a funcionalidade que é compartilhada por tipos de coleção genéricos. O .NET 7 introduz
interfaces genéricas para tipos semelhantes a números, por exemplo,
System.Numerics.INumber<TSelf>. Essas interfaces permitem definir métodos genéricos
que fornecem funcionalidade matemática, em que o parâmetro de tipo genérico é
restrito a ser um tipo que implementa uma interface numérica genérica.

7 Observação

Os parâmetros de tipo de várias interfaces genéricas são marcados como


covariantes ou contravariantes, o que fornece mais flexibilidade na atribuição e no
uso dos tipos que implementam essas interfaces. Para obter mais informações,
consulte Covariância e contravariância.

Comparações de ordem e igualdade


No namespace System, as interfaces genéricas System.IComparable<T> e
System.IEquatable<T>, assim como suas contrapartes não genéricas, definem
métodos para comparações de classificação e de igualdade, respectivamente. Os
tipos implementam essas interfaces para permitir a execução dessas comparações.

No namespace System.Collections.Generic, as interfaces genéricas IComparer<T>


e IEqualityComparer<T> oferecem uma maneira de definir uma comparação de
ordenação ou igualdade para tipos que não implementam a interface
System.IComparable<T> ou System.IEquatable<T>. Elas também fornecem uma
maneira de redefinir essas relações para tipos que o fazem.

Essas interfaces são usadas pelos métodos e construtores de muitas classes de


coleção genérica. Por exemplo, você pode passar um objeto IComparer<T>
genérico para o construtor da classe SortedDictionary<TKey,TValue>, a fim de
especificar uma ordem de classificação para um tipo que não implementa um
System.IComparable<T> genérico. Há sobrecargas do método estático genérico
Array.Sort e do método de instância List<T>.Sort para classificar matrizes e listas
usando implementações IComparer<T> genéricas.

As classes genéricas Comparer<T> e EqualityComparer<T> fornecem classes base


para implementações das interfaces genéricas IComparer<T> e
IEqualityComparer<T>, e também fornecem comparações padrão de ordem e de
igualdade por meio de suas respectivas propriedades Comparer<T>.Default e
EqualityComparer<T>.Default.

Funcionalidade de coleção
A interface genérica ICollection<T> é a interface básica para tipos de coleção
genérica. Ela fornece a funcionalidade básica para adicionar, remover, copiar e
enumerar elementos. ICollection<T> herda da IEnumerable<T> genérica e da
IEnumerable não genérica.

A interface genérica IList<T> estende a interface genérica ICollection<T> com


métodos para recuperação indexada.

A interface genérica IDictionary<TKey,TValue> estende a interface genérica


ICollection<T> com métodos para recuperação codificada. Os tipos genéricos de
dicionário na biblioteca de classes base do .NET também implementam a interface
não genérica IDictionary.

A interface genérica IEnumerable<T> fornece uma estrutura de enumerador


genérico. A interface genérica IEnumerator<T> implementada pelos enumeradores
genéricos herda a interface não genérica IEnumerator; os membros MoveNext e
Reset, que não dependem do parâmetro de tipo T , só aparecem na interface não
genérica. Isso significa que qualquer consumidor da interface não genérica
também poderá consumir a interface genérica.

Funcionalidade matemática
O .NET 7 apresenta interfaces genéricas no namespace System.Numerics que descrevem
tipos semelhantes a números e a funcionalidade disponível para eles. Os 20 tipos
numéricos que a biblioteca de classes base .NET fornece, por exemplo, Int32 e Double,
foram atualizados para implementar essas interfaces. A mais proeminente dessas
interfaces é a INumber<TSelf>, que corresponde aproximadamente a um número "real".

Para obter mais informações sobre essas interfaces, confira Matemática genérica.
Confira também
System.Collections.Generic
System.Collections.ObjectModel
Genéricos
Coleções genéricas no .NET
Delegados genéricos para manipulação de matrizes e listas
Covariância e contravariância
Covariância e contravariância em
genéricos
Artigo • 10/05/2023

Covariância e contravariância são termos que fazem referência à capacidade de usar um


tipo mais derivado (mais específico) ou menos derivado (menos específico) do que o
especificado originalmente. Os parâmetros de tipo genéricos oferecem suporte a
covariância e contravariância para fornecer maior flexibilidade na atribuição e no uso de
tipos genéricos.

Quando você se refere a um sistema de tipos, a covariância, contravariância e a


invariância têm as definições a seguir. Os exemplos assumem uma classe base chamada
Base e uma classe derivada chamada Derived .

Covariance

Permite usar um tipo mais derivado que o especificado originalmente.

Você pode atribuir uma instância de IEnumerable<Derived> uma variável do tipo


IEnumerable<Base> .

Contravariance

Permite a você usar um tipo mais genérico (menos derivado) do que aquele
especificado originalmente.

Você pode atribuir uma instância de Action<Base> uma variável do tipo


Action<Derived> .

Invariance

Significa que você só pode usar o tipo especificado originalmente. Um parâmetro


de tipo genérico invariante não é covariante nem contravariante.

Você não pode atribuir uma instância de List<Base> a uma variável do tipo
List<Derived> , ou vice-versa.

Os parâmetros de tipo covariantes permitem fazer atribuições muito semelhantes ao


Polimorfismo comum, conforme mostrado no código a seguir.

C#
IEnumerable<Derived> d = new List<Derived>();
IEnumerable<Base> b = d;

A classe List<T> implementa a interface IEnumerable<T>. Assim, List<Derived>


( List(Of Derived) no Visual Basic) implementa IEnumerable<Derived> . O parâmetro de
tipo covariante faz o resto.

A contravariância, por outro lado, parece não ser intuitiva. O exemplo a seguir cria um
delegado do tipo Action<Base> ( Action(Of Base) no Visual Basic) e, em seguida, atribuir
o delegado a uma variável do tipo Action<Derived> .

C#

Action<Base> b = (target) => { Console.WriteLine(target.GetType().Name); };


Action<Derived> d = b;
d(new Derived());

Isso parece ser um retrocesso, mas é esse código de tipo seguro que compila e executa.
A expressão lambda corresponde ao delegado ao qual ela é atribuída. Assim, ela define
um método que recebe um parâmetro do tipo Base e não tem nenhum valor retornado.
O delegado resultante pode ser atribuído a uma variável do tipo Action<Derived>
porque o parâmetro de tipo T do delegado Action<T> é contravariante. O código é de
tipo seguro porque T especifica um tipo de parâmetro. Quando o delegado do tipo
Action<Base> é invocado como se fosse um delegado do tipo Action<Derived> , seu

argumento deve ser do tipo Derived . Este argumento sempre pode ser passado ao
método subjacente com segurança porque o parâmetro do método é do tipo Base .

Geralmente, um parâmetro de tipo de covariante pode ser usado como o tipo de


retorno de um delegado, e os parâmetros de tipo contravariant podem ser usados
como tipos de parâmetro. Para uma interface, os parâmetros de tipo covariantes podem
ser usados como os tipos de retorno dos métodos da interface, e os parâmetros de tipo
contravariantes podem ser usados como os tipos de parâmetro dos métodos da
interface.

A covariância e a contravariância são referidas coletivamente como variância. Um


parâmetro de tipo genérico que não é covariante ou contravariante é referido como
invariante. Um breve resumo de fatos sobre variância em Common Language Runtime:

Os parâmetros de tipo variantes são restringidos à interface genérica e tipos de


delegados genéricos.
Uma interface genérica ou um tipo delegado genérico podem ter parâmetros de
tipo covariantes e contravariantes.

A variância aplica-se apenas para referenciar tipos; se você especificar um tipo de


valor para um parâmetro de tipo variante, esse parâmetro de tipo será invariante
para o tipo construído resultante.

A variância não se aplica à combinação de delegado. Ou seja, considerando dois


delegados de tipos Action<Derived> e Action<Base> ( Action(Of Derived) e
Action(Of Base) no Visual Basic), você não pode combinar o segundo delegado

com o primeiro, ainda que o resultado seja de tipo seguro. A variância permite que
o segundo delegado seja atribuído a uma variável do tipo Action<Derived> , mas
os delegados podem ser combinados somente quando seus tipos são exatamente
iguais.

Do C# 9 em diante, há suporte para tipos de retorno covariantes. Um método de


substituição pode declarar um tipo de retorno mais derivado do método que ele
substitui e uma propriedade somente leitura de substituição pode declarar um tipo
mais derivado.

Interfaces genéricas com parâmetros de tipo


covariantes
Várias interfaces genéricas têm parâmetros de tipo covariantes, por exemplo:
IEnumerable<T>, IEnumerator<T>, IQueryable<T> e IGrouping<TKey,TElement>. Todos
os parâmetros de tipo dessas interfaces são covariantes, de forma que os parâmetros de
tipo são usados apenas para os tipos de retorno dos membros.

O exemplo a seguir ilustra parâmetros de tipo covariantes. O exemplo define dois tipos:
Base tem um método estático chamado PrintBases que usa um IEnumerable<Base>

( IEnumerable(Of Base) no Visual Basic) e imprime os elementos. Derived herda de Base .


O exemplo cria um List<Derived> vazio ( List(Of Derived) no Visual Basic) e demonstra
que esse tipo pode ser passado a PrintBases e atribuído a uma variável do tipo
IEnumerable<Base> sem converter. List<T> implementa IEnumerable<T>, o qual tem um
único parâmetro de tipo covariante. O parâmetro de tipo covariante é a razão pela qual
uma instância de IEnumerable<Derived> pode ser usada em vez de IEnumerable<Base> .

C#

using System;
using System.Collections.Generic;
class Base
{
public static void PrintBases(IEnumerable<Base> bases)
{
foreach(Base b in bases)
{
Console.WriteLine(b);
}
}
}

class Derived : Base


{
public static void Main()
{
List<Derived> dlist = new List<Derived>();

Derived.PrintBases(dlist);
IEnumerable<Base> bIEnum = dlist;
}
}

Interfaces genéricas com parâmetros de tipo


contravariantes
Várias interfaces genéricas têm parâmetros de tipo contravariantes, por exemplo:
IComparer<T>, IComparable<T> e IEqualityComparer<T>. Essas interfaces possuem
apenas parâmetros de tipo contravariantes. Assim, os parâmetros de tipo são usados
apenas como parâmetros de tipo nos membros das interfaces.

O exemplo a seguir ilustra parâmetros de tipo contravariantes. O exemplo define uma


classe abstrata ( MustInherit no Visual Basic) Shape com uma propriedade Area . O
exemplo também define uma classe ShapeAreaComparer que implementa
IComparer<Shape> ( IComparer(Of Shape) no Visual Basic). A implementação do método
IComparer<T>.Compare baseia-se no valor da propriedade Area , portanto
ShapeAreaComparer pode ser usado para classificar objetos Shape por área.

A classe Circle herda Shape e substitui Area . O exemplo cria um SortedSet<T> de


objetos Circle usando um construtor que usa IComparer<Circle> ( IComparer(Of
Circle) no Visual Basic). Porém, em vez de passar um IComparer<Circle> , o exemplo
passa um objeto ShapeAreaComparer que implementa IComparer<Shape> . O exemplo
pode passar um comparador de um tipo derivado ( Shape ) quando um código chama
um comparador de um tipo mais derivado ( Circle ) porque o parâmetro de tipo da
interface genérica IComparer<T> é contravariante.
Quando um novo objeto Circle é adicionado a SortedSet<Circle> , o método
IComparer<Shape>.Compare (método IComparer(Of Shape).Compare no Visual Basic) do
objeto ShapeAreaComparer é chamado sempre que um novo elemento é comparado a
um elemento existente. O tipo de parâmetro do método ( Shape ) é menos derivado que
o tipo que está sendo passado ( Circle ). Assim, a chamada é de tipo seguro. A
contravariância permite que ShapeAreaComparer classifique uma coleção de um único
tipo, bem como uma coleção mista de tipos, que deriva de Shape .

C#

using System;
using System.Collections.Generic;

abstract class Shape


{
public virtual double Area { get { return 0; }}
}

class Circle : Shape


{
private double r;
public Circle(double radius) { r = radius; }
public double Radius { get { return r; }}
public override double Area { get { return Math.PI * r * r; }}
}

class ShapeAreaComparer : System.Collections.Generic.IComparer<Shape>


{
int IComparer<Shape>.Compare(Shape a, Shape b)
{
if (a == null) return b == null ? 0 : -1;
return b == null ? 1 : a.Area.CompareTo(b.Area);
}
}

class Program
{
static void Main()
{
// You can pass ShapeAreaComparer, which implements
IComparer<Shape>,
// even though the constructor for SortedSet<Circle> expects
// IComparer<Circle>, because type parameter T of IComparer<T> is
// contravariant.
SortedSet<Circle> circlesByArea =
new SortedSet<Circle>(new ShapeAreaComparer())
{ new Circle(7.2), new Circle(100), null, new Circle(.01) };

foreach (Circle c in circlesByArea)


{
Console.WriteLine(c == null ? "null" : "Circle with area " +
c.Area);
}
}
}

/* This code example produces the following output:

null
Circle with area 0.000314159265358979
Circle with area 162.860163162095
Circle with area 31415.9265358979
*/

Delegados genéricos com parâmetros de tipo


variantes
Os delegados genéricos Func , como Func<T,TResult>, têm tipos de retorno covariantes
e tipos de parâmetro contravariantes. Os delegados genéricos Action , como
Action<T1,T2>, possuem tipos de parâmetros contravariantes. Isso significa que os
delegados podem ser atribuídos a variáveis que possuem tipos de parâmetro mais
derivados e (no caso dos delegados genéricos Func ) menos tipos de retorno derivados.

7 Observação

O último parâmetro de tipo genérico dos delegados genéricos Func especifica o


tipo do valor de retorno na assinatura do delegado. É covariante (palavra-chave
out ), enquanto os outros parâmetros de tipo genéricos são contravariantes
(palavra-chave in ).

O código a seguir ilustra isso. O primeiro trecho de código define uma classe chamada
Base , uma classe chamada Derived que herda Base e outra classe com um método

static ( Shared no Visual Basic) chamado MyMethod . O método usa uma instância de

Base e retorna uma instância de Derived . (Se o argumento for uma instância de
Derived , MyMethod o retornará; se o argumento for uma instância de Base , MyMethod

retornará uma nova instância de Derived .) Em Main() , o exemplo cria uma instância de
Func<Base, Derived> ( Func(Of Base, Derived) no Visual Basic) que representa MyMethod

e a armazena na variável f1 .

C#

public class Base {}


public class Derived : Base {}
public class Program
{
public static Derived MyMethod(Base b)
{
return b as Derived ?? new Derived();
}

static void Main()


{
Func<Base, Derived> f1 = MyMethod;

O segundo trecho de código a seguir mostra que o delegado pode ser atribuído a uma
variável do tipo Func<Base, Base> ( Func(Of Base, Base) no Visual Basic) porque o tipo
de retorno é covariante.

C#

// Covariant return type.


Func<Base, Base> f2 = f1;
Base b2 = f2(new Base());

O terceiro trecho de código a seguir mostra que o delegado pode ser atribuído a uma
variável do tipo Func<Derived, Derived> ( Func(Of Derived, Derived) no Visual Basic)
porque o tipo de parâmetro é contravariante.

C#

// Contravariant parameter type.


Func<Derived, Derived> f3 = f1;
Derived d3 = f3(new Derived());

O trecho final de código mostra que o delegado pode ser atribuído a uma variável de
tipo Func<Derived, Base> ( Func(Of Derived, Base) no Visual Basic), combinando os
efeitos do tipo de parâmetro contravariante e do tipo de retorno covariante.

C#

// Covariant return type and contravariant parameter type.


Func<Derived, Base> f4 = f1;
Base b4 = f4(new Derived());

Variância em delegados não genéricos


No código anterior, a assinatura de MyMethod corresponde exatamente à assinatura do
delegado genérico construído: Func<Base, Derived> ( Func(Of Base, Derived) no Visual
Basic). O exemplo mostra que esse delegado genérico pode ser armazenado em
variáveis ou parâmetros do método que têm tipos de parâmetro mais derivados e tipos
de retorno menos derivados, desde que todos os tipos de delegados sejam construídos
do tipo de delegado genérico Func<T,TResult>.

Este é um aspecto importante. Os efeitos da covariância e da contravariância nos


parâmetros de tipo de delegados genéricos são semelhantes aos efeitos da covariância
e da contravariância na associação comum de delegação (confira Variância em
delegados (C#) e Variância em delegados (Visual Basic)). No entanto, a variância na
associação de delegados funciona com todos os tipos de delegados, e não apenas com
tipos de delegados genéricos com parâmetros de tipo variantes. Além disso, a variância
na associação de delegados possibilita a um método a ser associado a qualquer
delegado que tenha os tipos de parâmetro mais restritivos e um tipo de retorno menos
restritivo, enquanto que a atribuição de delegados genéricos só funciona quando
ambos os tipos de delegados são construídos da mesma definição de tipo genérico.

O exemplo a seguir mostra os efeitos combinados da variância na associação de


delegados e a variância em parâmetros de tipo genéricos. O exemplo define uma
hierarquia de tipo que inclui três tipos, do menos derivado ( Type1 ) para o mais derivado
( Type3 ). A variância na associação comum de delegados é usada para associar um
método a um tipo de parâmetro Type1 e um tipo de retorno Type3 a um representante
genérico com um tipo de parâmetro Type2 e um tipo de retorno Type2 . O delegado
genérico resultante é então atribuído a outra variável cujo tipo de delegado genérico
possui um parâmetro de tipo Type3 e tipo de retorno Type1 , usando a covariância e a
contravariância de parâmetros de tipo genéricos. A segunda atribuição requer que o
tipo de variável e o tipo de delegado sejam construídos a partir da mesma definição de
tipo genérico, nesse caso, Func<T,TResult>.

C#

using System;

public class Type1 {}


public class Type2 : Type1 {}
public class Type3 : Type2 {}

public class Program


{
public static Type3 MyMethod(Type1 t)
{
return t as Type3 ?? new Type3();
}
static void Main()
{
Func<Type2, Type2> f1 = MyMethod;

// Covariant return type and contravariant parameter type.


Func<Type3, Type1> f2 = f1;
Type1 t1 = f2(new Type3());
}
}

Definir delegados e interfaces genéricas


variantes
O Visual Basic e o C# têm palavras-chave que permitem marcar os parâmetros de tipo
genéricos de interfaces e delegados como covariantes ou contravariantes.

Um parâmetro de tipo de covariante é marcado com a palavra-chave out (palavra-


chave Out no Visual Basic). Você pode usar um parâmetro de tipo covariante como o
valor de retorno de um método que pertence a uma interface ou como o tipo de
retorno de um delegado. Você não pode usar um parâmetro de tipo covariante como
uma restrição de tipo genérico para métodos de interface.

7 Observação

Se um método de uma interface tem um parâmetro que é um tipo de delegado


genérico, um parâmetro de tipo covariante do tipo da interface pode ser usado
para especificar um parâmetro de tipo contravariante do tipo delegado.

Um parâmetro de tipo contracovariante é marcado com a palavra-chave in (palavra-


chave In no Visual Basic). Você pode usar um parâmetro de tipo contravariante como o
tipo de um parâmetro de um método que pertence a uma interface ou como o tipo de
um parâmetro de um delegado. Você pode usar um parâmetro de tipo contravariante
como uma restrição de tipo genérico para um método de interface.

Somente tipos de interfaces e tipos de delegados podem ter parâmetros de tipo


variantes. Um tipo de delegado ou interface pode ter parâmetros de tipo covariantes e
contravariantes.

O Visual Basic e o C# e não permitem que você viole as regras de uso de parâmetros de
tipo covariantes e contravariantes nem adicionar anotações de covariância e de
contravariância aos parâmetros de tipo de tipos que não sejam interfaces e delegados.
Para obter mais informações e códigos de exemplo, confira Variação em interfaces
genéricas (C#) e Variação em interfaces genéricas (Visual Basic).

Lista de tipos
Os tipos de interface e delegados a seguir têm parâmetros de tipo covariantes e/ou
contravariantes.

Tipo Parâmetros Parâmetros de


de tipo tipo
covariantes contravariantes

Action<T> em Sim
Action<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16>

Comparison<T> Sim

Converter<TInput,TOutput> Sim Sim

Func<TResult> Sim

Func<T,TResult> em Sim Yes


Func<T1,T2,T3,T4,T5,T6,T7,T8,T9,T10,T11,T12,T13,T14,T15,T16,TResult>

IComparable<T> Sim

Predicate<T> Yes

IComparer<T> Yes

IEnumerable<T> Yes

IEnumerator<T> Sim

IEqualityComparer<T> Sim

IGrouping<TKey,TElement> Sim

IOrderedEnumerable<TElement> Sim

IOrderedQueryable<T> Sim

IQueryable<T> Sim

Confira também
Covariância e contravariância (C#)
Covariância e contravariância (Visual Basic)
Variação em delegados (C#)
Variação em delegados (Visual Basic)
Coleções e estruturas de dados
Artigo • 27/01/2024

Dados semelhantes podem normalmente ser tratados com mais eficiência quando armazenados e
manipulados como uma coleção. Você pode usar a classe System.Array ou as classes dos
namespaces System.Collections, System.Collections.Generic, System.Collections.Concurrent e
System.Collections.Immutable para adicionar, remover e modificar elementos individuais ou um
intervalo de elementos em uma coleção.

Há dois tipos principais de coleções; coleções genéricas e coleções não genéricas. Coleções
genéricas são fortemente tipadas em tempo de compilação. Por isso, coleções genéricas
normalmente oferecem melhor desempenho. Coleções genéricas aceitam um parâmetro de tipo
quando são criadas e não exigem que você converta de e para o tipo Object ao adicionar ou
remover itens da coleção. Além disso, há suporte para a maioria das coleções genéricas nos
aplicativos da Windows Store. As coleções não genéricas armazenam itens como Object, exigem a
conversão e a maioria não tem suporte para o desenvolvimento de aplicativos da Windows Store.
No entanto, você pode ver as coleções não genéricas no código mais antigo.

A partir do .NET Framework 4, as coleções do namespace System.Collections.Concurrent fornecem


operações thread-safe eficientes para acessar itens da coleção por meio de vários threads. As
classes de coleção imutáveis do namespace System.Collections.Immutable (pacote NuGet ) são
inerentemente thread-safe, pois as operações são executadas em uma cópia da coleção original e
a coleção original não pode ser modificada.

Recursos comuns de coleção


Todas as coleções fornecem métodos para adicionar, remover ou localizar itens na coleção. Além
disso, todas as coleções que direta ou indiretamente implementam a interface ICollection ou a
interface ICollection<T> compartilham estes recursos:

A capacidade de enumerar a coleção

As coleções do .NET implementam System.Collections.IEnumerable ou


System.Collections.Generic.IEnumerable<T> para permitir a iteração na coleção. Um
enumerador pode ser considerado um ponteiro móvel para qualquer elemento da coleção.
A instrução foreach, in e a Instrução For Each...Next usam o enumerador exposto pelo
método GetEnumerator e ocultam a complexidade de manipulação do enumerador. Além
disso, qualquer coleção que implementa System.Collections.Generic.IEnumerable<T> é
considerada um tipo passível de consulta e pode ser consultada com LINQ. Consultas LINQ
fornecem um padrão comum para o acesso de dados. Elas são geralmente mais concisas e
legíveis que loops foreach padrão e fornecem filtragem, classificação e agrupamento de
recursos. Consultas LINQ também podem melhorar o desempenho. Para obter mais
informações, consulte LINQ to Objects (C#), LINQ to Objects (Visual Basic), Parallel LINQ
(PLINQ), Introdução a consultas LINQ (C#) e Operações básicas de consulta (Visual Basic).
A capacidade de copiar o conteúdo da coleção para uma matriz

Todas as coleções podem ser copiadas para uma matriz usando o método CopyTo; no
entanto, a ordem dos elementos na nova matriz se baseia na sequência na qual o
enumerador os retorna. A matriz resultante é sempre unidimensional com um limite inferior
de zero.

Além disso, muitas classes de coleção contêm os seguintes recursos:

Propriedades de capacidade e contagem

A capacidade de uma coleção é o número de elementos que ela pode conter. A contagem
de uma coleção é o número de elementos que ela realmente contém. Algumas coleções
ocultam a capacidade ou a contagem ou ambas.

A maioria das coleções se expande automaticamente em capacidade quando a capacidade


atual é atingida. A memória é realocada e os elementos são copiados da coleção antiga para
a nova. Isso reduz o código necessário para usar a coleção; no entanto, o desempenho da
coleção pode ser afetado de forma negativa. Por exemplo, para List<T>, se Count for inferior
a Capacity, a adição de um item será uma operação O(1). Se a capacidade precisar ser
aumentada para acomodar o novo elemento, a adição de um item passará a ser uma
operação O( n ), em que n é Count. A melhor maneira de evitar um desempenho ruim
causado por várias realocações é definir a capacidade inicial para que seja o tamanho
estimado da coleção.

Uma BitArray é um caso especial; sua capacidade é a mesma que seu comprimento, que é o
mesmo de sua contagem.

Um limite inferior consistente

O limite inferior de uma coleção é o índice do seu primeiro elemento. Todas as coleções
indexadas nos namespaces System.Collections têm um limite inferior de zero, indicando que
são indexados em 0. Array tem um limite inferior de zero por padrão, mas um limite inferior
diferente pode ser definido ao criar uma instância da classe Matriz usando
Array.CreateInstance.

Sincronização para acesso de vários threads (System.Collections somente classes).

Os tipos de coleções não genéricas no namespace System.Collections oferecem algum


acesso thread-safe com sincronização, geralmente exposto por meio dos membros SyncRoot
e IsSynchronized. Essas coleções são não thread-safe por padrão. Se você precisar de acesso
com multithread escalável e eficiente para uma coleção, use uma das classes no namespace
System.Collections.Concurrent ou considere usar uma coleção imutável. Para obter mais
informações, veja Coleções thread-safe.

Escolher uma coleção


Em geral, você deve usar coleções genéricas. A tabela a seguir descreve alguns cenários comuns
de coleção e as classes de coleção que você pode usar para esses cenários. Se você for
inexperiente com coleções genéricas, esta tabela o ajudará a escolher a coleção genérica
adequada para a tarefa.

ノ Expandir a tabela

Eu quero… Opções de coleção Opções de Opções de coleção thread-safe ou


genérica coleção não imutável
genérica

Armazenar itens como Dictionary<TKey,TValue> Hashtable ConcurrentDictionary<TKey,TValue>


pares chave/valor para
consulta rápida por chave (Um conjunto ReadOnlyDictionary<TKey,TValue>
de pares
chave/valor ImmutableDictionary<TKey,TValue>
que são
organizados
com base no
código hash
da chave.)

Itens de acesso por índice List<T> Array ImmutableList<T>

ArrayList ImmutableArray

Usar itens primeiro a Queue<T> Queue ConcurrentQueue<T>


entrar, primeiro a sair
(PEPS) ImmutableQueue<T>

Usar dados último a Stack<T> Stack ConcurrentStack<T>


entrar, primeiro a sair
(UEPS) ImmutableStack<T>

Acessar itens em LinkedList<T> Nenhuma Nenhuma recomendação


sequência recomendação

Receba notificações ObservableCollection<T> Nenhuma Nenhuma recomendação


quando itens forem recomendação
removidos da coleção ou
adicionados a ela.
(implementa
INotifyPropertyChanged e
INotifyCollectionChanged)

Uma coleção classificada SortedList<TKey,TValue> SortedList ImmutableSortedDictionary<TKey,TValue>

ImmutableSortedSet<T>

Um conjunto de funções HashSet<T> Nenhuma ImmutableHashSet<T>


matemáticas recomendação
SortedSet<T> ImmutableSortedSet<T>
Complexidade algorítmica de coleções
Ao escolher uma classe de coleção, vale a pena considerar as possíveis compensações no
desempenho. Use a tabela a seguir para ter uma referência de comparação dos vários tipos de
coleções mutáveis em complexidade algorítmica com os equivalentes imutáveis correspondentes.
Geralmente, os tipos de coleções imutáveis têm um desempenho inferior, mas fornecem
imutabilidade – o que costuma ser um benefício comparativo válido.

ノ Expandir a tabela

Mutável Amortizado Pior caso Imutável Complexidade

Stack<T>.Push O(1) O( n ) ImmutableStack<T>.Push O(1)

Queue<T>.Enqueue O(1) O( n ) ImmutableQueue<T>.Enqueue O(1)

List<T>.Add O(1) O( n ) ImmutableList<T>.Add O(log n )

List<T>.Item[Int32] O(1) O(1) ImmutableList<T>.Item[Int32] O(log n )

List<T>.Enumerator O( n ) O( n ) ImmutableList<T>.Enumerator O( n )

Pesquisa de O(1) O( n ) ImmutableHashSet<T>.Add O(log n )


HashSet<T>.Add

SortedSet<T>.Add O(log n ) O( n ) ImmutableSortedSet<T>.Add O(log n )

Dictionary<T>.Add O(1) O( n ) ImmutableDictionary<T>.Add O(log n )

Pesquisa de O(1) O(1) – ou Pesquisa de O(log n )


Dictionary<T> estritamente ImmutableDictionary<T>
O( n )

SortedDictionary<T>.Add O(log n ) O( n log n ) ImmutableSortedDictionary<T>.Add O(log n )

Uma List<T> pode ser enumerada com eficiência por meio de um loop for ou um loop foreach .
No entanto, uma ImmutableList<T> não é eficiente em um loop for , devido ao tempo de O(log
n ) do indexador. A enumeração de uma ImmutableList<T> por meio de loop foreach usando é

eficiente porque ImmutableList<T> usa uma árvore binária para armazenar os dados em vez de
uma matriz simples como usado por List<T> . Uma matriz pode ser indexada muito rapidamente,
enquanto uma árvore binária precisa ser percorrida até que o nó com o índice desejado seja
encontrado.

Além disso, SortedSet<T> tem a mesma complexidade que ImmutableSortedSet<T> . Isso porque
ambos usam árvores binárias. Obviamente, a diferença significativa é que ImmutableSortedSet<T>
usa uma árvore binária imutável. Como ImmutableSortedSet<T> também oferece uma classe
System.Collections.Immutable.ImmutableSortedSet<T>.Builder que permite a mutação, é possível
ter imutabilidade e desempenho.
Tópicos Relacionados
ノ Expandir a tabela

Título Descrição

Selecionando uma classe de Descreve as diferentes coleções e ajuda a selecionar uma para o seu
coleção cenário.

Tipos de Coleção de Uso Descreve os tipos de coleção genérica e não genérica normalmente
Comum usadas, tais como System.Array, System.Collections.Generic.List<T>, e
System.Collections.Generic.Dictionary<TKey,TValue>.

Quando Usar Coleções Descreve o uso de tipos de coleção genérica.


Genéricas

Comparações e Classificações Discute o uso de comparações de igualdade e comparações de


Dentro de Coleções classificação em coleções.

Tipos de Coleção Sorted Descreve as características e o desempenho de coleções classificadas

Tipos de Coleção de Tabela de Descreve os recursos de tipos de dicionário baseado em hash genérico e
Hash e Dicionário não genérico.

Coleções Thread-Safe Descreve os tipos de coleção, tais como


System.Collections.Concurrent.BlockingCollection<T> e
System.Collections.Concurrent.ConcurrentBag<T> que dão suporte a
acesso simultâneo seguro e eficiente de vários threads.

System.Collections.Immutable Apresenta as coleções imutáveis e fornece links para os tipos de coleção.

Referência
System.Array System.Collections System.Collections.Concurrent System.Collections.Generic
System.Collections.Specialized System.Linq System.Collections.Immutable

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Selecionando uma classe de coleção
Artigo • 10/05/2023

Certifique-se de escolher sua classe de coleção com cuidado. A utilização do tipo errado
pode restringir o uso da coleção.

) Importante

Evite usar os tipos no namespace System.Collections. As versões genéricas e


simultâneas das coleções são as recomendadas devido à maior segurança de tipos
e outras melhorias.

Considere as seguintes perguntas:

Você precisa de uma lista sequencial em que o elemento normalmente será


descartado após seu valor ser recuperado?

Em caso afirmativo, considere usar a classe Queue ou a classe genérica


Queue<T> caso precise do comportamento PEPS (primeiro a entrar, primeiro a
sair). Considere usar a classe Stack ou a classe genérica Stack<T> caso precise
do comportamento UEPS (último a entrar, primeiro a sair). Para obter acesso
seguro de vários threads, use as versões simultâneas ConcurrentQueue<T> e
ConcurrentStack<T>. Para imutabilidade, considere as versões imutáveis
ImmutableQueue<T> e ImmutableStack<T>.

Caso contrário, considere usar outras coleções.

Você precisa acessar os elementos em uma ordem específica, como PEPS, UEPS ou
aleatória?

A classe Queue e as classes genéricas Queue<T>, ConcurrentQueue<T> e


ImmutableQueue<T> oferecem acesso FIFO. Para obter mais informações,
consulte Quando usar uma coleção thread-safe.

A classe Stack e as classes genéricas Stack<T>, ConcurrentStack<T> e


ImmutableStack<T> oferecem acesso LIFO. Para obter mais informações,
consulte Quando usar uma coleção thread-safe.

A classe genérica LinkedList<T> permite o acesso sequencial de ponta a ponta.

Você precisa acessar cada elemento pelo índice?


As classes ArrayList e StringCollection e a classe genérica List<T> oferecem
acesso aos seus elementos pelo índice baseado em zero do elemento. Para
imutabilidade, considere as versões genéricas imutáveis ImmutableArray<T> e
ImmutableList<T>.

As classes Hashtable, SortedList, ListDictionary e StringDictionary, e as classes


genéricas Dictionary<TKey,TValue> e SortedDictionary<TKey,TValue>, oferecem
acesso aos seus elementos pela chave do elemento. Além disso, há versões
imutáveis de vários tipos correspondentes: ImmutableHashSet<T>,
ImmutableDictionary<TKey,TValue>, ImmutableSortedSet<T> e
ImmutableSortedDictionary<TKey,TValue>.

As classes NameObjectCollectionBase e NameValueCollection, e as classes


genéricas KeyedCollection<TKey,TItem> e SortedList<TKey,TValue>, oferecem
acesso aos seus elementos pelo índice baseado em zero ou pela chave do
elemento.

Cada elemento conterá um valor, uma combinação de uma chave e um valor ou


uma combinação de uma chave e diversos valores?

Um valor: use qualquer uma das coleções baseadas na interface do IList ou na


interface genérica do IList<T>. Para uma opção imutável, considere a interface
genérica IImmutableList<T>.

Uma chave e um valor: use qualquer uma das coleções baseadas na interface do
IDictionary ou na interface genérica do IDictionary<TKey,TValue>. Para uma
opção imutável, considere a interface genérica IImmutableSet<T> ou
IImmutableDictionary<TKey,TValue>.

Um valor com chave incorporada: use a classe genérica


KeyedCollection<TKey,TItem>.

Uma chave e vários valores: use a classe NameValueCollection.

Você precisa classificar os elementos de forma diferente de como foram inseridos?

O Hashtable classifica os elementos dele pelos próprios códigos hash.

A classe SortedList e as classes genéricas SortedList<TKey,TValue> e


SortedDictionary<TKey,TValue> classificam seus elementos pela chave. A ordem
de classificação se baseia na implementação da interface IComparer da classe
SortedList e na implementação da interface genérica IComparer<T> das classes
genéricas SortedList<TKey,TValue> e SortedDictionary<TKey,TValue>. Dos dois
tipos genéricos, SortedDictionary<TKey,TValue> oferece um melhor
desempenho do que SortedList<TKey,TValue>, enquanto
SortedList<TKey,TValue> consome menos memória.

O ArrayList oferece um método Sort que usa uma implementação IComparer


como parâmetro. Seu equivalente genérico, a classe genérica List<T>, fornece
um método Sort que usa uma implementação da interface genérica
IComparer<T> como parâmetro.

Você precisa de rapidez para pesquisas e recuperação de informações?


ListDictionary é mais rápido do que o Hashtable para pequenas coleções (10
itens ou menos). A classe genérica Dictionary<TKey,TValue> fornece pesquisa
mais rápida do que a classe genérica SortedDictionary<TKey,TValue>. A
implementação com multithread é ConcurrentDictionary<TKey,TValue>. O
ConcurrentBag<T> fornece uma inserção com multithread rápida para dados
não ordenados. Para obter mais informações sobre os dois tipos de multi-
threaded, consulte Quando usar uma coleção thread-safe.

Você precisa de coleções que aceitem apenas cadeias de caracteres?

StringCollection (com base no IList) e StringDictionary (com base no IDictionary)


estão no namespace System.Collections.Specialized.

Além disso, é possível usar qualquer uma das classes genéricas de coleção no
namespace System.Collections.Generic como coleções de cadeias de caracteres
fortemente tipadas especificando a classe String para seus argumentos de tipo
genérico. Por exemplo, é possível declarar que uma variável é do tipo
List<String> ou Dictionary<String,String>.

LINQ to Objects e PLINQ


O LINQ to Objects permite que os desenvolvedores usem consultas LINQ para acessar
objetos na memória, desde que o tipo de objeto implemente IEnumerable ou
IEnumerable<T>. As consultas LINQ fornecem um padrão comum para acessar dados,
são geralmente mais concisas e legíveis que os loops padrão foreach e fornecem
capacidades de filtragem, ordenação e agrupamento. Para obter mais informações,
confira LINQ to Objects (C#) e LINQ to Objects (Visual Basic).

PLINQ fornece uma implementação paralela de LINQ to Objects que pode oferecer uma
execução de consulta mais rápida em muitos cenários, por meio do uso mais eficiente
dos computadores de vários núcleos. Para obter mais informações, consulte PLINQ
(Parallel LINQ).
Confira também
System.Collections
System.Collections.Specialized
System.Collections.Generic
Coleções thread-safe
Tipos de coleção usados com frequência
Artigo • 09/05/2023

Os tipos de coleção representam diferentes maneiras de coletar dados, como tabelas de


hash, filas, pilhas, recipientes, dicionários e listas.

Todas as coleções são baseadas de forma direta ou indireta nas interfaces ICollection ou
ICollection<T>. IList e IDictionary seus equivalentes genéricos derivam dessas duas
interfaces.

Em coleções baseadas em IList ou diretamente em ICollection, cada elemento contém


apenas um valor. Esses tipos incluem:

Array
ArrayList
List<T>
Queue
ConcurrentQueue<T>
Stack
ConcurrentStack<T>
LinkedList<T>

Em coleções baseadas na interface IDictionary, cada elemento contém uma chave e um


valor. Esses tipos incluem:

Hashtable
SortedList
SortedList<TKey,TValue>
Dictionary<TKey,TValue>
ConcurrentDictionary<TKey,TValue>

A classe KeyedCollection<TKey,TItem> é exclusiva, pois ela é uma lista de valores com


chaves incorporadas aos valores. Como resultado, ela se comporta como uma lista e
como um dicionário.

Quando você precisar de acesso à coleção com multithread eficiente, use as coleções
genéricas no namespace System.Collections.Concurrent.

As classes Queue e Queue<T> fornecem listas do tipo primeiro a entrar, primeiro a sair.
As classes Stack e Stack<T> fornecem listas do tipo último a entrar, primeiro a sair.

Tipagem forte
Coleções genéricas são a melhor solução para tipagem forte. Por exemplo, adicionar um
elemento de qualquer tipo diferente de um Int32 a uma coleção List<Int32> causa um
erro em tempo de compilação. No entanto, se o idioma não oferecer suporte a
genéricos, o namespace System.Collections incluirá classes base abstratas que você
pode estender para criar classes de coleção que são fortemente tipadas. Essas classes
base incluem:

CollectionBase
ReadOnlyCollectionBase
DictionaryBase

Como coleções variam


As coleções variam em como armazenam, classificam e comparam elementos e como
executam pesquisas.

A classe SortedList e a classe genérica SortedList<TKey,TValue> fornecem versões


classificadas da classe Hashtable e da classe genérica Dictionary<TKey,TValue>.

Todas as coleções usam índices baseados em zero, exceto Array, a qual permite matrizes
que não sejam baseadas em zero.

Você pode acessar os elementos de um SortedList ou um KeyedCollection<TKey,TItem>


pela chave ou pelo índice do elemento. Somente é possível os elementos de um
Hashtable ou um Dictionary<TKey,TValue> pela chave do elemento.

Usar LINQ com tipos de coleção


O recurso LINQ to Objects fornece um padrão comum para acessar objetos na memória
de qualquer tipo que implemente IEnumerable ou IEnumerable<T>. As consultas LINQ
têm vários benefícios em relação a constructos padrão, como loops foreach :

Elas são concisas e fáceis de entender.


Elas podem filtrar, ordenar e agrupar dados.
Elas podem melhorar o desempenho.

Para obter mais informações, confira LINQ to Objects (C#), LINQ to Objects (Visual Basic)
e PLINQ (Parallel LINQ).

Tópicos relacionados
Título Descrição

Coleções e Discute os diversos tipos de coleção disponíveis no .NET, incluindo pilhas, filas,
Estruturas de listas, matrizes e dicionários.
Dados

Tipos de Descreve os recursos de tipos de dicionário baseados em hash genérico e não


Coleção de genérico.
Tabela de
Hash e
Dicionário

Tipos de Descreve as classes que fornecem funcionalidade de classificação para listas e


Coleção conjuntos.
Sorted

Genéricos Descreve o recurso Genéricos, incluindo coleções, delegados e interfaces


genéricos fornecidos pelo .NET. Fornece links à documentação de recursos para
C#, Visual Basic e Visual C++ e para oferecer suporte a tecnologias, tais como a
de reflexão.

Referência
System.Collections

System.Collections.Generic

System.Collections.ICollection

System.Collections.Generic.ICollection<T>

System.Collections.IList

System.Collections.Generic.IList<T>

System.Collections.IDictionary

System.Collections.Generic.IDictionary<TKey,TValue>
Quando usar coleções genéricas
Artigo • 10/05/2023

Usar coleções genéricas oferece o benefício automático da segurança de tipos sem


precisar derivar de um tipo de coleção base e implementar membros específicos do
tipo. Geralmente, tipos de coleção genérica também executam melhor do que os tipos
de coleção não genérica correspondentes (e melhor do que tipos que são derivados de
tipos de coleção base não genérica) quando os elementos da coleção forem tipos de
valor, pois com genéricos não é necessário colocar os elementos em caixa.

Para programas destinados ao .NET Standard 1.0 ou posteriores, use as classes de


coleção genérica no namespace System.Collections.Concurrent quando vários threads
podem estar adicionando ou removendo itens da coleção simultaneamente. Além disso,
quando a imutabilidade for desejada, considere as classes de coleção genéricas no
namespace System.Collections.Immutable.

Os seguintes tipos genéricos correspondem aos tipos de coleção existentes:

List<T> é a classe genérica que corresponde à ArrayList.

Dictionary<TKey,TValue> e ConcurrentDictionary<TKey,TValue> são as classes


genéricas que correspondem à Hashtable.

Collection<T> é a classe genérica que corresponde à CollectionBase.


Collection<T> pode ser usada como uma classe base, mas, ao contrário de
CollectionBase, não é abstrata, o que a torna muito mais fácil de usar.

ReadOnlyCollection<T> é a classe genérica que corresponde à


ReadOnlyCollectionBase. ReadOnlyCollection<T> não é abstrata e tem um
construtor que facilita expor uma List<T> existente como uma coleção somente
leitura.

As classes genéricas Queue<T>, ConcurrentQueue<T>, ImmutableQueue<T>,


ImmutableArray<T>, SortedList<TKey,TValue> e ImmutableSortedSet<T>
correspondem às classes não genéricas respectivas com os mesmos nomes.

Tipos adicionais
Vários tipos de coleção genérica não têm equivalentes não genéricas. Eles incluem o
seguinte:
LinkedList<T> é uma lista vinculada de uso geral que fornece operações de
inserção e remoção O(1).

SortedDictionary<TKey,TValue> é um dicionário classificado com operações de


inserção e recuperação O(log n ), o que o torna uma alternativa útil para
SortedList<TKey,TValue>.

KeyedCollection<TKey,TItem> é um híbrido entre uma lista e um dicionário, que


fornece uma maneira de armazenar objetos que contenham suas próprias chaves.

BlockingCollection<T> implementa uma classe de coleção com funcionalidade de


delimitação e bloqueio.

ConcurrentBag<T> fornece rápida inserção e remoção de elementos não


classificados.

Construtores imutáveis
Quando você deseja funcionalidade de imutabilidade em seu aplicativo, o namespace
System.Collections.Immutable oferece tipos de coleções genéricas que você pode usar.
Todos os tipos de coleção imutáveis oferecem classes Builder que podem otimizar o
desempenho quando você estiver executando várias mutações. A classe Builder agrupa
operações em um estado mutável. Quando todas as mutações tiverem sido concluídas,
chame o método ToImmutable para "congelar" todos os nós e criar uma coleção
genérica imutável, por exemplo, um ImmutableList<T>.

O objeto Builder pode ser criado chamando o método CreateBuilder() não genérico.
Em uma instância Builder , você pode chamar ToImmutable() . Da mesma forma, na
coleção Immutable* , você pode chamar ToBuilder() para criar uma instância de
construtor a partir da coleção imutável genérica. Veja a seguir os vários tipos Builder .

ImmutableArray<T>.Builder
ImmutableDictionary<TKey,TValue>.Builder
ImmutableHashSet<T>.Builder
ImmutableList<T>.Builder
ImmutableSortedDictionary<TKey,TValue>.Builder
ImmutableSortedSet<T>.Builder

Objetos LINQ to
O recurso LINQ para objetos permite que você use consultas LINQ para acessar objetos
na memória, desde que o tipo de objeto implemente a interface
System.Collections.IEnumerable ou System.Collections.Generic.IEnumerable<T>. As
consultas LINQ fornecem um padrão comum para acessar dados, são geralmente mais
concisas e legíveis que os loops padrão foreach e fornecem capacidades de filtragem,
ordenação e agrupamento. Consultas LINQ também podem melhorar o desempenho.
Para obter mais informações, confira LINQ to Objects (C#), LINQ to Objects (Visual Basic)
e PLINQ (Parallel LINQ).

Funcionalidade adicional
Alguns dos tipos genéricos possuem funcionalidades que não se encontram em tipos de
coleção genérica. Por exemplo, a classe List<T>, que corresponde à classe ArrayList não
genérica, possui vários métodos que aceitam delegados genéricos, tais como o
delegado Predicate<T> que permite que você especifique métodos para pesquisa na
lista, o delegado Action<T> que representa métodos que atuam em cada elemento da
lista e o delegado Converter<TInput,TOutput> que permite definir conversões entre
tipos.

A classe List<T> permite que você especifique suas próprias implementações de


interface genérica IComparer<T> para classificação e pesquisa na lista. As classes
SortedDictionary<TKey,TValue> e SortedList<TKey,TValue> também possuem esse
recurso. Além disso, essas classes permitem que você especifique comparadores
quando a coleção for criada. De maneira semelhante, as classes
Dictionary<TKey,TValue> e KeyedCollection<TKey,TItem> permitem que você
especifique seus próprios comparadores de igualdade.

Confira também
Coleções e Estruturas de Dados
Tipos de Coleção de Uso Comum
Genéricos
Comparações e classificações dentro de
coleções
Artigo • 10/05/2023

As classes System.Collections executam comparações em quase todos os processos


envolvidos no gerenciamento de coleções, seja procura pelo elemento para remoção ou
retorno do valor de um par de chaves e valores.

As coleções normalmente usam um comparador de igualdade e/ou um comparador de


ordenação. Dois constructos são usados para comparações.

Verificar a igualdade
Métodos como Contains , IndexOf, LastIndexOf e Remove usam um comparador de
igualdade para os elementos da coleção. Se a coleção for genérica, os itens serão
comparados com relação à igualdade, de acordo com as seguintes diretrizes:

Se o tipo T implementa a interface genérica IEquatable<T>, então o comparador


de igualdade será o método Equals dessa interface.

Se o tipo T não implementar IEquatable<T>, Object.Equals será usado.

Além disso, algumas sobrecargas de construtores para coleções de dicionários aceitam


uma implementação IEqualityComparer<T>, que é usada para comparar chaves com
relação à igualdade. Para ver um exemplo, consulte o construtor
Dictionary<TKey,TValue>.

Determinar a ordem de classificação


Métodos como BinarySearch e Sort usam um comparador de ordenação para os
elementos da coleção. As comparações podem ser entre elementos da coleção ou entre
um elemento e um valor especificado. Para comparar objetos, há o conceito de um
default comparer e um explicit comparer .

O comparador padrão baseia-se em pelo menos um dos objetos que estão sendo
comparados para implementar a interface IComparable. É uma boa prática implementar
IComparable em todas as classes usadas como valores em uma coleção de lista ou
como chaves em uma coleção de dicionário. Para uma coleção genérica, a comparação
de igualdade é determinada de acordo com o seguinte:
Se o tipo T implementa a interface genérica System.IComparable<T>, então o
comparador padrão será o método IComparable<T>.CompareTo(T) dessa
interface.

Se o tipo T implementa a interface não genérica System.IComparable, então o


comparador padrão será o método IComparable.CompareTo(Object) dessa
interface.

Se o tipo T não implementar nenhuma das interfaces, não haverá um comparador


padrão, e um comparador ou um delegado de comparação precisará ser fornecido
explicitamente.

Para fornecer comparações explícitas, alguns métodos aceitam uma implementação de


IComparer como um parâmetro. Por exemplo, o método List<T>.Sort aceita uma
implementação System.Collections.Generic.IComparer<T>.

A configuração de cultura atual do sistema pode afetar as comparações e as


classificações dentro de uma coleção. Por padrão, as comparações e classificações nas
classes Collections levam em conta a cultura. Para ignorar a configuração de cultura e
assim obter comparação consistente e classificar os resultados, use o InvariantCulture
com sobrecargas de membros que aceitam uma CultureInfo. Para obter mais
informações, confira Executar operações de cadeia de caracteres que não diferenciam a
cultura em coleções e Executar operações de cadeia de caracteres que não diferenciam
a cultura em matrizes.

Exemplo de igualdade e classificação


O código a seguir demonstra uma implementação de IEquatable<T> e IComparable<T>
em um objeto de negócios simples. Além disso, quando o objeto é armazenado em
uma lista e classificado, você verá que chamar o método Sort() resulta no uso do
comparador padrão para o tipo Part e o método Sort(Comparison<T>) implementado
usando um método anônimo.

C#

using System;
using System.Collections.Generic;

// Simple business object. A PartId is used to identify the


// type of part but the part name can change.
public class Part : IEquatable<Part>, IComparable<Part>
{
public string PartName { get; set; }

public int PartId { get; set; }


public override string ToString() =>
$"ID: {PartId} Name: {PartName}";

public override bool Equals(object obj) =>


(obj is Part part)
? Equals(part)
: false;

public int SortByNameAscending(string name1, string name2) =>


name1?.CompareTo(name2) ?? 1;

// Default comparer for Part type.


// A null value means that this object is greater.
public int CompareTo(Part comparePart) =>
comparePart == null ? 1 : PartId.CompareTo(comparePart.PartId);

public override int GetHashCode() => PartId;

public bool Equals(Part other) =>


other is null ? false : PartId.Equals(other.PartId);

// Should also override == and != operators.


}

public class Example


{
public static void Main()
{
// Create a list of parts.
var parts = new List<Part>
{
// Add parts to the list.
new Part { PartName = "regular seat", PartId = 1434 },
new Part { PartName = "crank arm", PartId = 1234 },
new Part { PartName = "shift lever", PartId = 1634 },
// Name intentionally left null.
new Part { PartId = 1334 },
new Part { PartName = "banana seat", PartId = 1444 },
new Part { PartName = "cassette", PartId = 1534 }
};

// Write out the parts in the list. This will call the overridden
// ToString method in the Part class.
Console.WriteLine("\nBefore sort:");
parts.ForEach(Console.WriteLine);

// Call Sort on the list. This will use the


// default comparer, which is the Compare method
// implemented on Part.
parts.Sort();

Console.WriteLine("\nAfter sort by part number:");


parts.ForEach(Console.WriteLine);
// This shows calling the Sort(Comparison<T> comparison) overload
using
// a lambda expression as the Comparison<T> delegate.
// This method treats null as the lesser of two values.
parts.Sort((Part x, Part y) =>
x.PartName == null && y.PartName == null
? 0
: x.PartName == null
? -1
: y.PartName == null
? 1
: x.PartName.CompareTo(y.PartName));

Console.WriteLine("\nAfter sort by name:");


parts.ForEach(Console.WriteLine);

/*

Before sort:
ID: 1434 Name: regular seat
ID: 1234 Name: crank arm
ID: 1634 Name: shift lever
ID: 1334 Name:
ID: 1444 Name: banana seat
ID: 1534 Name: cassette

After sort by part number:


ID: 1234 Name: crank arm
ID: 1334 Name:
ID: 1434 Name: regular seat
ID: 1444 Name: banana seat
ID: 1534 Name: cassette
ID: 1634 Name: shift lever

After sort by name:


ID: 1334 Name:
ID: 1444 Name: banana seat
ID: 1534 Name: cassette
ID: 1234 Name: crank arm
ID: 1434 Name: regular seat
ID: 1634 Name: shift lever

*/
}
}

Confira também
IComparer
IEquatable<T>
IComparer<T>
IComparable
IComparable<T>
Tipos de coleção Sorted
Artigo • 10/05/2023

A classe System.Collections.SortedList, a classe genérica


System.Collections.Generic.SortedList<TKey,TValue> e a classe genérica
System.Collections.Generic.SortedDictionary<TKey,TValue> são semelhantes à classe
Hashtable e à classe genérica Dictionary<TKey,TValue>, pois elas implementam a
interface IDictionary, mas mantêm seus elementos em ordem de classificação por chave
e não têm a inserção de O(1) nem a característica de recuperação das tabelas de hash.
As três classes têm várias funcionalidades em comum:

Todas as três classes implementam a interface System.Collections.IDictionary. As


duas classes genéricas também implementam a interface genérica
System.Collections.Generic.IDictionary<TKey,TValue>.

Cada elemento é um par chave/valor para fins de enumeração.

7 Observação

A classe não genérica SortedList retorna objetos DictionaryEntry quando


enumerada, embora os dois tipos genéricos retornem objetos
KeyValuePair<TKey,TValue>.

Os elementos são classificados de acordo com uma implementação de


System.Collections.IComparer (para a SortedList não genérica) ou uma
implementação de System.Collections.Generic.IComparer<T> (para as duas classes
genéricas).

Cada classe fornece propriedades que retornam coleções contendo apenas as


chaves ou apenas os valores.

A tabela a seguir lista algumas das diferenças entre as duas classes de listas classificadas
e a classe SortedDictionary<TKey,TValue>.

SortedList classe não genérica e SortedDictionary<TKey,TValue>


SortedList<TKey,TValue> classe genérica classe genérica

As propriedades que retornam chaves e valores são Recuperação não indexada.


indexadas, permitindo uma recuperação indexada eficiente.

A recuperação é O(log n ). A recuperação é O(log n ).


SortedList classe não genérica e SortedDictionary<TKey,TValue>
SortedList<TKey,TValue> classe genérica classe genérica

A inserção e a remoção são geralmente O( n ). No entanto, a A inserção e a remoção são O(log


inserção é O(log n ) para dados que já estão em ordem de n ).
classificação, de forma que cada elemento seja adicionado
ao final da lista. (Isso pressupõe que um redimensionamento
não é necessário.)

Usa menos memória do que um Usa mais memória do que a classe


SortedDictionary<TKey,TValue>. não genérica SortedList e a classe
genérica
SortedList<TKey,TValue>.

Para listas ou dicionários classificados que precisam estar acessíveis simultaneamente


em vários threads, você pode adicionar a lógica de classificação a uma classe derivada
de ConcurrentDictionary<TKey,TValue>. Ao considerar a imutabilidade, os seguintes
tipos imutáveis correspondentes seguem uma semântica de classificação semelhante:
ImmutableSortedSet<T> e ImmutableSortedDictionary<TKey,TValue>.

7 Observação

Para valores que contêm suas próprias chaves (por exemplo, registros de
funcionários que contêm um número de ID do funcionário), você pode criar uma
coleção com chave que tem algumas características de uma lista e algumas
características de um dicionário, derivando da classe genérica
KeyedCollection<TKey,TItem>.

A partir do .NET Framework 4, a classe SortedSet<T> fornece uma árvore de


balanceamento automático que mantém os dados na ordem classificada após inserções,
exclusões e pesquisas. Essa classe e a classe HashSet<T> implementam a interface
ISet<T>.

Confira também
System.Collections.IDictionary
System.Collections.Generic.IDictionary<TKey,TValue>
ConcurrentDictionary<TKey,TValue>
Tipos de Coleção de Uso Comum
Tipos de coleção Hashtable e Dictionary
Artigo • 07/04/2023

A classe System.Collections.Hashtable, e as classes genéricas


System.Collections.Generic.Dictionary<TKey,TValue> e
System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue>, implementam a
interface System.Collections.IDictionary. A classe genérica Dictionary<TKey,TValue>
também implementa a interface genérica IDictionary<TKey,TValue>. Portanto, cada
elemento nessas coleções é um par chave-valor.

Um Hashtable objeto consiste em buckets que contêm os elementos da coleção. Um


bucket é um subgrupo virtual de elementos dentro de Hashtable, o que torna a
pesquisa e a recuperação mais fáceis e rápidas do que na maioria das coleções. Cada
bucket é associado um código hash, que é gerado usando uma função de hash e tem
como base a chave do elemento.

A classe HashSet<T> genérica é uma coleção não ordenada com a finalidade de conter
elementos exclusivos.

Uma função de hash é um algoritmo que retorna um código hash numérico com base
em uma chave. A chave é o valor da alguma propriedade do objeto sendo armazenado.
Uma função de hash deve sempre retornar o mesmo código hash para a mesma chave.
É possível para uma função de hash gerar o mesmo código hash para duas chaves
diferentes, mas uma função de hash que gera um código hash exclusivo para cada
chave específica resulta em um melhor desempenho ao recuperar os elementos da
tabela de hash.

Cada objeto que é usado como um elemento em um Hashtable deve ser capaz de gerar
um código hash para si mesmo usando uma implementação do método GetHashCode.
No entanto, você também pode especificar uma função de hash para todos os
elementos em uma Hashtable usando um construtor Hashtable que aceita uma
implementação IHashCodeProvider como um de seus parâmetros.

Quando um objeto é adicionado a um Hashtable, ele é armazenado no compartimento


de memória que está associado ao código hash que corresponde ao código hash do
objeto. Quando um valor está sendo procurado no Hashtable, o código hash é gerado
para esse valor e o bucket associado a esse código hash é pesquisado.

Por exemplo, uma função de hash para uma cadeia de caracteres pode pegar os
códigos ASCII de cada caractere na cadeia e adicioná-los em conjunto para gerar um
código hash. A cadeia de caracteres "piquenique" teria um código hash diferente
daquele da cadeia de caracteres "cesta"; portanto, as cadeias de caracteres "piquenique"
e "cesta" ficariam em buckets diferentes. Por outro lado, "stressed" e "desserts" teriam o
mesmo código hash e ficariam no mesmo bucket.

As classes Dictionary<TKey,TValue> e ConcurrentDictionary<TKey,TValue> têm a


mesma funcionalidade que a classe Hashtable. Um Dictionary<TKey,TValue> de um tipo
específico (diferente de Object) fornece desempenho melhor do que um Hashtable para
tipos de valor. Isso ocorre porque os elementos de Hashtable são do tipo Object;
portanto, conversões boxing e unboxing normalmente ocorrem quando você armazena
ou recupera um tipo de valor. A classe ConcurrentDictionary<TKey,TValue> deve ser
usada quando vários threads podem estar acessando a coleção simultaneamente.

Confira também
Hashtable
IDictionary
IHashCodeProvider
Dictionary<TKey,TValue>
System.Collections.Generic.IDictionary<TKey,TValue>
System.Collections.Concurrent.ConcurrentDictionary<TKey,TValue>
Tipos de Coleção de Uso Comum
Coleções thread-safe
Artigo • 27/01/2024

O .NET Framework 4 introduz o namespace System.Collections.Concurrent, que inclui


várias classes de coleção que são tanto thread-safe quanto escalonáveis. Vários threads
podem adicionar ou remover itens dessas coleções de modo seguro e eficiente sem a
necessidade de sincronização adicional no código do usuário. Ao escrever um novo
código, use as classes de coleção simultâneas sempre que vários threads forem ser
gravados simultaneamente nas coleções. Se estiver lendo apenas de uma coleção
compartilhada, você poderá usar as classes no namespace System.Collections.Generic. É
recomendável não usar as classes da coleção 1.0, a menos que você precise ter como
destino o runtime do .NET Framework 1.1 ou anterior.

Sincronização de thread nas coleções do .NET


Framework 1.0 e 2.0
As coleções introduzidas no .NET Framework 1.0 são encontradas no namespace
System.Collections. Essas coleções, que incluem os comumente usados ArrayList e
Hashtable, fornecem algum acesso thread-safe por meio da propriedade Synchronized ,
que retorna um wrapper thread-safe em torno da coleção. O wrapper funciona
bloqueando toda a coleção a cada operação de adição ou remoção. Portanto, cada
thread que está tentando acessar a coleção deve aguardar por sua vez para receber esse
bloqueio. Isso não é escalonável e pode causar degradação significativa no desempenho
para coleções grandes. Além disso, o design não é totalmente protegido contra
condições de corrida. Para saber mais, confira Synchronization in Generic Collections
(Sincronização em coleções genéricas).

As classes de coleções introduzidas no .NET Framework 2.0 são encontradas no


namespace System.Collections.Generic. Entre elas List<T>, Dictionary<TKey,TValue> e
assim por diante. Essas classes fornecem desempenho e segurança de tipos
aprimorados em comparação com as classes do .NET Framework 1.0. No entanto, as
classes de coleção do .NET Framework 2.0 não fornecem nenhuma sincronização de
thread; o código de usuário deve fornecer sincronização quando itens são adicionados
ou removidos simultaneamente em vários threads.

O recomendável são as classes de coleções simultâneas no .NET Framework 4, pois elas


fornecem não apenas a segurança de tipos das classes de coleção do .NET Framework
2.0, mas também acesso thread-safe mais eficiente e completo do que o fornecido pelas
coleções do .NET Framework 1.0.
Bloqueio refinado e mecanismos sem bloqueio
Alguns dos tipos de coleção simultâneos usam mecanismos de sincronização leves
como SpinLock, SpinWait, SemaphoreSlim e CountdownEvent, que são novos no .NET
Framework 4. Esses tipos de sincronização geralmente usam giro ocupado por breves
períodos antes de colocarem o thread em um verdadeiro estado de espera. Quando
espera-se que os tempos de espera sejam muito curtos, girar é muito menos
dispendioso em termos de recursos computacionais do que esperar, o que envolve uma
transição de kernel que utiliza muitos recursos. Para classes de coleção que usam o giro,
essa eficiência significa que vários threads podem adicionar e remover itens em uma
taxa muito alta. Para saber mais sobre rotação versus bloqueio, confira SpinLock e
SpinWait.

As classes ConcurrentQueue<T> e ConcurrentStack<T> não usam bloqueios. Em vez


disso, elas usam operações do Interlocked para obter acesso thread-safe.

7 Observação

Considerando que as classes de coleções simultâneas dão suporte a ICollection,


elas fornecem implementações para as propriedades IsSynchronized e SyncRoot,
embora essas propriedades sejam irrelevantes. IsSynchronized sempre retorna
false e SyncRoot é sempre null ( Nothing no Visual Basic).

A tabela a seguir lista os tipos de coleção no namespace System.Collections.Concurrent.

ノ Expandir a tabela

Tipo Descrição

BlockingCollection<T> Fornece funcionalidade de delimitação e bloqueio de


qualquer tipo que implemente
IProducerConsumerCollection<T>. Para obter mais
informações, veja Visão geral de BlockingCollection.

ConcurrentDictionary<TKey,TValue> Implementação thread-safe de um dicionário de pares


chave-valor.

ConcurrentQueue<T> Implementação thread-safe de uma fila PEPS (primeiro a


entrar, primeiro a sair).

ConcurrentStack<T> Implementação thread-safe de uma fila LIFO (último a


entrar, primeiro a sair).

ConcurrentBag<T> Implementação thread-safe de uma coleção não ordenada


Tipo Descrição

de elementos.

IProducerConsumerCollection<T> A interface que um tipo deve implementar para uso em um


BlockingCollection .

Tópicos Relacionados
ノ Expandir a tabela

Título Descrição

Visão geral de Este tópico descreve a funcionalidade fornecida pelo tipo


BlockingCollection BlockingCollection<T>.

Como: Adicionar e remover Descreve como adicionar e remover elementos de um


itens de um ConcurrentDictionary<TKey,TValue>
ConcurrentDictionary

Como: Adicionar e remover Descreve como adicionar e recuperar itens de uma coleta de
itens individualmente de uma bloqueio sem usar o enumerador de somente leitura.
BlockingCollection

Como: Adicionar a Descreve como usar qualquer classe de coleção como o


funcionalidade de delimitação e mecanismo de armazenamento subjacente para uma coleção
bloqueio a uma coleção IProducerConsumerCollection<T>.

Como: Usar ForEach para Descreve como usar foreach ( For Each no Visual Basic) para
remover itens de uma remover todos os itens em uma coleção de bloqueios.
BlockingCollection

Como: Usar matrizes de Descreve como usar várias coleções de bloqueio ao mesmo
coleções de bloqueio em um tempo para implementar um pipeline.
pipeline

Como: Criar um pool de objetos, Mostra como usar um recipiente simultâneo para melhorar o
usando um ConcurrentBag desempenho em cenários nos quais, em vez de criar novos
objetos continuamente, você pode reutilizá-los.

Referência
System.Collections.Concurrent

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Delegados e lambdas
Artigo • 09/05/2023

Um delegado define um tipo que representa referências aos métodos que têm uma lista
de parâmetros e um tipo de retorno específicos. Um método (estático ou instância) cuja
correspondência de lista de parâmetros e tipo de retorno podem ser atribuídos a uma
variável desse tipo, então chamado diretamente (com os argumentos adequados) ou
passado como um argumento para outro método e, então, chamado. O exemplo a
seguir demonstra o uso de um delegado.

C#

using System;
using System.Linq;

public class Program


{
public delegate string Reverse(string s);

static string ReverseString(string s)


{
return new string(s.Reverse().ToArray());
}

static void Main(string[] args)


{
Reverse rev = ReverseString;

Console.WriteLine(rev("a string"));
}
}

A linha public delegate string Reverse(string s); cria um tipo de delegado de


um método que usa um parâmetro de cadeia de caracteres e retorna um
parâmetro de cadeia de caracteres.
O método static string ReverseString(string s) , que tem exatamente a mesma
lista de parâmetros e tipo de retorno que o tipo de delegado definido, implementa
o delegado.
A linha Reverse rev = ReverseString; mostra que você pode atribuir um método a
uma variável do tipo de delegado correspondente.
A linha Console.WriteLine(rev("a string")); demonstra como usar uma variável
de um tipo de delegado para invocar o delegado.

Para simplificar o processo de desenvolvimento, o .NET inclui um conjunto de tipos de


delegados que os programadores podem reutilizar, sem precisar criar novos tipos. Esses
tipos são Func<> , Action<> e Predicate<> , e podem ser usados em vários locais das
APIs .NET sem a necessidade de definir novos tipos de delegado. Há algumas diferenças
entre os três tipos que têm a ver com a maneira como eles devem ser usados:

Action<> é usado quando é necessário executar uma ação usando os argumentos


do delegado. O método que ele encapsula não retorna um valor.
Func<> é normalmente é usado quando você tem uma transformação à mão, ou

seja, quando é necessário transformar os argumentos do delegado em um


resultado diferente. Projeções são um bom exemplo. O método que ele encapsula
retorna um valor especificado.
Predicate<> é usado quando você precisa determinar se o argumento satisfaz a

condição do delegado. Ele também pode ser escrito como um Func<T, bool> , o
que significa que o método retorna um valor booliano.

Agora, podemos pegar nosso exemplo acima e reescrevê-lo usando o delegado Func<>
em vez de um tipo personalizado. O programa continuará sendo executado exatamente
da mesma forma.

C#

using System;
using System.Linq;

public class Program


{
static string ReverseString(string s)
{
return new string(s.Reverse().ToArray());
}

static void Main(string[] args)


{
Func<string, string> rev = ReverseString;

Console.WriteLine(rev("a string"));
}
}

Para este exemplo simples, ter um método definido fora do método Main parece um
pouco supérfluo. O .NET Framework 2.0 introduziu o conceito de representantes
anônimos, que permitem criar delegados "embutidos" sem necessidade de especificar
nenhum tipo ou método adicional.

No exemplo a seguir, um delegado anônimo filtra uma lista apenas para os números
pares e os imprime no console.
C#

using System;
using System.Collections.Generic;

public class Program


{
public static void Main(string[] args)
{
List<int> list = new List<int>();

for (int i = 1; i <= 100; i++)


{
list.Add(i);
}

List<int> result = list.FindAll(


delegate (int no)
{
return (no % 2 == 0);
}
);

foreach (var item in result)


{
Console.WriteLine(item);
}
}
}

Como você pode ver, o corpo do delegado é apenas um conjunto de expressões, como
aconteceria com qualquer outro delegado. Mas em vez de ele ser uma definição
separada, nós o introduzimos ad hoc em nossa chamada ao método List<T>.FindAll.

No entanto, mesmo com essa abordagem, ainda há muito código que podemos
descartar. É aí que as expressões lambda entram em cena. As expressões lambda ou
apenas "lambdas" para abreviar, foram introduzidas no C# 3.0 como um dos principais
elementos de construção da LINQ (Consulta integrada à linguagem). Elas são apenas
uma sintaxe mais conveniente para usar delegados. Elas declaram uma lista de
parâmetros e um corpo do método, mas não têm uma identidade formal própria, a
menos que sejam atribuídas a um delegado. Ao contrário dos representantes, elas
podem ser atribuídas diretamente como o lado direito do registro de eventos ou em
várias cláusulas e métodos LINQ.

Como uma expressão lambda é apenas outra maneira de especificar um delegado, nós
podemos reescrever o exemplo acima para usar uma expressão lambda em vez de um
delegado anônimo.
C#

using System;
using System.Collections.Generic;

public class Program


{
public static void Main(string[] args)
{
List<int> list = new List<int>();

for (int i = 1; i <= 100; i++)


{
list.Add(i);
}

List<int> result = list.FindAll(i => i % 2 == 0);

foreach (var item in result)


{
Console.WriteLine(item);
}
}
}

No exemplo anterior, a expressão lambda usada é i => i % 2 == 0 . Mais uma vez, é


apenas uma sintaxe conveniente para usar delegados. O que acontece nos bastidores é
semelhante ao que acontece com o delegado anônimo.

Novamente, as lambdas são apenas delegados, o que significa que podem ser usadas
como um manipulador de eventos sem problemas, como o snippet de código a seguir
ilustra.

C#

public MainWindow()
{
InitializeComponent();

Loaded += (o, e) =>


{
this.Title = "Loaded";
};
}

O operador += nesse contexto é usado para assinar um evento. Para obter mais
informações, confira Como assinar e cancelar a assinatura de eventos.
Recursos e leituras adicionais
Representantes
Expressões lambda
Métodos
System.Delegate.CreateDelegate
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Os CreateDelegate métodos criam um delegado de um tipo especificado.

Método CreateDelegate(Type, MethodInfo)


Essa sobrecarga de método é equivalente a chamar a sobrecarga de
CreateDelegate(Type, MethodInfo, Boolean) método e especificar true para
throwOnBindFailure .

Exemplos
Esta seção contém dois exemplos de código. O primeiro exemplo demonstra os dois
tipos de delegados que podem ser criados com essa sobrecarga de método: abrir sobre
um método de instância e abrir sobre um método estático.

O segundo exemplo de código demonstra tipos de parâmetro correspondentes e tipos


de retorno.

Exemplo 1
O exemplo de código a seguir demonstra as duas maneiras que um delegado pode ser
criado usando essa sobrecarga do CreateDelegate método.

7 Observação

Há duas sobrecargas do CreateDelegate método que especificam um argumento,


mas não um MethodInfo primeiro: sua funcionalidade é a mesma, exceto que uma
permite que você especifique se deve lançar a falha para vincular, e a outra sempre
lança. Este exemplo de código usa ambas as sobrecargas.

O exemplo declara uma classe C com um método estático e um método M1 M2 de


instância , e dois tipos delegados: D1 usa uma instância de e uma cadeia de caracteres e
D2 usa uma cadeia de C caracteres.

Uma segunda classe chamada Example contém o código que cria os representantes.

Um representante de tipo D1 , representando um método de instância aberto, é


criado para o método de instância M1 . Uma instância deve ser passada quando o
representante é invocado.
Um representante de tipo D2 , representando um método estático aberto, é criado
para o método estático M2 .

C#

using System;
using System.Reflection;

// Declare three delegate types for demonstrating the combinations


// of static versus instance methods and open versus closed
// delegates.
//
public delegate void D1(C c, string s);
public delegate void D2(string s);
public delegate void D3();

// A sample class with an instance method and a static method.


//
public class C
{
private int id;
public C(int id) { this.id = id; }

public void M1(string s)


{
Console.WriteLine("Instance method M1 on C: id = {0}, s = {1}",
this.id, s);
}

public static void M2(string s)


{
Console.WriteLine("Static method M2 on C: s = {0}", s);
}
}

public class Example2


{
public static void Main()
{
C c1 = new C(42);

// Get a MethodInfo for each method.


//
MethodInfo mi1 = typeof(C).GetMethod("M1",
BindingFlags.Public | BindingFlags.Instance);
MethodInfo mi2 = typeof(C).GetMethod("M2",
BindingFlags.Public | BindingFlags.Static);

D1 d1;
D2 d2;
D3 d3;

Console.WriteLine("\nAn instance method closed over C.");


// In this case, the delegate and the
// method must have the same list of argument types; use
// delegate type D2 with instance method M1.
//
Delegate test =
Delegate.CreateDelegate(typeof(D2), c1, mi1, false);

// Because false was specified for throwOnBindFailure


// in the call to CreateDelegate, the variable 'test'
// contains null if the method fails to bind (for
// example, if mi1 happened to represent a method of
// some class other than C).
//
if (test != null)
{
d2 = (D2)test;

// The same instance of C is used every time the


// delegate is invoked.
d2("Hello, World!");
d2("Hi, Mom!");
}

Console.WriteLine("\nAn open instance method.");


// In this case, the delegate has one more
// argument than the instance method; this argument comes
// at the beginning, and represents the hidden instance
// argument of the instance method. Use delegate type D1
// with instance method M1.
//
d1 = (D1)Delegate.CreateDelegate(typeof(D1), null, mi1);

// An instance of C must be passed in each time the


// delegate is invoked.
//
d1(c1, "Hello, World!");
d1(new C(5280), "Hi, Mom!");

Console.WriteLine("\nAn open static method.");


// In this case, the delegate and the method must
// have the same list of argument types; use delegate type
// D2 with static method M2.
//
d2 = (D2)Delegate.CreateDelegate(typeof(D2), null, mi2);

// No instances of C are involved, because this is a static


// method.
//
d2("Hello, World!");
d2("Hi, Mom!");

Console.WriteLine("\nA static method closed over the first argument


(String).");
// The delegate must omit the first argument of the method.
// A string is passed as the firstArgument parameter, and
// the delegate is bound to this string. Use delegate type
// D3 with static method M2.
//
d3 = (D3)Delegate.CreateDelegate(typeof(D3),
"Hello, World!", mi2);

// Each time the delegate is invoked, the same string is


// used.
d3();
}
}

/* This code example produces the following output:

An instance method closed over C.


Instance method M1 on C: id = 42, s = Hello, World!
Instance method M1 on C: id = 42, s = Hi, Mom!

An open instance method.


Instance method M1 on C: id = 42, s = Hello, World!
Instance method M1 on C: id = 5280, s = Hi, Mom!

An open static method.


Static method M2 on C: s = Hello, World!
Static method M2 on C: s = Hi, Mom!

A static method closed over the first argument (String).


Static method M2 on C: s = Hello, World!
*/

Exemplo 2

O exemplo de código a seguir demonstra a compatibilidade dos tipos de parâmetro e


de retorno.

O exemplo de código define uma classe base chamada Base e uma classe chamada
Derived derivada de Base . A classe derivada tem um método static ( Shared no Visual

Basic) chamado MyMethod com um parâmetro do tipo Base e um retorno do tipo de


Derived . O exemplo de código também define um representante chamado Example que
tem um parâmetro do tipo Derived e um tipo de retorno de Base .
O exemplo de código demonstra que o representante chamado Example pode ser
usado para representar o método MyMethod . O método pode ser associado ao
representante porque:

O tipo de parâmetro do representante ( Derived ) é mais restritivo do que o tipo de


parâmetro MyMethod ( Base ), logo, é sempre seguro passar o argumento do
representante para MyMethod .
O tipo de retorno de MyMethod ( Derived ) é mais restritivo do que o tipo de
parâmetro do representante ( Base ), logo, é sempre seguro converter o tipo de
retorno do método no tipo de retorno do representante.

O exemplo de código não produz nenhuma saída.

C#

using System;
using System.Reflection;

// Define two classes to use in the demonstration, a base class and


// a class that derives from it.
//
public class Base { }

public class Derived : Base


{
// Define a static method to use in the demonstration. The method
// takes an instance of Base and returns an instance of Derived.
// For the purposes of the demonstration, it is not necessary for
// the method to do anything useful.
//
public static Derived MyMethod(Base arg)
{
Base dummy = arg;
return new Derived();
}
}

// Define a delegate that takes an instance of Derived and returns an


// instance of Base.
//
public delegate Base Example5(Derived arg);

class Test
{
public static void Main()
{
// The binding flags needed to retrieve MyMethod.
BindingFlags flags = BindingFlags.Public | BindingFlags.Static;

// Get a MethodInfo that represents MyMethod.


MethodInfo minfo = typeof(Derived).GetMethod("MyMethod", flags);

// Demonstrate contravariance of parameter types and covariance


// of return types by using the delegate Example5 to represent
// MyMethod. The delegate binds to the method because the
// parameter of the delegate is more restrictive than the
// parameter of the method (that is, the delegate accepts an
// instance of Derived, which can always be safely passed to
// a parameter of type Base), and the return type of MyMethod
// is more restrictive than the return type of Example5 (that
// is, the method returns an instance of Derived, which can
// always be safely cast to type Base).
//
Example5 ex =
(Example5)Delegate.CreateDelegate(typeof(Example5), minfo);

// Execute MyMethod using the delegate Example5.


//
Base b = ex(new Derived());
}
}

Métodos CreateDelegate(Type, Object,


MethodInfo) e CreateDelegate(Type, Object,
MethodInfo, Boolean)
A funcionalidade dessas duas sobrecargas é a mesma, exceto que uma permite que
você especifique se deve lançar a falha para ligar, e a outra sempre lança.

O tipo de delegado e o método devem ter tipos de retorno compatíveis. Ou seja, o tipo
de retorno de deve ser atribuível ao tipo de retorno de method type .

firstArgument , o segundo parâmetro para essas sobrecargas, é o primeiro argumento

do método que o delegado representa. Se firstArgument é fornecido, é passado para


method cada vez que o delegado é invocado, firstArgument é dito estar vinculado ao

delegado, e o delegado é dito ser fechado sobre seu primeiro argumento. Se method for
( no Visual Basic), a lista de argumentos fornecida ao invocar o delegado inclui todos os
parâmetros, exceto o primeiro; se method for static um método de instância, então
firstArgument é passado para o parâmetro de instância oculto ( Shared representado

por em C# ou por this Me em Visual Basic).

Se firstArgument for fornecido, o primeiro parâmetro de deve ser um tipo de method


referência e firstArgument deve ser compatível com esse tipo.
) Importante

Se method for ( Shared no Visual Basic) e seu primeiro parâmetro for static do tipo
ou ValueType, então firstArgument pode ser um tipo Object de valor. Neste caso
firstArgument é automaticamente encaixotado. Boxing automático não ocorre

para quaisquer outros argumentos, como seria em uma chamada de função C# ou


Visual Basic.

Se firstArgument for uma referência nula e for um método de instância, o resultado


dependerá das assinaturas do tipo type delegado e method de method :

Se a assinatura de incluir explicitamente o primeiro parâmetro oculto de , o


delegado representará um método de type method instância aberta. Quando o
delegado é chamado, o primeiro argumento na lista de argumentos é passado
para o parâmetro de instância oculta de method .
Se as assinaturas de e type corresponderem (ou seja, todos os tipos de method
parâmetro forem compatíveis), o delegado será dito fechado sobre uma referência
nula. Invocar o delegado é como chamar um método de instância em uma
instância nula, o que não é uma coisa particularmente útil a fazer.

Se firstArgument for uma referência nula e for estática, o resultado dependerá das
assinaturas do tipo type delegado e method de method :

Se a assinatura de e type corresponder (ou seja, todos os tipos de method


parâmetro forem compatíveis), o delegado representará um método estático
aberto. Este é o caso mais comum para métodos estáticos. Nesse caso, você pode
obter um desempenho um pouco melhor usando a sobrecarga do
CreateDelegate(Type, MethodInfo) método.
Se a assinatura de começa com o segundo parâmetro de e o resto dos tipos de
type method parâmetro são compatíveis, então o delegado é dito ser fechado

sobre uma referência nula. Quando o delegado é chamado, uma referência nula é
passada para o primeiro parâmetro de method .

Exemplo
O exemplo de código a seguir mostra todos os métodos que um único tipo de
delegado pode representar: fechado sobre um método de instância, aberto sobre um
método de instância, aberto sobre um método estático e fechado sobre um método
estático.
O exemplo de código define duas classes C e , e F um tipo delegado com um
argumento do tipo C D . As classes têm métodos M1 estáticos e de instância
correspondentes, e , M3 e M4 a classe C também tem um método M2 de instância que
não tem argumentos.

Uma terceira classe chamada Example contém o código que cria os representantes.

Os delegados são criados, por exemplo, método M1 de tipo e tipo, cada um é


fechado sobre uma instância do respectivo tipo C F . Método M1 do tipo C exibe
as ID propriedades da instância acoplada e do argumento.
Um delegado é criado para o método M2 do tipo C . Este é um delegado de
instância aberta, no qual o argumento do delegado representa o primeiro
argumento oculto no método de instância. O método não tem outros argumentos.
É chamado como se fosse um método estático.
Os delegados são criados para o método M3 estático de tipo e tipo F C , são
delegados estáticos abertos.
Finalmente, os delegados são criados para o método estático de tipo e tipo, cada
método M4 tem o tipo declarante como seu primeiro argumento, e uma instância
do tipo C F é fornecida, de modo que os delegados são fechados sobre seus
primeiros argumentos. Método M4 do tipo C exibe as ID propriedades da
instância acoplada e do argumento.

C#

using System;
using System.Reflection;

// Declare a delegate type. The object of this code example


// is to show all the methods this delegate can bind to.
//
public delegate void D(C1 c);

// Declare two sample classes, C1 and F. Class C1 has an ID


// property so instances can be identified.
//
public class C1
{
private int id;
public int ID { get { return id; } }
public C1(int id) { this.id = id; }

public void M1(C1 c)


{
Console.WriteLine("Instance method M1(C1 c) on C1: this.id = {0},
c.ID = {1}",
this.id, c.ID);
}
public void M2()
{
Console.WriteLine("Instance method M2() on C1: this.id = {0}",
this.id);
}

public static void M3(C1 c)


{
Console.WriteLine("Static method M3(C1 c) on C1: c.ID = {0}",
c.ID);
}

public static void M4(C1 c1, C1 c2)


{
Console.WriteLine("Static method M4(C1 c1, C1 c2) on C1: c1.ID =
{0}, c2.ID = {1}",
c1.ID, c2.ID);
}
}

public class F
{
public void M1(C1 c)
{
Console.WriteLine("Instance method M1(C1 c) on F: c.ID = {0}",
c.ID);
}

public static void M3(C1 c)


{
Console.WriteLine("Static method M3(C1 c) on F: c.ID = {0}", c.ID);
}

public static void M4(F f, C1 c)


{
Console.WriteLine("Static method M4(F f, C1 c) on F: c.ID = {0}",
c.ID);
}
}

public class Example


{
public static void Main()
{
C1 c1 = new C1(42);
C1 c2 = new C1(1491);
F f1 = new F();

D d;

// Instance method with one argument of type C1.


MethodInfo cmi1 = typeof(C1).GetMethod("M1");
// Instance method with no arguments.
MethodInfo cmi2 = typeof(C1).GetMethod("M2");
// Static method with one argument of type C1.
MethodInfo cmi3 = typeof(C1).GetMethod("M3");
// Static method with two arguments of type C1.
MethodInfo cmi4 = typeof(C1).GetMethod("M4");

// Instance method with one argument of type C1.


MethodInfo fmi1 = typeof(F).GetMethod("M1");
// Static method with one argument of type C1.
MethodInfo fmi3 = typeof(F).GetMethod("M3");
// Static method with an argument of type F and an argument
// of type C1.
MethodInfo fmi4 = typeof(F).GetMethod("M4");

Console.WriteLine("\nAn instance method on any type, with an


argument of type C1.");
// D can represent any instance method that exactly matches its
// signature. Methods on C1 and F are shown here.
//
d = (D)Delegate.CreateDelegate(typeof(D), c1, cmi1);
d(c2);
d = (D)Delegate.CreateDelegate(typeof(D), f1, fmi1);
d(c2);

Console.WriteLine("\nAn instance method on C1 with no arguments.");


// D can represent an instance method on C1 that has no arguments;
// in this case, the argument of D represents the hidden first
// argument of any instance method. The delegate acts like a
// static method, and an instance of C1 must be passed each time
// it is invoked.
//
d = (D)Delegate.CreateDelegate(typeof(D), null, cmi2);
d(c1);

Console.WriteLine("\nA static method on any type, with an argument


of type C1.");
// D can represent any static method with the same signature.
// Methods on F and C1 are shown here.
//
d = (D)Delegate.CreateDelegate(typeof(D), null, cmi3);
d(c1);
d = (D)Delegate.CreateDelegate(typeof(D), null, fmi3);
d(c1);

Console.WriteLine("\nA static method on any type, with an argument


of");
Console.WriteLine(" that type and an argument of type C1.");
// D can represent any static method with one argument of the
// type the method belongs and a second argument of type C1.
// In this case, the method is closed over the instance of
// supplied for the its first argument, and acts like an instance
// method. Methods on F and C1 are shown here.
//
d = (D)Delegate.CreateDelegate(typeof(D), c1, cmi4);
d(c2);
Delegate test =
Delegate.CreateDelegate(typeof(D), f1, fmi4, false);

// This final example specifies false for throwOnBindFailure


// in the call to CreateDelegate, so the variable 'test'
// contains Nothing if the method fails to bind (for
// example, if fmi4 happened to represent a method of
// some class other than F).
//
if (test != null)
{
d = (D)test;
d(c2);
}
}
}

/* This code example produces the following output:

An instance method on any type, with an argument of type C1.


Instance method M1(C1 c) on C1: this.id = 42, c.ID = 1491
Instance method M1(C1 c) on F: c.ID = 1491

An instance method on C1 with no arguments.


Instance method M2() on C1: this.id = 42

A static method on any type, with an argument of type C1.


Static method M3(C1 c) on C1: c.ID = 42
Static method M3(C1 c) on F: c.ID = 42

A static method on any type, with an argument of


that type and an argument of type C1.
Static method M4(C1 c1, C1 c2) on C1: c1.ID = 42, c2.ID = 1491
Static method M4(F f, C1 c) on F: c.ID = 1491
*/

Tipos de parâmetros compatíveis e tipo de


retorno
Os tipos de parâmetro e o tipo de retorno de um delegado criado usando essa
sobrecarga de método devem ser compatíveis com os tipos de parâmetro e o tipo de
retorno do método que o delegado representa; os tipos não precisam corresponder
exatamente.

Um parâmetro de um delegado será compatível com o parâmetro correspondente de


um método se o tipo do parâmetro de delegado for mais restritivo do que o tipo do
parâmetro de método, porque isso garante que um argumento passado para o
delegado possa ser passado com segurança para o método.
Da mesma forma, o tipo de retorno de um delegado será compatível com o tipo de
retorno de um método se o tipo de retorno do método for mais restritivo do que o tipo
de retorno do delegado, porque isso garante que o valor retornado do método possa
ser convertido com segurança para o tipo retorno do delegado.

Por exemplo, um delegado com um parâmetro de type e um return type de pode


representar um método com um parâmetro de type e um valor de return de Object type
HashtableObjectHashtable.

Determinar os métodos que um delegado pode


representar
Outra maneira útil de pensar na flexibilidade fornecida pela CreateDelegate(Type,
Object, MethodInfo) sobrecarga é que qualquer delegado pode representar quatro
combinações diferentes de assinatura de método e tipo de método (estático versus
instância). Considere um tipo de delegado com um argumento do tipo D C . O seguinte
descreve os métodos D que podem representar, ignorando o tipo de retorno, uma vez
que ele deve corresponder em todos os casos:

D pode representar qualquer método de instância que tenha exatamente um

argumento do tipo , independentemente do tipo C ao qual o método de instância


pertence. Quando CreateDelegate é chamado, é uma instância do tipo method a
que pertence, firstArgument e o delegado resultante é dito ser fechado sobre essa
instância. (Trivialmente, D também pode ser fechado sobre uma referência nula se
firstArgument for uma referência nula.)

D pode representar um método de C instância que não tem argumentos. Quando

CreateDelegate é chamado, firstArgument é uma referência nula. O delegado


resultante representa um método de instância aberta e uma instância de C deve
ser fornecida sempre que for invocada.

D pode representar um método estático que usa um argumento do tipo , e esse

método pode pertencer a qualquer tipo C . Quando CreateDelegate é chamado,


firstArgument é uma referência nula. O delegado resultante representa um
método estático aberto e uma instância de C deve ser fornecida sempre que é
invocada.

D pode representar um método estático que pertence ao tipo e tem dois

argumentos, de tipo e tipo F F C . Quando CreateDelegate é chamado,


firstArgument é uma instância de F . O delegado resultante representa um
método estático que é fechado sobre essa instância do F . Observe que no caso
em F que e C são do mesmo tipo, o método estático tem dois argumentos desse
tipo. (Nesse caso, D é fechado sobre uma referência nula se firstArgument for
uma referência nula.)

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Enum classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Uma enumeração é um conjunto de constantes nomeadas cujo tipo subjacente é


qualquer tipo integral. Se nenhum tipo subjacente for declarado explicitamente, Int32
será usado. Enum é a classe base para todas as enumerações no .NET. Os tipos de
enumeração são definidos pela enum palavra-chave em C#, a construção ... End Enum no
Visual Basic e a Enum type palavra-chave em F#.

Enum Fornece métodos para comparar instâncias dessa classe, converter o valor de uma
instância em sua representação de cadeia de caracteres, converter a representação de
cadeia de caracteres de um número em uma instância dessa classe e criar uma instância
de uma enumeração e valor especificados.

Você também pode tratar uma enumeração como um campo de bits. Para obter mais
informações, consulte a seção Membros não exclusivos e o atributo Sinalizadores e
FlagsAttribute.

Criar um tipo de enumeração


As linguagens de programação normalmente fornecem sintaxe para declarar uma
enumeração que consiste em um conjunto de constantes nomeadas e seus valores. O
exemplo a seguir ilustra a sintaxe usada por C#, F# e Visual Basic para definir uma
enumeração. Ele cria uma enumeração nomeada ArrivalStatus que tem três membros:
ArrivalStatus.Early , ArrivalStatus.OnTime e ArrivalStatus.Late . Observe que, em

todos os casos, a enumeração não herda explicitamente de ; a relação de Enumherança


é tratada implicitamente pelo compilador.

C#

public enum ArrivalStatus { Unknown=-3, Late=-1, OnTime=0, Early=1 };

2 Aviso

Você nunca deve criar um tipo de enumeração cujo tipo subjacente seja não
integral ou Char. Embora você possa criar esse tipo de enumeração usando
reflexão, as chamadas de método que usam o tipo resultante não são confiáveis e
também podem lançar exceções adicionais.

Instanciar um tipo de enumeração


Você pode instanciar um tipo de enumeração da mesma forma que instancia qualquer
outro tipo de valor: declarando uma variável e atribuindo uma das constantes da
enumeração a ela. O exemplo a seguir instancia um ArrivalStatus cujo valor é
ArrivalStatus.OnTime .

C#

public class Example


{
public static void Main()
{
ArrivalStatus status = ArrivalStatus.OnTime;
Console.WriteLine("Arrival Status: {0} ({0:D})", status);
}
}
// The example displays the following output:
// Arrival Status: OnTime (0)

Você também pode instanciar um valor de enumeração das seguintes maneiras:

Usando os recursos de uma linguagem de programação específica para converter


(como em C#) ou converter (como no Visual Basic) um valor inteiro em um valor
de enumeração. O exemplo a seguir cria um ArrivalStatus objeto cujo valor está
ArrivalStatus.Early dessa maneira.

C#

ArrivalStatus status2 = (ArrivalStatus)1;


Console.WriteLine("Arrival Status: {0} ({0:D})", status2);
// The example displays the following output:
// Arrival Status: Early (1)

Chamando seu construtor implícito sem parâmetros. Como mostra o exemplo a


seguir, nesse caso, o valor subjacente da instância de enumeração é 0. No entanto,
esse não é necessariamente o valor de uma constante válida na enumeração.

C#
ArrivalStatus status1 = new ArrivalStatus();
Console.WriteLine("Arrival Status: {0} ({0:D})", status1);
// The example displays the following output:
// Arrival Status: OnTime (0)

Chamando o método or TryParse para analisar uma cadeia de caracteres que


contém o Parse nome de uma constante na enumeração. Para obter mais
informações, consulte a seção Analisar valores de enumeração.

Chamando o ToObject método para converter um valor integral em um tipo de


enumeração. Para obter mais informações, consulte a seção Executar conversões .

Práticas recomendadas de enumeração


Recomendamos que você use as seguintes práticas recomendadas ao definir tipos de
enumeração:

Se você não tiver definido um membro de enumeração cujo valor seja 0, considere
a criação de uma None constante enumerada. Por padrão, a memória usada para a
enumeração é inicializada como zero pelo Common Language Runtime.
Consequentemente, se você não definir uma constante cujo valor é zero, a
enumeração conterá um valor ilegal quando for criada.

Se houver um caso padrão óbvio que seu aplicativo precisa representar, considere
usar uma constante enumerada cujo valor é zero para representá-lo. Se não
houver nenhum caso padrão, considere usar uma constante enumerada cujo valor
é zero para especificar o caso que não é representado por nenhuma das outras
constantes enumeradas.

Não especifique constantes enumeradas que são reservadas para uso futuro.

Ao definir um método ou propriedade que usa uma constante enumerada como


um valor, considere validar o valor. O motivo é que você pode converter um valor
numérico para o tipo de enumeração mesmo se esse valor numérico não estiver
definido na enumeração.

Práticas recomendadas adicionais para tipos de enumeração cujas constantes são


campos de bit são listadas na seção Membros não exclusivos e na seção Atributo
Sinalizadores .

Executar operações com enumerações


Não é possível definir novos métodos ao criar uma enumeração. No entanto, um tipo de
enumeração herda um conjunto completo de métodos estáticos e de instância da Enum
classe. As seções a seguir examinam a maioria desses métodos, além de vários outros
métodos que são comumente usados ao trabalhar com valores de enumeração.

Realizar conversões
Você pode converter entre um membro de enumeração e seu tipo subjacente usando
um operador de conversão (em C# e F#) ou conversão (em Visual Basic). Em F#, a enum
função também é usada. O exemplo a seguir usa operadores de conversão ou
conversão para executar conversões de um inteiro para um valor de enumeração e de
um valor de enumeração para um inteiro.

C#

int value3 = 2;
ArrivalStatus status3 = (ArrivalStatus)value3;

int value4 = (int)status3;

A Enum classe também inclui um método que converte um valor de qualquer tipo
integral em um ToObject valor de enumeração. O exemplo a seguir usa o ToObject(Type,
Int32) método para converter um em um Int32 ArrivalStatus valor. Observe que, como
o retorna um valor de tipo , o uso de um operador de conversão ou conversão de
conversão ainda pode ser necessário para converter o objeto para o ToObject tipo
Objectde enumeração.

C#

int number = -1;


ArrivalStatus arrived =
(ArrivalStatus)ArrivalStatus.ToObject(typeof(ArrivalStatus), number);

Ao converter um inteiro em um valor de enumeração, é possível atribuir um valor que


não seja realmente um membro da enumeração. Para evitar isso, você pode passar o
inteiro para o IsDefined método antes de executar a conversão. O exemplo a seguir usa
esse método para determinar se os elementos em uma matriz de valores inteiros podem
ser convertidos em ArrivalStatus valores.

C#

using System;
public class Example3
{
public static void Main()
{
int[] values = { -3, -1, 0, 1, 5, Int32.MaxValue };
foreach (var value in values)
{
ArrivalStatus status;
if (Enum.IsDefined(typeof(ArrivalStatus), value))
status = (ArrivalStatus)value;
else
status = ArrivalStatus.Unknown;
Console.WriteLine("Converted {0:N0} to {1}", value, status);
}
}
}
// The example displays the following output:
// Converted -3 to Unknown
// Converted -1 to Late
// Converted 0 to OnTime
// Converted 1 to Early
// Converted 5 to Unknown
// Converted 2,147,483,647 to Unknown

Embora a Enum classe forneça implementações de interface explícitas da IConvertible


interface para converter de um valor de enumeração para um tipo integral, você deve
usar os Convert métodos da classe, como ToInt32, para executar essas conversões. O
exemplo a seguir ilustra como você pode usar o método junto com o
GetUnderlyingTypeConvert.ChangeType método para converter um valor de
enumeração em seu tipo subjacente. Observe que este exemplo não exige que o tipo
subjacente da enumeração seja conhecido em tempo de compilação.

C#

ArrivalStatus status = ArrivalStatus.Early;


var number = Convert.ChangeType(status,
Enum.GetUnderlyingType(typeof(ArrivalStatus)));
Console.WriteLine("Converted {0} to {1}", status, number);
// The example displays the following output:
// Converted Early to 1

Analisar valores de enumeração


Os Parse métodos e TryParse permitem converter a representação de cadeia de
caracteres de um valor de enumeração para esse valor. A representação de cadeia de
caracteres pode ser o nome ou o valor subjacente de uma constante de enumeração.
Observe que os métodos de análise converterão com êxito representações de cadeia de
caracteres de números que não são membros de uma enumeração específica se as
cadeias de caracteres puderem ser convertidas em um valor do tipo subjacente da
enumeração. Para evitar isso, o IsDefined método pode ser chamado para garantir que o
resultado do método de análise é um valor de enumeração válido. O exemplo ilustra
essa abordagem e demonstra chamadas para os Parse(Type, String) métodos e
Enum.TryParse<TEnum>(String, TEnum) . Observe que o método de análise não
genérico retorna um objeto que talvez você precise converter (em C# e F#) ou converter
(no Visual Basic) para o tipo de enumeração apropriado.

C#

string number = "-1";


string name = "Early";

try
{
ArrivalStatus status1 = (ArrivalStatus)Enum.Parse(typeof(ArrivalStatus),
number);
if (!(Enum.IsDefined(typeof(ArrivalStatus), status1)))
status1 = ArrivalStatus.Unknown;
Console.WriteLine("Converted '{0}' to {1}", number, status1);
}
catch (FormatException)
{
Console.WriteLine("Unable to convert '{0}' to an ArrivalStatus value.",
number);
}

ArrivalStatus status2;
if (Enum.TryParse<ArrivalStatus>(name, out status2))
{
if (!(Enum.IsDefined(typeof(ArrivalStatus), status2)))
status2 = ArrivalStatus.Unknown;
Console.WriteLine("Converted '{0}' to {1}", name, status2);
}
else
{
Console.WriteLine("Unable to convert '{0}' to an ArrivalStatus value.",
number);
}
// The example displays the following output:
// Converted '-1' to Late
// Converted 'Early' to Early

Formatar valores de enumeração


Você pode converter valores de enumeração em suas representações de cadeia de
caracteres chamando o método estático Format , bem como as sobrecargas do método
de instância ToString . Você pode usar uma cadeia de caracteres de formato para
controlar a maneira precisa na qual um valor de enumeração é representado como uma
cadeia de caracteres. Para obter mais informações, consulte Cadeias de caracteres de
formato de enumeração. O exemplo a seguir usa cada uma das cadeias de caracteres de
formato de enumeração com suporte ("G" ou "g", "D" ou "d", "X" ou "x" e "F" ou "f")
para converter um membro da enumeração em suas representações de cadeia de
ArrivalStatus caracteres.

C#

string[] formats = { "G", "F", "D", "X" };


ArrivalStatus status = ArrivalStatus.Late;
foreach (var fmt in formats)
Console.WriteLine(status.ToString(fmt));

// The example displays the following output:


// Late
// Late
// -1
// FFFFFFFF

Iterar membros de enumeração


O Enum tipo não implementa a interface ou, que IEnumerable permitiria que você
iterasse membros de uma coleção usando uma foreach construção (em C#), for..in
(em F#) ou IEnumerable<T> For Each (no Visual Basic). No entanto, você pode
enumerar membros de duas maneiras.

Você pode chamar o GetNames método para recuperar uma matriz de cadeia de
caracteres que contém os nomes dos membros da enumeração. Em seguida, para
cada elemento da matriz de cadeia de caracteres, você pode chamar o Parse
método para converter a cadeia de caracteres em seu valor de enumeração
equivalente. O exemplo a seguir ilustra esta abordagem.

C#

string[] names = Enum.GetNames(typeof(ArrivalStatus));


Console.WriteLine("Members of {0}:", typeof(ArrivalStatus).Name);
Array.Sort(names);
foreach (var name in names)
{
ArrivalStatus status =
(ArrivalStatus)Enum.Parse(typeof(ArrivalStatus), name);
Console.WriteLine(" {0} ({0:D})", status);
}
// The example displays the following output:
// Members of ArrivalStatus:
// Early (1)
// Late (-1)
// OnTime (0)
// Unknown (-3)

Você pode chamar o GetValues método para recuperar uma matriz que contém os
valores subjacentes na enumeração. Em seguida, para cada elemento da matriz,
você pode chamar o método para converter o ToObject inteiro em seu valor de
enumeração equivalente. O exemplo a seguir ilustra esta abordagem.

C#

var values = Enum.GetValues(typeof(ArrivalStatus));


Console.WriteLine("Members of {0}:", typeof(ArrivalStatus).Name);
foreach (ArrivalStatus status in values)
{
Console.WriteLine(" {0} ({0:D})", status);
}
// The example displays the following output:
// Members of ArrivalStatus:
// OnTime (0)
// Early (1)
// Unknown (-3)
// Late (-1)

Membros não exclusivos e o atributo Flags


Um uso comum de uma enumeração é representar um conjunto de valores
mutuamente exclusivos. Por exemplo, uma ArrivalStatus instância pode ter um valor
de Early , OnTime ou Late . Não faz sentido que o valor de uma instância reflita mais de
uma ArrivalStatus constante de enumeração.

Em outros casos, no entanto, o valor de um objeto de enumeração pode incluir vários


membros de enumeração e cada membro representa um campo de bit no valor de
enumeração. O FlagsAttribute atributo pode ser usado para indicar que a enumeração
consiste em campos de bits. Por exemplo, uma enumeração nomeada Pets pode ser
usada para indicar os tipos de animais de estimação em uma casa. Pode ser definido da
seguinte forma.

C#

[Flags]
public enum Pets
{
None = 0, Dog = 1, Cat = 2, Bird = 4, Rodent = 8,
Reptile = 16, Other = 32
};

A Pets enumeração pode então ser usada como mostrado no exemplo a seguir.

C#

Pets familyPets = Pets.Dog | Pets.Cat;


Console.WriteLine("Pets: {0:G} ({0:D})", familyPets);
// The example displays the following output:
// Pets: Dog, Cat (3)

As práticas recomendadas a seguir devem ser usadas ao definir uma enumeração bit a
bit e aplicar o FlagsAttribute atributo.

Use o FlagsAttribute atributo personalizado para uma enumeração somente se


uma operação bit a bit (AND, OR, EXCLUSIVE OR) for executada em um valor
numérico.

Defina constantes de enumeração em potências de dois, ou seja, 1, 2, 4, 8 e assim


por diante. Isso significa que os sinalizadores individuais em constantes de
enumeração combinadas não se sobrepõem.

Considere a criação de uma constante enumerada para combinações de


sinalizadores comumente usadas. Por exemplo, se você tiver uma enumeração
usada para operações de E/S de Read = 1 arquivo que contém as constantes
enumeradas e , considere a criação da constante ReadWrite = Read OR
Write enumerada , que combina os Read sinalizadores e Write = 2 Write . Além

disso, a operação OR bit a bit usada para combinar os sinalizadores pode ser
considerada um conceito avançado em algumas circunstâncias que não deve ser
necessário para tarefas simples.

Tenha cuidado se você definir um número negativo como uma constante


enumerada de sinalizador porque muitas posições de sinalizador podem ser
definidas como 1, o que pode tornar seu código confuso e incentivar erros de
codificação.

Uma maneira conveniente de testar se um sinalizador está definido em um valor


numérico é chamar o método de instância HasFlag , conforme mostrado no
exemplo a seguir.

C#
Pets familyPets = Pets.Dog | Pets.Cat;
if (familyPets.HasFlag(Pets.Dog))
Console.WriteLine("The family has a dog.");
// The example displays the following output:
// The family has a dog.

É equivalente a executar uma operação bit a bit AND entre o valor numérico e a
constante enumerada do sinalizador, que define todos os bits no valor numérico
como zero que não correspondem ao sinalizador e, em seguida, testar se o
resultado dessa operação é igual à constante enumerada do sinalizador. Isso é
ilustrado no exemplo a seguir.

C#

Pets familyPets = Pets.Dog | Pets.Cat;


if ((familyPets & Pets.Dog) == Pets.Dog)
Console.WriteLine("The family has a dog.");
// The example displays the following output:
// The family has a dog.

Use None como o nome da constante enumerada do sinalizador cujo valor é zero.
Você não pode usar a constante enumerada em uma operação bit a None bit AND
para testar um sinalizador porque o resultado é sempre zero. No entanto, você
pode executar uma comparação lógica, não bit a bit, entre o valor numérico e a
None constante enumerada para determinar se algum bit no valor numérico está

definido. Isso é ilustrado no exemplo a seguir.

C#

Pets familyPets = Pets.Dog | Pets.Cat;


if (familyPets == Pets.None)
Console.WriteLine("The family has no pets.");
else
Console.WriteLine("The family has pets.");
// The example displays the following output:
// The family has pets.

Não defina um valor de enumeração apenas para espelhar o estado da própria


enumeração. Por exemplo, não defina uma constante enumerada que apenas
marca o final da enumeração. Se você precisar determinar o último valor da
enumeração, verifique esse valor explicitamente. Além disso, você pode executar
uma verificação de intervalo para a primeira e a última constante enumerada se
todos os valores dentro do intervalo forem válidos.
Adicionar métodos de enumeração
Como os tipos de enumeração são definidos por estruturas de linguagem, como enum
(C#) e Enum (Visual Basic), você não pode definir métodos personalizados para um tipo
de enumeração diferente daqueles métodos herdados da Enum classe. No entanto, você
pode usar métodos de extensão para adicionar funcionalidade a um tipo de
enumeração específico.

No exemplo a seguir, a enumeração Grades representa as letras possíveis que um aluno


pode receber em uma classe. Um método de extensão chamado Passing é adicionado
ao tipo Grades de forma que cada instância desse tipo agora "sabe" se ele representa
uma nota de aprovação ou não. A Extensions classe também contém uma variável
estática de leitura-gravação que define a nota mínima de aprovação. O valor de retorno
do Passing método de extensão reflete o valor atual dessa variável.

C#

using System;

// Define an enumeration to represent student grades.


public enum Grades { F = 0, D = 1, C = 2, B = 3, A = 4 };

// Define an extension method for the Grades enumeration.


public static class Extensions
{
public static Grades minPassing = Grades.D;

public static bool Passing(this Grades grade)


{
return grade >= minPassing;
}
}

class Example8
{
static void Main()
{
Grades g1 = Grades.D;
Grades g2 = Grades.F;
Console.WriteLine("{0} {1} a passing grade.", g1, g1.Passing() ?
"is" : "is not");
Console.WriteLine("{0} {1} a passing grade.", g2, g2.Passing() ?
"is" : "is not");

Extensions.minPassing = Grades.C;
Console.WriteLine("\nRaising the bar!\n");
Console.WriteLine("{0} {1} a passing grade.", g1, g1.Passing() ?
"is" : "is not");
Console.WriteLine("{0} {1} a passing grade.", g2, g2.Passing() ?
"is" : "is not");
}
}
// The exmaple displays the following output:
// D is a passing grade.
// F is not a passing grade.
//
// Raising the bar!
//
// D is not a passing grade.
// F is not a passing grade.

Exemplos
O exemplo a seguir demonstra o uso de uma enumeração para representar valores
nomeados e outra enumeração para representar campos de bits nomeados.

C#

using System;

public class EnumTest {


enum Days { Saturday, Sunday, Monday, Tuesday, Wednesday, Thursday,
Friday };
enum BoilingPoints { Celsius = 100, Fahrenheit = 212 };
[Flags]
enum Colors { Red = 1, Green = 2, Blue = 4, Yellow = 8 };

public static void Main() {

Type weekdays = typeof(Days);


Type boiling = typeof(BoilingPoints);

Console.WriteLine("The days of the week, and their corresponding


values in the Days Enum are:");

foreach ( string s in Enum.GetNames(weekdays) )


Console.WriteLine( "{0,-11}= {1}", s, Enum.Format( weekdays,
Enum.Parse(weekdays, s), "d"));

Console.WriteLine();
Console.WriteLine("Enums can also be created which have values that
represent some meaningful amount.");
Console.WriteLine("The BoilingPoints Enum defines the following
items, and corresponding values:");

foreach ( string s in Enum.GetNames(boiling) )


Console.WriteLine( "{0,-11}= {1}", s, Enum.Format(boiling,
Enum.Parse(boiling, s), "d"));

Colors myColors = Colors.Red | Colors.Blue | Colors.Yellow;


Console.WriteLine();
Console.WriteLine("myColors holds a combination of colors. Namely:
{0}", myColors);
}
}

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.FlagsAttribute classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O FlagsAttribute atributo indica que uma enumeração pode ser tratada como um
campo de bits, ou seja, um conjunto de sinalizadores.

Os campos de bits são geralmente usados para listas de elementos que podem ocorrer
em combinação, enquanto as constantes de enumeração são geralmente usadas para
listas de elementos mutuamente exclusivos. Portanto, os campos de bits são projetados
para serem combinados com uma operação bit a bit OR para gerar valores sem nome,
enquanto as constantes enumeradas não são. Os idiomas variam no uso de campos de
bits em comparação com constantes de enumeração.

Atributos do FlagsAttribute
AttributeUsageAttribute é aplicado a essa classe e sua Inherited propriedade especifica
false . Esse atributo só pode ser aplicado a enumerações.

Diretrizes para FlagsAttribute e enum


Use o FlagsAttribute atributo personalizado para uma enumeração somente se
uma operação bit a bit (AND, OR, EXCLUSIVE OR) for executada em um valor
numérico.

Defina constantes de enumeração em potências de dois, ou seja, 1, 2, 4, 8 e assim


por diante. Isso significa que os sinalizadores individuais em constantes de
enumeração combinadas não se sobrepõem.

Considere a criação de uma constante enumerada para combinações de


sinalizadores comumente usadas. Por exemplo, se você tiver uma enumeração
usada para operações de E/S de Read = 1 arquivo que contém as constantes
enumeradas e , considere a criação da constante ReadWrite = Read OR
Write enumerada , que combina os Read sinalizadores e Write = 2 Write . Além

disso, a operação OR bit a bit usada para combinar os sinalizadores pode ser
considerada um conceito avançado em algumas circunstâncias que não deve ser
necessário para tarefas simples.
Tenha cuidado se você definir um número negativo como uma constante
enumerada de sinalizador porque muitas posições de sinalizador podem ser
definidas como 1, o que pode tornar seu código confuso e incentivar erros de
codificação.

Uma maneira conveniente de testar se um sinalizador está definido em um valor


numérico é executar uma operação bit a bit AND entre o valor numérico e a
constante enumerada do sinalizador, que define todos os bits no valor numérico
como zero que não correspondem ao sinalizador e, em seguida, testar se o
resultado dessa operação é igual à constante enumerada do sinalizador.

Use None como o nome da constante enumerada do sinalizador cujo valor é zero.
Você não pode usar a constante enumerada em uma operação bit a None bit AND
para testar um sinalizador porque o resultado é sempre zero. No entanto, você
pode executar uma comparação lógica, não bit a bit, entre o valor numérico e a
None constante enumerada para determinar se algum bit no valor numérico está

definido.

Se você criar uma enumeração de valor em vez de uma enumeração de


sinalizadores, ainda valerá a pena criar uma None constante enumerada. O motivo
é que, por padrão, a memória usada para a enumeração é inicializada como zero
pelo Common Language Runtime. Consequentemente, se você não definir uma
constante cujo valor é zero, a enumeração conterá um valor ilegal quando for
criada.

Se houver um caso padrão óbvio que seu aplicativo precisa representar, considere
o uso de uma constante enumerada cujo valor é zero para representar o padrão.
Se não houver nenhum caso padrão, considere usar uma constante enumerada
cujo valor é zero, o que significa o caso que não é representado por nenhuma das
outras constantes enumeradas.

Não defina um valor de enumeração apenas para espelhar o estado da própria


enumeração. Por exemplo, não defina uma constante enumerada que apenas
marca o final da enumeração. Se você precisar determinar o último valor da
enumeração, verifique esse valor explicitamente. Além disso, você pode executar
uma verificação de intervalo para a primeira e a última constante enumerada se
todos os valores dentro do intervalo forem válidos.

Não especifique constantes enumeradas que são reservadas para uso futuro.

Ao definir um método ou propriedade que usa uma constante enumerada como


um valor, considere validar o valor. O motivo é que você pode converter um valor
numérico para o tipo de enumeração mesmo se esse valor numérico não estiver
definido na enumeração.

Exemplos
O exemplo a FlagsAttribute seguir ilustra o uso do atributo e mostra o efeito sobre o
ToString método de uso FlagsAttribute em uma Enum declaração.

C#

using System;

class Example
{
// Define an Enum without FlagsAttribute.
enum SingleHue : short
{
None = 0,
Black = 1,
Red = 2,
Green = 4,
Blue = 8
};

// Define an Enum with FlagsAttribute.


[Flags]
enum MultiHue : short
{
None = 0,
Black = 1,
Red = 2,
Green = 4,
Blue = 8
};

static void Main()


{
// Display all possible combinations of values.
Console.WriteLine(
"All possible combinations of values without FlagsAttribute:");
for (int val = 0; val <= 16; val++)
Console.WriteLine("{0,3} - {1:G}", val, (SingleHue)val);

// Display all combinations of values, and invalid values.


Console.WriteLine(
"\nAll possible combinations of values with FlagsAttribute:");
for (int val = 0; val <= 16; val++)
Console.WriteLine("{0,3} - {1:G}", val, (MultiHue)val);
}
}
// The example displays the following output:
// All possible combinations of values without FlagsAttribute:
// 0 - None
// 1 - Black
// 2 - Red
// 3 - 3
// 4 - Green
// 5 - 5
// 6 - 6
// 7 - 7
// 8 - Blue
// 9 - 9
// 10 - 10
// 11 - 11
// 12 - 12
// 13 - 13
// 14 - 14
// 15 - 15
// 16 - 16
//
// All possible combinations of values with FlagsAttribute:
// 0 - None
// 1 - Black
// 2 - Red
// 3 - Black, Red
// 4 - Green
// 5 - Black, Green
// 6 - Red, Green
// 7 - Black, Red, Green
// 8 - Blue
// 9 - Black, Blue
// 10 - Red, Blue
// 11 - Black, Red, Blue
// 12 - Green, Blue
// 13 - Black, Green, Blue
// 14 - Red, Green, Blue
// 15 - Black, Red, Green, Blue
// 16 - 16

O exemplo anterior define duas enumerações SingleHue relacionadas a cores e


MultiHue . O segundo tem o atributo, o FlagsAttribute primeiro não. O exemplo mostra

a diferença no comportamento quando um intervalo de inteiros, incluindo inteiros que


não representam valores subjacentes do tipo de enumeração, são convertidos para o
tipo de enumeração e suas representações de cadeia de caracteres exibidas. Por
exemplo, observe que 3 não pode ser representado como um valor porque 3 não é o
valor subjacente de nenhum SingleHue membro, enquanto o FlagsAttribute atributo
torna possível representar 3 como um SingleHue MultiHue valor de Black, Red .

O exemplo a seguir define outra enumeração com o atributo e mostra como usar
operadores lógicos e de igualdade bit a FlagsAttribute bit para determinar se um ou
mais campos de bit são definidos em um valor de enumeração. Você também pode usar
o Enum.HasFlag método para fazer isso, mas isso não é mostrado neste exemplo.

C#

using System;

[Flags]
public enum PhoneService
{
None = 0,
LandLine = 1,
Cell = 2,
Fax = 4,
Internet = 8,
Other = 16
}

public class Example1


{
public static void Main()
{
// Define three variables representing the types of phone service
// in three households.
var household1 = PhoneService.LandLine | PhoneService.Cell |
PhoneService.Internet;
var household2 = PhoneService.None;
var household3 = PhoneService.Cell | PhoneService.Internet;

// Store the variables in an array for ease of access.


PhoneService[] households = { household1, household2, household3 };

// Which households have no service?


for (int ctr = 0; ctr < households.Length; ctr++)
Console.WriteLine("Household {0} has phone service: {1}",
ctr + 1,
households[ctr] == PhoneService.None ?
"No" : "Yes");
Console.WriteLine();

// Which households have cell phone service?


for (int ctr = 0; ctr < households.Length; ctr++)
Console.WriteLine("Household {0} has cell phone service: {1}",
ctr + 1,
(households[ctr] & PhoneService.Cell) ==
PhoneService.Cell ?
"Yes" : "No");
Console.WriteLine();

// Which households have cell phones and land lines?


var cellAndLand = PhoneService.Cell | PhoneService.LandLine;
for (int ctr = 0; ctr < households.Length; ctr++)
Console.WriteLine("Household {0} has cell and land line service:
{1}",
ctr + 1,
(households[ctr] & cellAndLand) == cellAndLand
?
"Yes" : "No");
Console.WriteLine();

// List all types of service of each household?//


for (int ctr = 0; ctr < households.Length; ctr++)
Console.WriteLine("Household {0} has: {1:G}",
ctr + 1, households[ctr]);
Console.WriteLine();
}
}
// The example displays the following output:
// Household 1 has phone service: Yes
// Household 2 has phone service: No
// Household 3 has phone service: Yes
//
// Household 1 has cell phone service: Yes
// Household 2 has cell phone service: No
// Household 3 has cell phone service: Yes
//
// Household 1 has cell and land line service: Yes
// Household 2 has cell and land line service: No
// Household 3 has cell and land line service: No
//
// Household 1 has: LandLine, Cell, Internet
// Household 2 has: None
// Household 3 has: Cell, Internet

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Manipular e gerar eventos
Artigo • 27/01/2024

Os eventos no .NET são baseados no modelo de representante. O modelo de


representante segue o padrão de design do observador, que permite a um assinante se
registrar em um provedor e receber notificações dele. Um remetente de eventos envia
uma notificação por push de que um evento ocorreu e um receptor de eventos recebe
essa notificação e define uma resposta. Este artigo descreve os principais componentes
do modelo de representante, como consumir eventos em aplicativos e como
implementar eventos no código.

Eventos
Um evento é uma mensagem enviada por um objeto para sinalizar a ocorrência de uma
ação. A ação pode ser causada pela interação do usuário, como o clique em um botão,
ou ser resultado de alguma outra lógica de programa, como a alteração do valor de
uma propriedade. O objeto que aciona o evento é chamado de remetente do evento. O
remetente do evento não sabe qual objeto ou método receberá (identificador) os
eventos que ele aciona. O evento normalmente é membro do remetente do evento. Por
exemplo, o evento Click é membro da classe Button e o evento PropertyChanged é
membro da classe que implementa a interface INotifyPropertyChanged.

Para definir um evento, use a palavra-chave event no C# ou Event no Visual Basic na


assinatura da sua classe de evento e especifique o tipo de representante para o evento.
Os representantes são descritos na próxima seção.

Normalmente, para acionar um evento, você adiciona um método que é marcado como
protected e virtual (em C#) ou Protected e Overridable (no Visual Basic). Dê a esse

método o nome On EventName; por exemplo, OnDataReceived . O método deve usar um


parâmetro que especifica um objeto de dados de evento, que é um objeto do tipo
EventArgs ou um tipo derivado. Você fornece esse método para permitir que as classes
derivadas substituam a lógica para acionamento do evento. Uma classe derivada
sempre deve chamar o método On EventName da classe base a fim de garantir que os
representantes registrados recebam o evento.

O exemplo de código a seguir mostra como declarar um evento denominado


ThresholdReached . O evento está associado ao representante EventHandler e é gerado

em um método chamado OnThresholdReached .

C#
class Counter
{
public event EventHandler ThresholdReached;

protected virtual void OnThresholdReached(EventArgs e)


{
EventHandler handler = ThresholdReached;
handler?.Invoke(this, e);
}

// provide remaining implementation for the class


}

Delegados
Um representante é um tipo que contém uma referência a um método. Um
representante é declarado com uma assinatura que mostra o tipo de retorno e os
parâmetros para os métodos aos quais faz referência, e pode conter referências apenas
aos métodos que correspondem à sua assinatura. Portanto, um representante é
equivalente a um ponteiro de função fortemente tipado ou um retorno de chamada.
Uma declaração de representante é suficiente para definir uma classe de representante.

Representantes têm muitos usos no .NET. No contexto de eventos, um representante é


um intermediário (ou mecanismo do tipo ponteiro) entre a origem do evento e o código
que manipula o evento. Associe um representante a um evento incluindo o tipo de
representante na declaração do evento, como mostrado no exemplo da seção anterior.
Para obter mais informações sobre representantes, consulte a classe Delegate.

O .NET fornece os representantes EventHandler e EventHandler<TEventArgs> para dar


suporte à maioria dos cenários de evento. Use o representante EventHandler para todos
os eventos que não incluem dados de evento. Use o representante
EventHandler<TEventArgs> para eventos que incluem dados sobre o evento. Esses
representantes não têm valor de tipo de retorno e usam dois parâmetros (um objeto
para a origem do evento e um objeto para dados do evento).

Os representantes são multicast, o que significa que eles podem manter referências a
mais de um método de manipulação de eventos. Para obter detalhes, consulte a página
de referência Delegate. Os representantes proporcionam flexibilidade e controle
refinado na manipulação de eventos. Um representante atua como um dispatcher de
evento para a classe que aciona o evento ao manter uma lista de manipuladores de
eventos registrados para o evento.
Para cenários em que os representantes EventHandler e EventHandler<TEventArgs> não
funcionam, você pode definir um representante. Os cenários que exigem que você
defina um representante são muito raros; por exemplo, quando você deve trabalhar
com código que não reconhece genéricos. Marque um representante com a palavra-
chave delegate no C# e Delegate no Visual Basic na declaração. O exemplo a seguir
mostra como declarar um representante chamado ThresholdReachedEventHandler .

C#

public delegate void ThresholdReachedEventHandler(object sender,


ThresholdReachedEventArgs e);

Dados de evento
Os dados associados a um evento podem ser fornecidos por meio de uma classe de
dados do evento. O .NET fornece muitas classes de dados de evento que podem ser
usadas em seus aplicativos. Por exemplo, a classe SerialDataReceivedEventArgs é a
classe de dados de evento do evento SerialPort.DataReceived. O .NET segue um padrão
de nomenclatura de terminação para todas as classes de dados de evento com
EventArgs . Determine qual classe de dados de evento está associada a um evento

observando o representante do evento. Por exemplo, o representante


SerialDataReceivedEventHandler inclui a classe SerialDataReceivedEventArgs como um
de seus parâmetros.

A classe EventArgs é o tipo base para todas as classes de dados de evento. EventArgs
também é a classe usada quando um evento não tem nenhum dado associado. Quando
você criar um evento cuja finalidade seja apenas notificar outras classes de que algo
aconteceu e que não precise passar nenhum dado, inclua a classe EventArgs como o
segundo parâmetro no representante. Você poderá passar o valor EventArgs.Empty
quando nenhum dado for fornecido. O representante EventHandler inclui a classe
EventArgs como um parâmetro.

Quando quiser criar uma classe de dados de evento personalizada, crie uma classe
derivada de EventArgs e forneça todos os membros necessários para passar dados que
estejam relacionados ao evento. Normalmente, você deve usar o mesmo padrão de
nomenclatura do .NET e terminar o nome da classe de dados de evento com EventArgs .

O exemplo a seguir mostra uma classe de dados de evento chamada


ThresholdReachedEventArgs . Ele contém propriedades que são específicas ao evento que

está sendo acionado.


C#

public class ThresholdReachedEventArgs : EventArgs


{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}

Manipuladores de eventos
Para responder a um evento, você pode definir um método de manipulador de eventos
no receptor do evento. Esse método deve corresponder à assinatura do representante
para o evento que está sendo manipulado. No manipulador de eventos, execute as
ações que são necessárias quando o evento é acionado, como coletar a entrada do
usuário depois que ele clica em um botão. Para receber notificações de ocorrência de
eventos, o método de manipulador de eventos deve estar inscrito no evento.

O exemplo a seguir mostra um método de manipulador de eventos chamado


c_ThresholdReached que corresponde à assinatura para o representante EventHandler. O

método está inscrito no evento ThresholdReached .

C#

class Program
{
static void Main()
{
var c = new Counter();
c.ThresholdReached += c_ThresholdReached;

// provide remaining implementation for the class


}

static void c_ThresholdReached(object sender, EventArgs e)


{
Console.WriteLine("The threshold was reached.");
}
}

Manipuladores de eventos estáticos e


dinâmicos
O .NET permite que os assinantes se registrem para receber notificações de eventos de
modo estático ou dinâmico. Os manipuladores de eventos estáticos permanecem em
vigor por toda a vida da classe cujos eventos eles manipulam. Os manipuladores de
eventos dinâmicos são ativados e desativados explicitamente durante a execução do
programa, geralmente em resposta a alguma lógica de programa condicional. Por
exemplo, eles podem ser usados se as notificações de eventos forem necessárias apenas
sob determinadas condições, ou se um aplicativo fornecer vários manipuladores de
eventos e as condições de tempo de execução definirem o apropriado para uso. O
exemplo na seção anterior mostra como adicionar dinamicamente um manipulador de
eventos. Para obter mais informações, veja Eventos (no Visual Basic) e Eventos (em C#).

Acionando vários eventos


Se sua classe acionar vários eventos, o compilador vai gerar um campo por instância de
representante de evento. Se o número de eventos for grande, o custo de
armazenamento de um campo por representante pode não ser aceitável. Para esses
casos, o .NET fornece propriedades de evento que você pode usar com outra estrutura
de dados de sua escolha para armazenar representantes de eventos.

As propriedades de evento consistem em declarações de evento acompanhadas por


acessadores de evento. Os acessadores de evento são métodos que você define para
adicionar ou remover instâncias de representante de evento da estrutura de dados de
armazenamento. Observe que as propriedades de evento são mais lentas que os
campos de evento, pois cada representante de evento deve ser recuperado para que
possa ser invocado. A compensação está entre a memória e a velocidade. Se sua classe
define muitos eventos que raramente são acionados, você desejará implementar as
propriedades de evento. Para saber mais, confira Como manipular vários eventos
usando propriedades de evento.

Artigos relacionados
ノ Expandir a tabela

Título Descrição

Como acionar e consumir eventos Contém exemplos de como acionar e consumir eventos.

Como manipular vários eventos Mostrar como usar propriedades de evento para manipular
usando propriedades de evento vários eventos.

Padrão de design do observador Descreve o padrão de design que permite a um assinante


se registrar em um provedor e receber notificações dele.
Confira também
EventHandler
EventHandler<TEventArgs>
EventArgs
Delegate
Eventos (Visual Basic)
Eventos (Guia de Programação em C#)
Visão geral de eventos e eventos roteados (aplicativos UWP)
Eventos em aplicativos da Windows Store 8.x

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Como acionar e consumir eventos
Artigo • 10/05/2023

Os exemplos neste artigo mostram como trabalhar com eventos. Elas incluem exemplos
do representante EventHandler, o representante EventHandler<TEventArgs> e um
representante personalizado, para ilustrar eventos com e sem dados.

Os exemplos usam conceitos descritos no artigo Eventos.

Exemplo 1
O primeiro exemplo mostra como gerar e consumir um evento que não tem dados. Ele
contém o nome de classe Counter que tem um evento chamado ThresholdReached . Esse
evento é gerado quando um valor de contador é igual ou maior que um valor de limite.
O representante EventHandler é associado ao evento, porque nenhum dado de evento
é fornecido.

C#

using System;

namespace ConsoleApplication1
{
class ProgramOne
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;

Console.WriteLine("press 'a' key to increase total");


while (Console.ReadKey(true).KeyChar == 'a')
{
Console.WriteLine("adding one");
c.Add(1);
}
}

static void c_ThresholdReached(object sender, EventArgs e)


{
Console.WriteLine("The threshold was reached.");
Environment.Exit(0);
}
}

class Counter
{
private int threshold;
private int total;

public Counter(int passedThreshold)


{
threshold = passedThreshold;
}

public void Add(int x)


{
total += x;
if (total >= threshold)
{
ThresholdReached?.Invoke(this, EventArgs.Empty);
}
}

public event EventHandler ThresholdReached;


}
}

Exemplo 2
O segundo exemplo mostra como gerar e consumir um evento que fornece dados. O
representante EventHandler<TEventArgs> é associado ao evento e uma instância de um
objeto de dados de evento personalizado é fornecida.

C#

using System;

namespace ConsoleApplication3
{
class ProgramThree
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;

Console.WriteLine("press 'a' key to increase total");


while (Console.ReadKey(true).KeyChar == 'a')
{
Console.WriteLine("adding one");
c.Add(1);
}
}

static void c_ThresholdReached(object sender,


ThresholdReachedEventArgs e)
{
Console.WriteLine("The threshold of {0} was reached at {1}.",
e.Threshold, e.TimeReached);
Environment.Exit(0);
}
}

class Counter
{
private int threshold;
private int total;

public Counter(int passedThreshold)


{
threshold = passedThreshold;
}

public void Add(int x)


{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new
ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}

protected virtual void OnThresholdReached(ThresholdReachedEventArgs


e)
{
EventHandler<ThresholdReachedEventArgs> handler =
ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}

public event EventHandler<ThresholdReachedEventArgs>


ThresholdReached;
}

public class ThresholdReachedEventArgs : EventArgs


{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}
}

Exemplo 3
O terceiro exemplo mostra como declarar um representante para um evento. O
representante é chamado ThresholdReachedEventHandler . Este exemplo é apenas uma
ilustração. Normalmente, você não precisa declarar um representante para um evento,
porque pode usar o representante EventHandler ou EventHandler<TEventArgs>. Você
deve declarar um representante somente em cenários raros, como tornar a classe
disponível para o código herdado que não pode usar genéricos.

C#

using System;

namespace ConsoleApplication4
{
class ProgramFour
{
static void Main(string[] args)
{
Counter c = new Counter(new Random().Next(10));
c.ThresholdReached += c_ThresholdReached;

Console.WriteLine("press 'a' key to increase total");


while (Console.ReadKey(true).KeyChar == 'a')
{
Console.WriteLine("adding one");
c.Add(1);
}
}

static void c_ThresholdReached(Object sender,


ThresholdReachedEventArgs e)
{
Console.WriteLine("The threshold of {0} was reached at {1}.",
e.Threshold, e.TimeReached);
Environment.Exit(0);
}
}

class Counter
{
private int threshold;
private int total;

public Counter(int passedThreshold)


{
threshold = passedThreshold;
}

public void Add(int x)


{
total += x;
if (total >= threshold)
{
ThresholdReachedEventArgs args = new
ThresholdReachedEventArgs();
args.Threshold = threshold;
args.TimeReached = DateTime.Now;
OnThresholdReached(args);
}
}

protected virtual void OnThresholdReached(ThresholdReachedEventArgs


e)
{
ThresholdReachedEventHandler handler = ThresholdReached;
if (handler != null)
{
handler(this, e);
}
}

public event ThresholdReachedEventHandler ThresholdReached;


}

public class ThresholdReachedEventArgs : EventArgs


{
public int Threshold { get; set; }
public DateTime TimeReached { get; set; }
}

public delegate void ThresholdReachedEventHandler(Object sender,


ThresholdReachedEventArgs e);
}

Confira também
Eventos
Como manipular vários eventos usando
propriedades de evento
Artigo • 07/04/2023

Para usar as propriedades de evento, defina as propriedades de evento na classe que


gera os eventos e, em seguida, defina os representantes das propriedades de evento
nas classes que tratam dos eventos. Para implementar várias propriedades de evento
em uma classe, a classe deve armazenar e manter internamente o delegado definido
para cada evento. Para cada evento semelhante a um campo, um tipo de referência de
suporte de campo correspondente é gerado. Isso pode resultar em alocações
desnecessárias quando o número de eventos aumentar. Como alternativa, uma
abordagem comum é manter um EventHandlerList que armazena eventos por chave.

Para armazenar os representantes para cada evento, você pode usar a classe
EventHandlerList ou implementar sua própria coleção. A classe da coleção deve fornecer
métodos para configurar, acessar e recuperar o representante do manipulador de
eventos com base na chave de evento. Por exemplo, você poderia usar uma classe
Hashtable ou derivar uma classe personalizada da classe DictionaryBase. Os detalhes da
implementação da coleção de representantes não precisam ser expostos fora de sua
classe.

Cada propriedade de evento dentro da classe define um método adicionar acessador e


um método remover acessador. O método adicionar acessador de uma propriedade de
eventos adiciona a instância do representante de entrada à coleção de representantes.
O acessador de remoção de uma propriedade de evento remove a instância do
representante de entrada da coleção de representantes. Os acessadores de
propriedades de evento usam a chave predefinida na propriedade de evento para
adicionar e remover instâncias da coleção de representantes.

Para manipular vários eventos usando propriedades de


evento
1. Defina a coleção de representantes na classe que gera os eventos.

2. Defina uma chave para cada evento.

3. Defina as propriedades de evento na classe que gera os eventos.

4. Use a coleção de representantes para implementar os métodos adicionar e


remover acessador nas propriedades de evento.
5. Use as propriedades de evento públicas para adicionar e remover representantes
do manipulador de eventos nas classes que tratam dos eventos.

Exemplo
O exemplo de C# a seguir implementa as propriedades de evento MouseDown e MouseUp
usando uma EventHandlerList para armazenar o representante de cada evento. As
palavras-chave dos constructos de propriedade de evento estão em negrito.

C#

// The class SampleControl defines two event properties, MouseUp and


MouseDown.
class SampleControl : Component
{
// :
// Define other control methods and properties.
// :

// Define the delegate collection.


protected EventHandlerList listEventDelegates = new EventHandlerList();

// Define a unique key for each event.


static readonly object mouseDownEventKey = new object();
static readonly object mouseUpEventKey = new object();

// Define the MouseDown event property.


public event MouseEventHandler MouseDown
{
// Add the input delegate to the collection.
add
{
listEventDelegates.AddHandler(mouseDownEventKey, value);
}
// Remove the input delegate from the collection.
remove
{
listEventDelegates.RemoveHandler(mouseDownEventKey, value);
}
}

// Raise the event with the delegate specified by mouseDownEventKey


private void OnMouseDown(MouseEventArgs e)
{
MouseEventHandler mouseEventDelegate =
(MouseEventHandler)listEventDelegates[mouseDownEventKey];
mouseEventDelegate(this, e);
}

// Define the MouseUp event property.


public event MouseEventHandler MouseUp
{
// Add the input delegate to the collection.
add
{
listEventDelegates.AddHandler(mouseUpEventKey, value);
}
// Remove the input delegate from the collection.
remove
{
listEventDelegates.RemoveHandler(mouseUpEventKey, value);
}
}

// Raise the event with the delegate specified by mouseUpEventKey


private void OnMouseUp(MouseEventArgs e)
{
MouseEventHandler mouseEventDelegate =
(MouseEventHandler)listEventDelegates[mouseUpEventKey];
mouseEventDelegate(this, e);
}
}

Confira também
System.ComponentModel.EventHandlerList
Eventos
Control.Events
Como declarar eventos personalizados para conservar memória
Padrão de design do observador
Artigo • 29/05/2023

O padrão de design do observador permite a um assinante se registrar em um provedor


e receber notificações dele. Ele é adequado para qualquer cenário que requer a
notificação baseada em push. O padrão define um provedor (também conhecido como
assunto ou observável) e zero, um ou mais observadores. Observadores registram-se no
provedor e, sempre que uma condição, evento ou alteração de estado predefinido
ocorrer, o provedor notificará automaticamente todos os observadores invocando um
representante. Nessa chamada de método, o provedor também pode fornecer
informações sobre o estado atual para observadores. No .NET, o padrão de design do
observador é aplicado ao implementar as interfaces genéricas System.IObservable<T> e
System.IObserver<T>. O parâmetro de tipo genérico representa o tipo que fornece
informações de notificação.

Quando aplicar o padrão


O padrão de design do observador é adequado para notificações por push distribuídas,
pois oferece suporte para uma separação clara entre dois componentes diferentes ou
camadas de aplicativo, como uma camada de fonte de dados (lógica de negócios) uma
camada de interface do usuário (exibição). O padrão pode ser implementado sempre
que um provedor usa retornos de chamada para fornecer informações atuais a seus
clientes.

A implementação do padrão exige que você forneça os seguintes detalhes:

Um provedor ou o assunto, que é o objeto que envia notificações para


observadores. Um provedor é uma classe ou estrutura que implementa a interface
IObservable<T>. O provedor deve implementar um único método,
IObservable<T>.Subscribe, que é chamado pelos observadores que desejam
receber notificações do provedor.

Um observador, que é um objeto que recebe notificações de um provedor. Um


observador é uma classe ou estrutura que implementa a interface IObserver<T>. O
observador deve implementar três métodos, que são chamados pelo provedor:
IObserver<T>.OnNext, que fornece ao observador informações novas ou atuais.
IObserver<T>.OnError, que informa o observador que ocorreu um erro.
IObserver<T>.OnCompleted, que indica que o provedor terminou de enviar
notificações.
Um mecanismo que permite que o provedor mantenha controle dos observadores.
Normalmente, o provedor usa um objeto de contêiner, como um objeto
System.Collections.Generic.List<T>, para manter as referências às implementações
de IObserver<T> que assinaram notificações. Usar um contêiner de
armazenamento para essa finalidade permite que o provedor lidar com um
número ilimitado de observadores. A ordem na qual os observadores recebem
notificações não está definida. O provedor está livre para usar qualquer método
para determinar a ordem.

Uma implementação de IDisposable que permite que o provedor remova os


observadores quando a notificação for concluída. Observadores recebem uma
referência para a implementação de IDisposable do método Subscribe, portanto,
eles também podem chamar o método IDisposable.Dispose para cancelar a
assinatura antes que o provedor tenha terminado de enviar notificações.

Um objeto que contém os dados que o provedor envia para seus observadores. O
tipo desse objeto corresponde ao parâmetro de tipo genérico das interfaces
IObservable<T> e IObserver<T>. Embora esse objeto possa ser o mesmo que a
implementação de IObservable<T>, geralmente ele é um tipo separado.

7 Observação

Além de implementar o padrão de design do observador, pode ser interessante


explorar bibliotecas que são criadas usando as interfaces IObservable<T> e
IObserver<T>. Por exemplo, Extensões Reativas para .NET (Rx) consistem em um
conjunto de métodos de extensão e operadores de sequência padrão LINQ para
oferecer suporte à programação assíncrona.

Implementar o padrão
O exemplo a seguir usa o padrão de design do observador para implementar um
sistema de informações de coleta de bagagem de aeroporto. Uma classe BaggageInfo
fornece informações sobre voos que chegam e as esteiras onde as bagagens de cada
voo estão disponíveis para retirada. Isso é mostrado no exemplo a seguir.

C#

namespace Observables.Example;

public readonly record struct BaggageInfo(


int FlightNumber,
string From,
int Carousel);

Uma classe BaggageHandler é responsável por receber informações sobre os voos que
chegam e as esteiras de coleta de bagagem. Internamente, ela mantém duas coleções:

_observers : uma coleção de clientes que observam informações atualizadas.

_flights : uma coleção de voos e as respectivas esteiras.

O código-fonte para a classe BaggageHandler é mostrado no exemplo a seguir.

C#

namespace Observables.Example;

public sealed class BaggageHandler : IObservable<BaggageInfo>


{
private readonly HashSet<IObserver<BaggageInfo>> _observers = new();
private readonly HashSet<BaggageInfo> _flights = new();

public IDisposable Subscribe(IObserver<BaggageInfo> observer)


{
// Check whether observer is already registered. If not, add it.
if (_observers.Add(observer))
{
// Provide observer with existing data.
foreach (BaggageInfo item in _flights)
{
observer.OnNext(item);
}
}

return new Unsubscriber<BaggageInfo>(_observers, observer);


}

// Called to indicate all baggage is now unloaded.


public void BaggageStatus(int flightNumber) =>
BaggageStatus(flightNumber, string.Empty, 0);

public void BaggageStatus(int flightNumber, string from, int carousel)


{
var info = new BaggageInfo(flightNumber, from, carousel);

// Carousel is assigned, so add new info object to list.


if (carousel > 0 && _flights.Add(info))
{
foreach (IObserver<BaggageInfo> observer in _observers)
{
observer.OnNext(info);
}
}
else if (carousel is 0)
{
// Baggage claim for flight is done.
if (_flights.RemoveWhere(
flight => flight.FlightNumber == info.FlightNumber) > 0)
{
foreach (IObserver<BaggageInfo> observer in _observers)
{
observer.OnNext(info);
}
}
}
}

public void LastBaggageClaimed()


{
foreach (IObserver<BaggageInfo> observer in _observers)
{
observer.OnCompleted();
}

_observers.Clear();
}
}

Clientes que desejem receber informações atualizadas devem chamar o método


BaggageHandler.Subscribe . Se o cliente não tiver assinado as notificações anteriormente,

uma referência à implementação do cliente IObserver<T> será adicionada à coleção


_observers .

O método BaggageHandler.BaggageStatus sobrecarregado pode ser chamado para


indicar que bagagem de um voo está sendo descarregada ou que não está mais sendo
descarregada. No primeiro caso, o método recebe um número de voo, o aeroporto que
originou o voo e a esteira onde bagagem está sendo descarregada. No segundo caso, o
método recebe apenas um número de voo. Para a bagagem que está sendo
descarregada, o método verifica se as informações de BaggageInfo passadas para o
método existem na coleção _flights . Se não existirem, o método adicionará as
informações e chamará o método OnNext de cada observador. Para voos cuja bagagem
não está mais sendo descarregada, o método verificará se as informações sobre esse
voo estão armazenadas na coleção _flights . Se estiverem, o método chamará o
método OnNext de cada observador e removerá o objeto BaggageInfo da coleção
_flights .

Quando o último voo do dia tiver aterrizado e sua bagagem tiver sido processada, o
método BaggageHandler.LastBaggageClaimed será chamado. Esse método chama o
método OnCompleted de cada observador para indicar que todas as notificações foram
finalizadas e, em seguida, limpa a coleção _observers .
O método Subscribe do provedor retorna uma implementação de IDisposable que
permite que os observadores interrompam o recebimento de notificações antes que o
método OnCompleted seja chamado. O código-fonte para essa classe Unsubscriber(Of
BaggageInfo) é mostrado no exemplo a seguir. Quando a classe é instanciada no
método BaggageHandler.Subscribe , ela é passada como referência à coleção _observers
e uma referência ao observador adicionado à coleção. Essas referências são atribuídas a
variáveis locais. Quando o método Dispose do objeto é chamado, ele verifica se o
observador ainda existe na coleção _observers e, em caso afirmativo, remove o
observador.

C#

namespace Observables.Example;

internal sealed class Unsubscriber<BaggageInfo> : IDisposable


{
private readonly ISet<IObserver<BaggageInfo>> _observers;
private readonly IObserver<BaggageInfo> _observer;

internal Unsubscriber(
ISet<IObserver<BaggageInfo>> observers,
IObserver<BaggageInfo> observer) => (_observers, _observer) =
(observers, observer);

public void Dispose() => _observers.Remove(_observer);


}

O exemplo a seguir fornece uma implementação de IObserver<T> denominada


ArrivalsMonitor , que é uma classe base que exibe informações de coleta de bagagem.
As informações são exibidas em ordem alfabética, pelo nome da cidade de origem. Os
métodos de ArrivalsMonitor são marcados como overridable (no Visual Basic) ou
virtual (em C#). Portanto, podem ser substituídos em uma classe derivada.

C#

namespace Observables.Example;

public class ArrivalsMonitor : IObserver<BaggageInfo>


{
private readonly string _name;
private readonly List<string> _flights = new();
private readonly string _format = "{0,-20} {1,5} {2, 3}";
private IDisposable? _cancellation;

public ArrivalsMonitor(string name)


{
ArgumentException.ThrowIfNullOrEmpty(name);
_name = name;
}

public virtual void Subscribe(BaggageHandler provider) =>


_cancellation = provider.Subscribe(this);

public virtual void Unsubscribe()


{
_cancellation?.Dispose();
_flights.Clear();
}

public virtual void OnCompleted() => _flights.Clear();

// No implementation needed: Method is not called by the BaggageHandler


class.
public virtual void OnError(Exception e)
{
// No implementation.
}

// Update information.
public virtual void OnNext(BaggageInfo info)
{
bool updated = false;

// Flight has unloaded its baggage; remove from the monitor.


if (info.Carousel is 0)
{
string flightNumber = string.Format("{0,5}", info.FlightNumber);
for (int index = _flights.Count - 1; index >= 0; index--)
{
string flightInfo = _flights[index];
if (flightInfo.Substring(21, 5).Equals(flightNumber))
{
updated = true;
_flights.RemoveAt(index);
}
}
}
else
{
// Add flight if it doesn't exist in the collection.
string flightInfo = string.Format(_format, info.From,
info.FlightNumber, info.Carousel);
if (_flights.Contains(flightInfo) is false)
{
_flights.Add(flightInfo);
updated = true;
}
}

if (updated)
{
_flights.Sort();
Console.WriteLine($"Arrivals information from {_name}");
foreach (string flightInfo in _flights)
{
Console.WriteLine(flightInfo);
}

Console.WriteLine();
}
}
}

A classe ArrivalsMonitor inclui os métodos Subscribe e Unsubscribe . O método


Subscribe permite que a classe salve a implementação de IDisposable retornada pela

chamada para Subscribe para uma variável particular. O método Unsubscribe permite
que a classe cancele a assinatura de notificações ao chamar a implementação de
Dispose do provedor. ArrivalsMonitor também fornece implementações dos métodos
OnNext, OnError, e OnCompleted. Somente a implementação de OnNext contém uma
quantidade significativa de código. O método funciona com um objeto List<T>
particular, classificado e genérico que mantém informações sobre os aeroportos de
origem dos voos que chegam e sobre as esteiras nas quais as respectivas bagagens
estarão disponíveis. Se a classe BaggageHandler relata um novo voo chegando, a
implementação do método OnNext adiciona informações sobre esse voo à lista. Se a
classe BaggageHandler relata que a bagagem do voo foi descarregada, o método
OnNext remove essa voo da lista. Sempre que uma alteração é feita, a lista é classificada
e exibida no console.

O exemplo a seguir contém o ponto de entrada do aplicativo que instancia a classe


BaggageHandler e duas instâncias da classe ArrivalsMonitor e usa o método
BaggageHandler.BaggageStatus para adicionar e remover informações sobre voos que

chegam. Em cada caso, os observadores recebem atualizações e exibem corretamente


as informações de coleta de bagagem.

C#

using Observables.Example;

BaggageHandler provider = new();


ArrivalsMonitor observer1 = new("BaggageClaimMonitor1");
ArrivalsMonitor observer2 = new("SecurityExit");

provider.BaggageStatus(712, "Detroit", 3);


observer1.Subscribe(provider);

provider.BaggageStatus(712, "Kalamazoo", 3);


provider.BaggageStatus(400, "New York-Kennedy", 1);
provider.BaggageStatus(712, "Detroit", 3);
observer2.Subscribe(provider);

provider.BaggageStatus(511, "San Francisco", 2);


provider.BaggageStatus(712);
observer2.Unsubscribe();

provider.BaggageStatus(400);
provider.LastBaggageClaimed();

// Sample output:
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
//
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
// Kalamazoo 712 3
//
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
//
// Arrivals information from SecurityExit
// Detroit 712 3
//
// Arrivals information from SecurityExit
// Detroit 712 3
// Kalamazoo 712 3
//
// Arrivals information from SecurityExit
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
//
// Arrivals information from BaggageClaimMonitor1
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from SecurityExit
// Detroit 712 3
// Kalamazoo 712 3
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from BaggageClaimMonitor1
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from SecurityExit
// New York-Kennedy 400 1
// San Francisco 511 2
//
// Arrivals information from BaggageClaimMonitor1
// San Francisco 511 2

Artigos relacionados
Título Descrição

Práticas recomendadas para o São descritas as práticas recomendadas ao desenvolver


padrão de design do aplicativos que implementam o padrão de design do
observador observador.

Como implementar um É fornecida uma implementação passo a passo de um


provedor provedor para uma aplicativo de monitoramento de
temperatura.

Como implementar um É fornecida uma implementação passo a passo de um


observador observador para uma aplicativo de monitoramento de
temperatura.
Práticas recomendadas para o padrão
de design do observador
Artigo • 10/05/2023

No .NET, o padrão de design de observador é implementado como um conjunto de


interfaces. A interface System.IObservable<T> representa o provedor de dados, que
também é responsável por fornecer uma implementação IDisposable que permite que
os observadores cancelem a assinatura de notificações. A interface
System.IObserver<T> representa o observador. Este tópico descreve as práticas
recomendadas que os desenvolvedores devem seguir ao implementar o padrão de
design de observador usando essas interfaces.

Threading
Normalmente, um provedor implementa o método IObservable<T>.Subscribe
adicionando um observador específico a uma lista de assinantes que é representada por
algum objeto de coleção e implementa o método IDisposable.Dispose removendo um
determinado observador da lista de assinantes. Um observador pode chamar esses
métodos a qualquer momento. Além disso, como o contrato de provedor/observador
não especifica quem é responsável por cancelar a assinatura após o método de retorno
de chamada IObserver<T>.OnCompleted, o provedor e o observador podem tentar
ambos remover o mesmo membro da lista. Devido a essa possibilidade, tanto o método
Subscribe quanto o método Dispose devem ser thread-safe. Normalmente, isso envolve
o uso de uma coleção simultânea ou um bloqueio. As implementações que não são
thread-safe devem documentar explicitamente esse fato.

Quaisquer garantias adicionais devem ser especificadas em uma camada no início do


contrato de provedor/observador. Os implementadores devem chamar claramente ao
imporem requisitos adicionais para evitar confusão do usuário sobre o contrato do
observador.

Tratando exceções
Devido ao fraco acoplamento entre um provedor de dados e um observador, as
exceções no padrão de design do observador devem ser informativas. Isso afeta como
os provedores e observadores manipulam exceções no padrão de design do
observador.
O provedor – Chamando o método OnError
O método OnError serve como uma mensagem informativa para observadores, da
mesma forma que o método IObserver<T>.OnNext. No entanto, o método OnNext foi
projetado para fornecer a um observador dados atuais ou atualizados, enquanto o
método OnError foi projetado para indicar que o provedor não é capaz de fornecer
dados válidos.

O provedor deve seguir essas práticas recomendadas ao manipular exceções e chamar o


método OnError:

O provedor deverá manipular suas próprias exceções se houver algum requisito


específico.

O provedor não deve esperar ou exigir que os observadores manipulem exceções


de alguma maneira específica.

O provedor deve chamar o método OnError ao manipular uma exceção que


comprometa sua capacidade de fornecer atualizações. Informações sobre essas
exceções podem ser passadas para o observador. Em outros casos, não há
necessidade de notificar os observadores com relação a uma exceção.

Uma vez que o provedor chame o método OnError ou IObserver<T>.OnCompleted, não


deverá haver nenhuma notificação adicional e o provedor poderá cancelar a assinatura
de seus observadores. No entanto, os observadores podem também cancelar eles
próprios sua assinatura a qualquer momento, inclusive antes e depois de receberem
uma notificação OnError ou IObserver<T>.OnCompleted. O padrão de design do
observador não determina se o provedor ou o observador é responsável pelo
cancelamento da assinatura; portanto, é possível que ambos tentem cancelar a
assinatura. Normalmente, quando os observadores cancelam a assinatura, eles são
removidos de uma coleção de assinantes. Em um aplicativo de thread único, a
implementação IDisposable.Dispose deve garantir que uma referência de objeto seja
válida e que o objeto seja um membro da coleção de assinantes antes de tentar
removê-lo. Em um aplicativo com multithread, deve-se usar um objeto de coleção
thread-safe, tal como um objeto System.Collections.Concurrent.BlockingCollection<T>.

O observador – Implementando o método OnError


Quando um observador recebe uma notificação de erro de um provedor, o observador
deve tratar a exceção como informativa e não deve ser necessária qualquer ação
específica.
O observador deve seguir essas práticas recomendadas ao responder a uma chamada
de método OnError de um provedor:

O observador não deve lançar exceções de suas implementações de interface, tais


como OnNext ou OnError. No entanto, se o observador lançar exceções, ele deverá
esperar que essas exceções fiquem sem tratamento.

Para preservar a pilha de chamadas, um observador que deseja gerar um objeto


Exception, que foi passado para o seu método OnError, deveria encapsular a
exceção antes de lançá-la. Um objeto de exceção padrão deve ser usado para essa
finalidade.

Práticas recomendadas adicionais


A tentativa de cancelar registro no método IObservable<T>.Subscribe pode resultar em
uma referência nula. Portanto, é recomendável que você evite essa prática.

Embora seja possível anexar um observador para vários provedores, o padrão


recomendado é anexar uma IObserver<T> instância a uma única instância
IObservable<T>.

Confira também
Padrão de design do observador
Como implementar um observador
Como implementar um provedor
Como implementar um provedor
Artigo • 07/04/2023

O padrão de design do observador requer uma divisão entre um provedor, que


monitora os dados e envia notificações e um ou mais observadores, que recebem
notificações (retornos de chamada) do provedor. Este tópico discute como criar um
provedor. Um tópico relacionado, Como implementar um observador, descreve como
criar um observador.

Para criar um provedor


1. Defina os dados que o provedor é responsável por enviar para os observadores.
Embora o provedor e os dados que ele envia para observadores possam ser um
tipo único, geralmente são representados por tipos diferentes. Por exemplo, em
um aplicativo de monitoramento de temperatura, a estrutura Temperature define
os dados que o provedor (que é representado pela classe TemperatureMonitor
definida na próxima etapa) monitora e quais observadores assinar.

C#

using System;

public struct Temperature


{
private decimal temp;
private DateTime tempDate;

public Temperature(decimal temperature, DateTime dateAndTime)


{
this.temp = temperature;
this.tempDate = dateAndTime;
}

public decimal Degrees


{ get { return this.temp; } }

public DateTime Date


{ get { return this.tempDate; } }
}

2. Defina o provedor de dados, que é um tipo que implementa a interface


System.IObservable<T>. O argumento de tipo genérico do provedor é o tipo que
o provedor envia a observadores. O exemplo a seguir define uma classe
TemperatureMonitor , que é uma implementação construída

System.IObservable<T> com um argumento de tipo genérico de Temperature .

C#

using System;
using System.Collections.Generic;

public class TemperatureMonitor : IObservable<Temperature>


{

3. Determine como o provedor armazena as referências a observadores para que


cada observador possa ser notificado quando apropriado. Normalmente, um
objeto de coleção como um objeto genérico List<T> é usado para essa finalidade.
O exemplo a seguir define um objeto particular List<T> que é instanciado no
constructo de classe TemperatureMonitor .

C#

using System;
using System.Collections.Generic;

public class TemperatureMonitor : IObservable<Temperature>


{
List<IObserver<Temperature>> observers;

public TemperatureMonitor()
{
observers = new List<IObserver<Temperature>>();
}

4. Defina uma implementação IDisposable que o provedor possa retornar para os


assinantes para que eles possam interromper o recebimento de notificações a
qualquer momento. O exemplo a seguir define uma classe Unsubscriber aninhada
para a qual é passada uma referência à coleção de assinantes e ao assinante
quando a classe é instanciada. Esse código permite que o assinante chame a
implementação IDisposable.Dispose do objeto para se remover da coleção de
assinantes.

C#

private class Unsubscriber : IDisposable


{
private List<IObserver<Temperature>> _observers;
private IObserver<Temperature> _observer;

public Unsubscriber(List<IObserver<Temperature>> observers,


IObserver<Temperature> observer)
{
this._observers = observers;
this._observer = observer;
}

public void Dispose()


{
if (! (_observer == null)) _observers.Remove(_observer);
}
}

5. Implementar o método de IObservable<T>.Subscribe . O método recebe uma


referência para a interface System.IObserver<T> e deve ser armazenado no objeto
criado para essa finalidade na etapa 3. O método deve retornar a implementação
IDisposable desenvolvida na etapa 4. O exemplo a seguir mostra a implementação
do método Subscribe na classe TemperatureMonitor .

C#

public IDisposable Subscribe(IObserver<Temperature> observer)


{
if (! observers.Contains(observer))
observers.Add(observer);

return new Unsubscriber(observers, observer);


}

6. Notificar os observadores conforme apropriado, chamando as implementações


IObserver<T>.OnNext, IObserver<T>.OnError e IObserver<T>.OnCompleted. Em
alguns casos, um provedor não pode chamar o método OnError quando ocorre
um erro. Por exemplo, o método GetTemperature a seguir simula um monitor que
lê dados de temperatura a cada cinco segundos e notifica os observadores se a
temperatura foi alterada em pelo menos 0,1 grau desde a leitura anterior. Se o
dispositivo não relatar uma temperatura (ou seja, se o valor for nulo), o provedor
notificará observadores de que a transmissão foi concluída. Observe que, além de
chamar o método OnCompleted de cada observador, o método GetTemperature
limpa a coleção List<T>. Nesse caso, o provedor não torna nenhuma chamada
para o método OnError de seus observadores.

C#

public void GetTemperature()


{
// Create an array of sample data to mimic a temperature device.
Nullable<Decimal>[] temps = {14.6m, 14.65m, 14.7m, 14.9m, 14.9m,
15.2m, 15.25m, 15.2m,
15.4m, 15.45m, null };
// Store the previous temperature, so notification is only sent
after at least .1 change.
Nullable<Decimal> previous = null;
bool start = true;

foreach (var temp in temps) {


System.Threading.Thread.Sleep(2500);
if (temp.HasValue) {
if (start || (Math.Abs(temp.Value - previous.Value) >= 0.1m ))
{
Temperature tempData = new Temperature(temp.Value,
DateTime.Now);
foreach (var observer in observers)
observer.OnNext(tempData);
previous = temp;
if (start) start = false;
}
}
else {
foreach (var observer in observers.ToArray())
if (observer != null) observer.OnCompleted();

observers.Clear();
break;
}
}
}

Exemplo
O exemplo a seguir contém o código-fonte completo para definir uma implementação
IObservable<T> para uma aplicativo de monitoramento de temperatura. Ele inclui a
estrutura Temperature , os dados enviados para observadores e a classe
TemperatureMonitor , que é a implementação IObservable<T>.

C#

using System.Threading;
using System;
using System.Collections.Generic;

public class TemperatureMonitor : IObservable<Temperature>


{
List<IObserver<Temperature>> observers;

public TemperatureMonitor()
{
observers = new List<IObserver<Temperature>>();
}

private class Unsubscriber : IDisposable


{
private List<IObserver<Temperature>> _observers;
private IObserver<Temperature> _observer;

public Unsubscriber(List<IObserver<Temperature>> observers,


IObserver<Temperature> observer)
{
this._observers = observers;
this._observer = observer;
}

public void Dispose()


{
if (! (_observer == null)) _observers.Remove(_observer);
}
}

public IDisposable Subscribe(IObserver<Temperature> observer)


{
if (! observers.Contains(observer))
observers.Add(observer);

return new Unsubscriber(observers, observer);


}

public void GetTemperature()


{
// Create an array of sample data to mimic a temperature device.
Nullable<Decimal>[] temps = {14.6m, 14.65m, 14.7m, 14.9m, 14.9m,
15.2m, 15.25m, 15.2m,
15.4m, 15.45m, null };
// Store the previous temperature, so notification is only sent after
at least .1 change.
Nullable<Decimal> previous = null;
bool start = true;

foreach (var temp in temps) {


System.Threading.Thread.Sleep(2500);
if (temp.HasValue) {
if (start || (Math.Abs(temp.Value - previous.Value) >= 0.1m )) {
Temperature tempData = new Temperature(temp.Value,
DateTime.Now);
foreach (var observer in observers)
observer.OnNext(tempData);
previous = temp;
if (start) start = false;
}
}
else {
foreach (var observer in observers.ToArray())
if (observer != null) observer.OnCompleted();
observers.Clear();
break;
}
}
}
}

Confira também
IObservable<T>
Padrão de design do observador
Como implementar um observador
Práticas recomendadas para o padrão de design do observador
Como implementar um observador
Artigo • 10/05/2023

O padrão de design do observador exige uma divisão entre um observador, que registra
as notificações, e um provedor, que monitora os dados e envia notificações e um ou
mais observadores. Este tópico discute como criar um observador. Um tópico
relacionado, Como implementar um provedor, descreve como criar um provedor.

Para criar um observador


1. Defina o observador, que é um tipo que implementa a interface
System.IObserver<T>. Por exemplo, o código a seguir define um tipo chamado
TemperatureReporter , que é uma implementação System.IObserver<T> construída

com um argumento de tipo genérico de Temperature .

C#

public class TemperatureReporter : IObserver<Temperature>

2. Se o observador puder interromper o recebimento de notificações antes de o


provedor chamar sua implementação de IObserver<T>.OnCompleted, defina uma
variável privada que armazenará a implementação de IDisposable retornada pelo
método IObservable<T>.Subscribe do provedor. Você também deve definir um
método de assinatura que chama o método Subscribe do provedor e armazena o
objeto IDisposable retornado. Por exemplo, o código a seguir define uma variável
privada chamada unsubscriber , e define um método Subscribe que chama o
método Subscribe do provedor e atribui o objeto retornado à variável
unsubscriber .

C#

public class TemperatureReporter : IObserver<Temperature>


{
private IDisposable unsubscriber;
private bool first = true;
private Temperature last;

public virtual void Subscribe(IObservable<Temperature> provider)


{
unsubscriber = provider.Subscribe(this);
}
3. Defina um método que permite ao observador interromper o recebimento de
notificações antes de o provedor chamar sua implementação de
IObserver<T>.OnCompleted, se esse recurso for necessário. O exemplo a seguir
define um método Unsubscribe .

C#

public virtual void Unsubscribe()


{
unsubscriber.Dispose();
}

4. Forneça implementações dos três métodos definidos pela interface IObserver<T>:


IObserver<T>.OnNext, IObserver<T>.OnError e IObserver<T>.OnCompleted.
Dependendo do provedor e das necessidades do aplicativo, os métodos OnError e
OnCompleted podem ser implementações de stub. Observe que o método
OnError não deve tratar do objeto Exception transmitido como uma exceção, e o
método OnCompleted é livre para chamar a implementação IDisposable.Dispose
do provedor. O exemplo a seguir mostra a implementação IObserver<T> da classe
TemperatureReporter .

C#

public virtual void OnCompleted()


{
Console.WriteLine("Additional temperature data will not be
transmitted.");
}

public virtual void OnError(Exception error)


{
// Do nothing.
}

public virtual void OnNext(Temperature value)


{
Console.WriteLine("The temperature is {0}°C at {1:g}",
value.Degrees, value.Date);
if (first)
{
last = value;
first = false;
}
else
{
Console.WriteLine(" Change: {0}° in {1:g}", value.Degrees -
last.Degrees,

value.Date.ToUniversalTime() - last.Date.ToUniversalTime());
}
}

Exemplo
O exemplo a seguir contém o código-fonte completo para a classe
TemperatureReporter , que fornece a implementação IObserver<T> para uma aplicativo

de monitoramento de temperatura.

C#

public class TemperatureReporter : IObserver<Temperature>


{
private IDisposable unsubscriber;
private bool first = true;
private Temperature last;

public virtual void Subscribe(IObservable<Temperature> provider)


{
unsubscriber = provider.Subscribe(this);
}

public virtual void Unsubscribe()


{
unsubscriber.Dispose();
}

public virtual void OnCompleted()


{
Console.WriteLine("Additional temperature data will not be
transmitted.");
}

public virtual void OnError(Exception error)


{
// Do nothing.
}

public virtual void OnNext(Temperature value)


{
Console.WriteLine("The temperature is {0}°C at {1:g}", value.Degrees,
value.Date);
if (first)
{
last = value;
first = false;
}
else
{
Console.WriteLine(" Change: {0}° in {1:g}", value.Degrees -
last.Degrees,
value.Date.ToUniversalTime() - last.Date.ToUniversalTime());
}
}
}

Confira também
IObserver<T>
Padrão de design do observador
Como implementar um provedor
Práticas recomendadas para o padrão de design do observador
Tratando e gerando exceções no .NET
Artigo • 27/01/2024

Aplicativos devem ser capazes de tratar de erros que ocorrem durante a execução de
uma maneira consistente. O .NET fornece um modelo para notificar aplicativos sobre
erros de maneira uniforme: operações do .NET indicam falhas por meio da geração de
exceções.

Exceções
Uma exceção é qualquer condição de erro ou comportamento inesperado encontrado
por um programa em execução. Exceções podem ser geradas devido a uma falha em
seu código ou no código que você chama (como uma biblioteca compartilhada),
recursos do sistema operacional não disponíveis, condições inesperadas encontradas
pelo runtime (como código que não pode ser verificado) e assim por diante. Seu
aplicativo pode se recuperar de algumas dessas condições, mas não de outras. Embora
você possa se recuperar da maioria das exceções de aplicativo, não é possível
recuperar-se da maioria das exceções de runtime.

No .NET, uma exceção é um objeto herdado da classe System.Exception. Uma exceção é


lançada de uma área do código em que ocorreu um problema. A exceção é passada
pilha acima até que o aplicativo trate dela ou o programa seja encerrado.

Métodos de tratamento de exceção vs.


tratamento de erro tradicional
Tradicionalmente, o modelo de tratamento de erro da linguagem confiava na forma
exclusiva da linguagem de detectar erros e localizar manipuladores para eles ou no
mecanismo de tratamento de erro fornecido pelo sistema operacional. A maneira como
o .NET implementa o tratamento de exceção oferece as seguintes vantagens:

O lançamento e tratamento de exceção funciona da mesma maneira para


linguagens de programação .NET.

Não requer nenhuma sintaxe de linguagem específica para tratamento de exceção,


mas permite que cada linguagem defina sua própria sintaxe.

Exceções podem ser geradas pelos limites de processo e até mesmo de


computador.
O código de tratamento de exceção pode ser adicionado a um aplicativo para
aumentar a confiabilidade do programa.

As exceções oferecem vantagens sobre outros métodos de notificação de erro, como


códigos de retorno. Falhas não passam despercebidas porque se uma exceção for
lançada e você não tratar dela, o runtime encerra o aplicativo. Valores inválidos não
continuam a se propagar através do sistema como resultado do código que não
consegue verificar se há um código de retorno de falha.

Exceções comuns
A tabela a seguir lista algumas exceções comuns com exemplos do que pode causá-las.

ノ Expandir a tabela

Tipo de exceção Descrição Exemplo

Exception A classe base para todas Nenhuma (use uma classe


as exceções. derivada dessa exceção).

IndexOutOfRangeException Gerada pelo runtime Indexar uma matriz fora do


somente quando uma intervalo válido:
matriz é indexada arr[arr.Length+1]
incorretamente.

NullReferenceException Gerada pelo runtime object o = null;


somente quando um o.ToString();
objeto nulo é
referenciado.

InvalidOperationException Gerada por métodos Chamar Enumerator.MoveNext()


quando em um estado após a remoção de um item da
inválido. coleção subjacente.

ArgumentException A classe base para todas Nenhuma (use uma classe


as exceções de derivada dessa exceção).
argumento.

ArgumentNullException Gerada por métodos que String s = null;


não permitem que um "Calculate".IndexOf(s);
argumento seja nulo.

ArgumentOutOfRangeException Gerada por métodos que String s = "string";


verificam se os s.Substring(s.Length+1);
argumentos estão em um
determinado intervalo.
Confira também
Classe e propriedades da exceção
Como usar o bloco try-catch para capturar exceções
Como usar exceções específicas em um bloco catch
Como gerar exceções explicitamente
Como criar exceções definidas pelo usuário
Usando manipuladores de exceção filtrados por usuário
Como usar blocos finally
Manipulando exceções de interoperabilidade COM
Práticas recomendadas para exceções
O que todo desenvolvedor precisa saber sobre exceções no runtime

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Classe e propriedades da exceção
Artigo • 07/04/2023

A classe Exception é a classe base da qual as exceções herdam. Por exemplo, a


hierarquia de classe InvalidCastException é como se segue:

Object
Exception
SystemException
InvalidCastException

A classe Exception tem as propriedades a seguir, que ajudam a facilitar o entendimento


de uma exceção.

Nome da Descrição
propriedade

Data Um IDictionary que contém dados arbitrários em pares chave-valor.

HelpLink Pode conter uma URL (ou URN) para um arquivo de ajuda que fornece
informações abrangentes sobre a causa de uma exceção.

InnerException Essa propriedade pode ser usada para criar e manter uma série de exceções
durante o tratamento de exceção. Você pode usá-lo para criar uma nova
exceção contendo exceções previamente capturadas. A exceção original pode
ser capturada pela segunda exceção na propriedade InnerException, permitindo
que o código que trata da segunda exceção examine as informações adicionais.
Por exemplo, suponha que você tem um método que recebe um argumento que
está formatado de modo inadequado. O código tenta ler o argumento, mas uma
exceção é gerada. O método captura a exceção e gera um FormatException.
Para melhorar a capacidade do chamador para determinar o motivo pelo qual
que uma exceção é gerada, às vezes é desejável que um método capture uma
exceção gerada por uma rotina auxiliar e, em seguida, gere uma exceção mais
indicativa do erro que ocorreu. Uma exceção mais nova e mais significativa pode
ser criada, na qual a referência à exceção interna pode ser definida para a
exceção original. Essa exceção mais significativa pode, em seguida, ser gerada
para o chamador. Observe que com essa funcionalidade você pode criar uma
série de exceções vinculadas que termina com a primeira exceção gerada.

Message Fornece detalhes sobre a causa de uma exceção.

Source Obtém ou define o nome do aplicativo ou objeto que causa o erro.

StackTrace Contém um rastreamento de pilha que pode ser usado para determinar onde
um erro ocorreu. O rastreamento de pilha inclui o nome do arquivo de origem e
o número de linha de programa se informações de depuração estiverem
disponíveis.
A maioria das classes que herdam de Exception não implementa membros adicionais
nem fornece funcionalidade adicional; apenas herdam de Exception. Portanto, as
informações mais importantes para uma exceção podem ser encontradas na hierarquia
de classes de exceção, no nome da exceção e nas informações contidas na exceção.

É recomendável gerar e capturar apenas objetos que derivam de Exception, mas é


possível gerar como uma exceção qualquer objeto que derive da classe Object. Observe
que nem todas as linguagens dão suporte à geração e captura de objetos que não
derivam de Exception.

Confira também
Exceções
Como usar o bloco try/catch para
capturar exceções
Artigo • 10/05/2023

Colocar todas as instruções de código que podem elevar ou gerar uma exceção em um
bloco try e posicionar instruções usadas para tratar a exceção ou exceções em um ou
mais blocos catch abaixo do bloco try . Cada bloco catch inclui o tipo de exceção e
pode conter instruções adicionais necessárias para lidar com esse tipo de exceção.

No exemplo a seguir, um StreamReader abre um arquivo chamado data.txt e recupera


uma linha desse arquivo. Uma vez que o código pode gerar qualquer uma de três
exceções, ele é colocado em um bloco try . Três blocos catch capturam as exceções e
lidam com elas, exibindo os resultados no console.

C#

using System;
using System.IO;

public class ProcessFile


{
public static void Main()
{
try
{
using (StreamReader sr = File.OpenText("data.txt"))
{
Console.WriteLine($"The first line of this file is
{sr.ReadLine()}");
}
}
catch (FileNotFoundException e)
{
Console.WriteLine($"The file was not found: '{e}'");
}
catch (DirectoryNotFoundException e)
{
Console.WriteLine($"The directory was not found: '{e}'");
}
catch (IOException e)
{
Console.WriteLine($"The file could not be opened: '{e}'");
}
}
}
O CLR (Common Language Runtime) captura exceções não manipuladas pelos blocos
catch . Se uma exceção é capturada pelo CLR, um dos seguintes resultados pode
ocorrer dependendo da configuração do CLR:

Uma caixa de diálogo Depurar é exibida.


O programa interromperá a execução e uma caixa de diálogo será exibida com
informações de exceção.
Um erro é impresso no fluxo de saída de erro padrão.

7 Observação

A maioria dos códigos pode lançar uma exceção, sendo que algumas exceções, tais
como OutOfMemoryException, podem ser geradas pelo próprio CLR, a qualquer
momento. Embora os aplicativos não precisem lidar com essas exceções, esteja
ciente dessa possibilidade ao gravar bibliotecas para serem usadas por outros. Para
obter sugestões sobre quando definir código em um bloco try , confira Práticas
recomendadas para exceções.

Confira também
Exceções
Tratamento de erros de E/S no .NET
Como usar exceções específicas em um
bloco catch
Artigo • 07/04/2023

Em geral, é uma boa prática capturar um tipo específico de exceção em vez de usar a
instrução básica catch .

Quando ocorre uma exceção, ela é passada para cima na pilha e, a cada bloco catch, é
dada a oportunidade de tratá-la. A ordem das instruções catch é importante. Coloque
blocos catch direcionados para exceções específicas antes que um bloco catch de
exceção geral ou o compilador possa emitir um erro. O bloco catch adequado é
determinado ao fazer a correspondência entre o tipo da exceção e o nome da exceção
especificada no bloco catch. Se não houver nenhum bloco catch específico, a exceção
será detectada por um bloco catch geral, se houver.

O exemplo de código a seguir usa um bloco try / catch para capturar uma
InvalidCastException. O exemplo cria uma classe chamada Employee com uma única
propriedade, nível do funcionário ( Emlevel ). Um método, PromoteEmployee , utiliza um
objeto e incrementa o nível do funcionário. Um InvalidCastException ocorre quando
uma instância DateTime é passada para o método PromoteEmployee .

C#

using System;

public class Employee


{
//Create employee level property.
public int Emlevel
{
get
{
return(emlevel);
}
set
{
emlevel = value;
}
}

private int emlevel = 0;


}

public class Ex13


{
public static void PromoteEmployee(Object emp)
{
// Cast object to Employee.
var e = (Employee) emp;
// Increment employee level.
e.Emlevel = e.Emlevel + 1;
}

static void Main()


{
try
{
Object o = new Employee();
DateTime newYears = new DateTime(2001, 1, 1);
// Promote the new employee.
PromoteEmployee(o);
// Promote DateTime; results in InvalidCastException as newYears
is not an employee instance.
PromoteEmployee(newYears);
}
catch (InvalidCastException e)
{
Console.WriteLine("Error passing data to PromoteEmployee method.
" + e.Message);
}
}
}

Confira também
Exceções
Como gerar exceções explicitamente
Artigo • 10/05/2023

Você pode gerar explicitamente uma exceção usando o C# throw ou a instrução Throw
do Visual Basic. Você também pode lançar novamente uma exceção capturada usando a
instrução throw . É uma boa prática de codificação adicionar informações a uma exceção
que é lançada novamente para fornecer mais informações durante a depuração.

O exemplo de código a seguir usa um bloco try / catch para capturar uma possível
FileNotFoundException. Após o bloco try , há um bloco catch que captura a
FileNotFoundException e grava uma mensagem no console se o arquivo de dados não é
encontrado. A próxima instrução é a instrução throw , que gera uma nova
FileNotFoundException e adiciona informações de texto à exceção.

C#

var fs = default(FileStream);
try
{
// Opens a text tile.
fs = new FileStream(@"C:\temp\data.txt", FileMode.Open);
var sr = new StreamReader(fs);

// A value is read from the file and output to the console.


string? line = sr.ReadLine();
Console.WriteLine(line);
}
catch (FileNotFoundException e)
{
Console.WriteLine($"[Data File Missing] {e}");
throw new FileNotFoundException(@"[data.txt not in c:\temp directory]",
e);
}
finally
{
if (fs != null)
fs.Close();
}

Confira também
Exceções
Como criar exceções definidas pelo
usuário
Artigo • 09/05/2023

O .NET fornece uma hierarquia de classes de exceção derivada, em última análise, da


classe base Exception. No entanto, se nenhuma das exceções predefinidas atender às
suas necessidades, será possível criar suas próprias classes de exceção derivando da
classe Exception.

Ao criar suas próprias exceções, encerre o nome de classe de exceção definida pelo
usuário com a palavra "Exception" e implemente os três construtores comuns, como
mostrado no exemplo a seguir. O exemplo define uma nova classe de exceção chamada
EmployeeListNotFoundException . A classe é derivada da classe base Exception e inclui

três constructos.

C#

using System;

public class EmployeeListNotFoundException : Exception


{
public EmployeeListNotFoundException()
{
}

public EmployeeListNotFoundException(string message)


: base(message)
{
}

public EmployeeListNotFoundException(string message, Exception inner)


: base(message, inner)
{
}
}

7 Observação

Em situações nas quais você está usando a comunicação remota, você deve
garantir que os metadados para todas as exceções definidas pelo usuário estejam
disponíveis no servidor (computador chamado) e para o cliente (o objeto de proxy
ou chamador). Para obter mais informações, consulte Práticas recomendadas para
exceções.
Confira também
Exceções
Como criar exceções definidas pelo
usuário com mensagens de exceção
localizadas
Artigo • 10/05/2023

Neste artigo, você aprenderá a criar exceções definidas pelo usuário que são herdadas
da classe Exception base com mensagens de exceção localizadas usando assemblies
satélites.

Criar exceções personalizadas


O .NET contém muitas exceções diferentes que você pode usar. No entanto, em alguns
casos, quando nenhum delas atender às suas necessidades, você poderá criar suas
próprias exceções personalizadas.

Vamos supor que você queira criar um StudentNotFoundException que contenha uma
propriedade StudentName . Para criar um ponto de extremidade personalizado, siga estas
etapas:

1. Crie uma classe serializável que herda de Exception. O nome da classe deve
terminar com "Exceção":

C#

[Serializable]
public class StudentNotFoundException : Exception { }

2. Adicione os construtores padrão:

C#

[Serializable]
public class StudentNotFoundException : Exception
{
public StudentNotFoundException() { }

public StudentNotFoundException(string message)


: base(message) { }

public StudentNotFoundException(string message, Exception inner)


: base(message, inner) { }
}
3. Defina todas as propriedades e construtores adicionais:

C#

[Serializable]
public class StudentNotFoundException : Exception
{
public string StudentName { get; }

public StudentNotFoundException() { }

public StudentNotFoundException(string message)


: base(message) { }

public StudentNotFoundException(string message, Exception inner)


: base(message, inner) { }

public StudentNotFoundException(string message, string studentName)


: this(message)
{
StudentName = studentName;
}
}

Criar mensagens de exceção localizadas


Você criou uma exceção personalizada e pode lançá-la em qualquer lugar com um
código como o seguinte:

C#

throw new StudentNotFoundException("The student cannot be found.", "John");

O problema com a linha anterior é que "The student cannot be found." é apenas uma
cadeia de caracteres constante. Em um aplicativo localizado, convém ter mensagens
diferentes dependendo da cultura do usuário. Assemblies satélite são uma boa maneira
de fazer isso. Um assembly satélite é um .dll que contém recursos para uma linguagem
específica. Quando você solicita recursos específicos em tempo de execução, o CLR
localiza esse recurso dependendo da cultura do usuário. Se nenhum assembly satélite
for encontrado para essa cultura, serão usados os recursos da cultura padrão.

Para criar mensagens de exceção localizadas:

1. Crie uma nova pasta chamada Recursos para armazenar os arquivos de recurso.
2. Adicione um novo arquivo de recurso a ela. Para fazer isso no Visual Studio, clique
com o botão direito do mouse na pasta no Gerenciador de Soluções e selecione
Adicionar>Novo item>Arquivo de recursos . Nomeie o arquivo como
ExceptionMessages.resx. Este é o arquivo de recursos padrão.

3. Adicione um par de nome/valor para sua mensagem de exceção, como mostra a


imagem a seguir:

4. Adicione um novo arquivo de recurso para francês. Nomei-o como


ExceptionMessages.fr-FR.resx.

5. Adicione um par de nome/valor para a mensagem de exceção novamente, mas


com um valor em francês:

6. Depois de compilar o projeto, a pasta de saída de build deve conter a pasta fr-FR
com um arquivo .dll, que é o assembly satélite.

7. Você lança a exceção com código como o seguinte:

C#

var resourceManager = new


ResourceManager("FULLY_QUALIFIED_NAME_OF_RESOURCE_FILE",
Assembly.GetExecutingAssembly());
throw new
StudentNotFoundException(resourceManager.GetString("StudentNotFound"),
"John");

7 Observação

Se o nome do projeto for TestProject e o arquivo de recurso


ExceptionMessages.resx residir na pasta Resources do projeto, o nome
totalmente qualificado do arquivo de recurso será
TestProject.Resources.ExceptionMessages .
Confira também
Como criar exceções definidas pelo usuário
Criar assemblies satélite
base (Referência de C#)
this (Referência de C#)
Como usar blocos finally
Artigo • 07/04/2023

Quando ocorre uma exceção, a execução é interrompida e o controle é dado ao


manipulador de exceção apropriado. Geralmente, isso significa que linhas de código
que você espera que sejam executadas são ignoradas. A limpeza de alguns recursos,
assim como o fechamento de um arquivo, precisará ser feita mesmo se uma exceção for
gerada. Para fazer isso, você pode usar um bloco finally . Um bloco finally sempre é
executado, independentemente de uma exceção ser ou não gerada.

O exemplo de código a seguir usa um bloco try / catch para capturar uma
ArgumentOutOfRangeException. O método Main cria duas matrizes e tenta copiar uma
para a outra. A ação gera um ArgumentOutOfRangeException e o erro é gravado no
console. Este bloco finally é executado independentemente resultado da ação de
cópia.

C#

using System;

class ArgumentOutOfRangeExample
{
public static void Main()
{
int[] array1 = {0, 0};
int[] array2 = {0, 0};

try
{
Array.Copy(array1, array2, -1);
}
catch (ArgumentOutOfRangeException e)
{
Console.WriteLine("Error: {0}", e);
throw;
}
finally
{
Console.WriteLine("This statement is always executed.");
}
}
}

Confira também
Exceções
Usar manipuladores de exceção filtrados
pelo usuário
Artigo • 07/04/2023

Os manipuladores de exceção filtrados por usuário capturam e tratam exceções com


base nos requisitos que você define para a exceção. Esses manipuladores usam a
instrução catch com a palavra-chave when ( Catch e When no Visual Basic).

Essa técnica é útil quando um objeto de exceção em particular corresponde a vários


erros. Nesse caso, o objeto normalmente tem uma propriedade que contém o código
de erro específico associado ao erro. Você pode usar a propriedade do código de erro
na expressão para selecionar apenas o erro específico que deseja manipular nessa
cláusula catch .

O exemplo a seguir ilustra o a instrução catch / when .

C#

try
{
//Try statements.
}
catch (Exception ex) when (ex.Message.Contains("404"))
{
//Catch statements.
}

A expressão da cláusula filtrada pelo usuário não é restrita de nenhuma forma. Se


ocorrer uma exceção durante a execução da expressão filtrada pelo usuário, essa
exceção será descartada e será considerado que a expressão do filtro foi avaliada como
falsa. Nesse caso, o Common Language Runtime continua a pesquisar um manipulador
para a exceção atual.

Combinar a exceção específica e as cláusulas


filtradas pelo usuário
Uma instrução catch pode contar a exceção específica e as cláusulas filtradas pelo
usuário. O runtime testa primeiro a exceção específica. Se a exceção específica for bem-
sucedida, o runtime executará o filtro do usuário. O filtro genérico pode conter uma
referência à variável declarada no filtro da classe. Observe que a ordem das duas
cláusulas do filtro não pode ser invertida.
O exemplo a seguir mostra uma exceção específica na instrução catch e a cláusula
filtrada pelo usuário com o uso da palavra-chave when.

C#

try
{
//Try statements.
}
catch (System.Net.Http.HttpRequestException ex) when
(ex.Message.Contains("404"))
{
//Catch statements.
}

Confira também
Exceções
Manipulando exceções de
interoperabilidade COM
Artigo • 07/04/2023

Os códigos gerenciado e não gerenciado podem trabalhar juntos para tratar de


exceções. Se um método lança uma exceção no código gerenciado, o common
language runtime pode passar um HRESULT para um objeto COM. Se um método falhar
no código não gerenciado, retornando um HRESULT de falha, o runtime lançará uma
exceção que pode ser detectada pelo código gerenciado.

O runtime mapeia automaticamente o HRESULT da interoperabilidade COM para


exceções mais específicas. Por exemplo, E_ACCESSDENIED se torna
UnauthorizedAccessException, E_OUTOFMEMORY se torna OutOfMemoryException e
assim por diante.

Se o HRESULT for um resultado personalizado, ou se for desconhecido para o runtime, o


runtime passará um COMException genérico ao cliente. A propriedade ErrorCode do
COMException contém o valor de HRESULT.

Trabalhar com IErrorInfo


Quando um erro é passado do COM para o código gerenciado, o runtime preenche o
objeto de exceção com informações do erro. Objetos COM que dão suporte a IErrorInfo
e retornam HRESULTS fornecem essas informações para exceções de código gerenciado.
Por exemplo, o runtime mapeia a Descrição do erro COM para a propriedade Message
da exceção. Se o HRESULT não fornecer mais informações sobre o erro, o runtime
preencherá muitas das propriedades da exceção com valores padrão.

Se um método falhar no código não gerenciado, uma exceção poderá ser passada para
um segmento de código gerenciado. O tópico HRESULTS e exceções contém uma tabela
que mostra como HRESULTS mapeia para objetos de exceção de runtime.

Confira também
Exceções
Práticas recomendadas para exceções
Artigo • 10/05/2023

Um aplicativo bem projetado sabe tratar erros e exceções para evitar falhas. Este artigo
descreve as práticas recomendadas para tratar e criar exceções.

Usar blocos try/catch/finally para se recuperar


de erros ou liberar recursos
Use blocos try / catch ao redor do código que pode potencialmente gerar uma exceção
e seu código pode se recuperar dessa exceção. Em blocos catch , sempre ordene as
exceções da mais derivada para a menos derivada. Todas as exceções derivam da classe
Exception. Exceções mais derivadas não são manipuladas por uma cláusula de captura
que é precedida por uma cláusula de captura de uma classe de exceção de base.
Quando seu código não puder se recuperar de uma exceção, não use captura nessa
exceção. Habilite métodos adicionais na pilha de chamadas para se recuperar se
possível.

Limpe os recursos alocados com instruções using ou blocos finally . Prefira instruções
using para limpar recursos automaticamente quando exceções forem lançadas. Use

blocos finally para limpar os recursos que não implementam IDisposable. O código
em uma cláusula finally quase sempre é executado, mesmo quando exceções são
geradas.

Tratar de condições comuns sem gerar


exceções
Para condições que têm boa probabilidade de ocorrer mas que podem disparar uma
exceção, considere tratá-las de uma maneira que evite essa exceção. Por exemplo, se
você tentar fechar uma conexão que já está fechada, você obterá um
InvalidOperationException . Você pode evitar isso usando uma instrução if para

verificar o estado da conexão antes de tentar fechá-la.

C#

if (conn.State != ConnectionState.Closed)
{
conn.Close();
}
Se você não verificar o estado da conexão antes de fechar, você poderá capturar a
exceção InvalidOperationException .

C#

try
{
conn.Close();
}
catch (InvalidOperationException ex)
{
Console.WriteLine(ex.GetType().FullName);
Console.WriteLine(ex.Message);
}

O método a escolher depende da frequência com que você espera que o evento ocorra.

Use o tratamento de exceções se o evento não ocorrer muito frequentemente, ou


seja, se o evento for realmente excepcional e indicar um erro, como um fim de
arquivo inesperado. Quando você usa o tratamento de exceções, menos código é
executado em condições normais.

Verifique a existência de condições de erro no código se o evento ocorrer


rotineiramente e puder ser considerado parte da execução normal. Quando você
verifica se há condições de erro comuns, menos código é executado porque você
evita exceções.

Projetar classes de modo que as exceções


possam ser evitadas
Uma classe pode fornecer métodos ou propriedades que permitem que você evite fazer
uma chamada que dispararia uma exceção. Por exemplo, uma classe FileStream fornece
métodos que ajudam a determinar se ao final do arquivo foi atingido. Esses métodos
podem ser usados para evitar exceções geradas quando você faz a leitura após o fim do
arquivo. O exemplo a seguir mostra como ler até o final de um arquivo sem disparar
uma exceção:

C#

class FileRead
{
public void ReadAll(FileStream fileToRead)
{
// This if statement is optional
// as it is very unlikely that
// the stream would ever be null.
if (fileToRead == null)
{
throw new ArgumentNullException();
}

int b;

// Set the stream position to the beginning of the file.


fileToRead.Seek(0, SeekOrigin.Begin);

// Read each byte to the end of the file.


for (int i = 0; i < fileToRead.Length; i++)
{
b = fileToRead.ReadByte();
Console.Write(b.ToString());
// Or do something else with the byte.
}
}
}

Outra maneira de evitar exceções é retornar a nulo (ou padrão) para casos muito
comuns de erro, em vez de gerar uma exceção. Um caso de erro comum pode ser
considerado um fluxo normal de controle. Ao retornar nulo (ou padrão) nesses casos,
você minimiza o impacto no desempenho de um aplicativo.

Para tipos de valor, usar Nullable<T> ou padrão como indicador de erro é algo a ser
considerado para seu aplicativo. Ao usar Nullable<Guid> , default se torna null em vez
de Guid.Empty . Algumas vezes, adicionar Nullable<T> pode deixar mais claro quando
um valor está presente ou ausente. Outras vezes, adicionar Nullable<T> pode criar
casos extras que não precisam ser verificados e só servem para criar possíveis fontes de
erros.

Gerar exceções em vez de retornar um código


de erro
Exceções garantem que falhas não passem despercebidas porque o código de chamada
não verificou um código de retorno.

Usar os tipos de exceção do .NET predefinidos


Apresente uma nova classe de exceção apenas quando a predefinida não se aplicar. Por
exemplo:
Se uma definição de propriedade ou chamada de método não for adequada para
o estado atual do objeto, gere uma exceção InvalidOperationException.
Se parâmetros inválidos forem passados, gere uma exceção ArgumentException
ou uma das classes predefinidas que derivam de ArgumentException.

Terminar os nomes das classes de exceção com


a palavra Exception
Quando uma exceção personalizada for necessária, nomeie-a adequadamente e derive-
a da classe Exception. Por exemplo:

C#

public class MyFileNotFoundException : Exception


{
}

Incluir três construtores em classes de exceção


personalizada
Use pelo menos três os construtores comuns ao criar suas próprias classes de exceção: o
construtor sem parâmetros, um construtor que recebe uma mensagem de cadeia de
caracteres e uma exceção interna.

Exception(), que usa valores padrão.


Exception(String), que aceita uma mensagem de cadeia de caracteres.
Exception(String, Exception), que aceita uma mensagem de cadeia de caracteres e
a exceção interna.

Para ver um exemplo, veja Como criar exceções definidas pelo usuário.

Certifique-se de que os dados de exceção estão


disponíveis quando o código é executado
remotamente
Ao criar exceções definidas pelo usuário, assegure que os metadados para as exceções
estejam disponíveis para códigos executando remotamente.
Por exemplo, em implementações do .NET que dão suporte a domínios de aplicativo,
podem ocorrer exceções entre domínios de aplicativo. Suponha que o domínio de
aplicativo A crie o domínio de aplicativo B, que executa o código que gera uma exceção.
Para que o domínio de aplicativo A capture e trate corretamente a exceção, ele deverá
ser capaz de localizar o assembly que contém a exceção gerada pelo domínio de
aplicativo B. Se o domínio de aplicativo B gerar uma exceção contida em um assembly
em sua própria base de aplicativo, mas não na base de aplicativo do domínio de
aplicativo A, o domínio de aplicativo A não conseguirá localizar a exceção e o Common
Language Runtime gerará uma exceção FileNotFoundException. Para evitar essa
situação, você pode implantar o assembly que contém as informações de exceção de
duas maneiras:

Coloque o assembly em uma base de aplicativos comum compartilhada por


ambos os domínios de aplicativos.
Se os domínios não compartilham uma base de aplicativos comum, assine o
assembly que contém as informações de exceção com um nome forte e implante o
assembly no cache de assembly global.

Usar mensagens de erro gramaticalmente


corretas
Escreva frases claras e inclua pontuação final. Cada sentença na cadeia de caracteres
atribuída à propriedade Exception.Message deve terminar com um ponto. Por exemplo,
"A tabela de log estourou." seria uma cadeia de caracteres de mensagem apropriada.

Incluir uma mensagem de cadeia de caracteres


localizada em cada exceção
A mensagem de erro que o usuário recebe é derivada da propriedade
Exception.Message da exceção que foi gerada, e não do nome da classe de exceção.
Normalmente, você atribui um valor à propriedade Exception.Message passando a
cadeia de caracteres de mensagem para o argumento message de um Construtor de
exceção.

Para aplicativos localizados, você deverá fornecer uma cadeia de caracteres de


mensagem localizada para toda exceção que seu aplicativo puder gerar. Use arquivos
de recurso para fornecer mensagens de erro localizadas. Para obter informações sobre
como localizar aplicativos e recuperar cadeias de caracteres localizadas, confira os
artigos a seguir:
Instruções: Criar exceções definidas pelo usuário com mensagens de exceção
localizadas
Recursos em aplicativos .NET
System.Resources.ResourceManager

Em exceções personalizadas, forneça


propriedades adicionais conforme necessário
Forneça propriedades adicionais para uma exceção (além da cadeia de caracteres de
mensagem personalizada) somente quando houver um cenário programático no qual as
informações adicionais serão úteis. Por exemplo, o FileNotFoundException fornece a
propriedade FileName.

Posicionar instruções throw de modo que o


rastreamento de pilha seja útil
O rastreamento de pilha começa na instrução na qual a exceção é lançada e termina na
instrução catch que captura a exceção.

Usar métodos de construtor de exceção


É comum uma classe gerar a mesma exceção em locais diferentes em sua
implementação. Para evitar excesso de código, use métodos auxiliares que criam a
exceção e a retornam. Por exemplo:

C#

class FileReader
{
private string fileName;

public FileReader(string path)


{
fileName = path;
}

public byte[] Read(int bytes)


{
byte[] results = FileUtils.ReadFromFile(fileName, bytes);
if (results == null)
{
throw NewFileIOException();
}
return results;
}

FileReaderException NewFileIOException()
{
string description = "My NewFileIOException Description";

return new FileReaderException(description);


}
}

Em alguns casos, é mais adequado usar o construtor da exceção para compilá-la. Um


exemplo é uma classe de exceção global como ArgumentException.

Restaurar o estado quando os métodos não são


concluídos devido a exceções
Os chamadores devem ser capazes de pressupor que não haverá efeitos colaterais
quando uma exceção for gerada de um método. Por exemplo, se você tiver um código
que transfere dinheiro retirando-o de uma conta e depositando em outra e uma
exceção for gerada ao executar o depósito, você não desejará que a retirada permaneça
em vigor.

C#

public void TransferFunds(Account from, Account to, decimal amount)


{
from.Withdrawal(amount);
// If the deposit fails, the withdrawal shouldn't remain in effect.
to.Deposit(amount);
}

O método anterior não gera diretamente nenhuma exceção. No entanto, você deve
escrever o método para que o saque seja invertido se a operação de depósito falhar.

Uma maneira de lidar com essa situação é capturar todas as exceções geradas pela
transação do depósito e reverter a retirada.

C#

private static void TransferFunds(Account from, Account to, decimal amount)


{
string withdrawalTrxID = from.Withdrawal(amount);
try
{
to.Deposit(amount);
}
catch
{
from.RollbackTransaction(withdrawalTrxID);
throw;
}
}

Este exemplo ilustra o uso de throw para gerar novamente a exceção original, que pode
tornar mais fácil para os chamadores ver a causa real do problema sem a necessidade
de examinar a propriedade InnerException. Uma alternativa é gerar uma nova exceção e
incluir a exceção original como a exceção interna.

C#

catch (Exception ex)


{
from.RollbackTransaction(withdrawalTrxID);
throw new TransferFundsException("Withdrawal failed.", innerException:
ex)
{
From = from,
To = to,
Amount = amount
};
}

Capturar exceções para lançar novamente mais


tarde
Para capturar uma exceção e preservar sua pilha de chamadas para poder lançá-la
novamente mais tarde, use a classe
System.Runtime.ExceptionServices.ExceptionDispatchInfo. Essa classe fornece os
seguintes métodos e propriedades (entre outros):

Use ExceptionDispatchInfo.Capture(Exception) para capturar uma exceção e uma


pilha de chamadas.
Use ExceptionDispatchInfo.Throw() para restaurar o estado que foi salvo quando a
exceção foi capturada e lançar novamente a exceção capturada.
Use a propriedade ExceptionDispatchInfo.SourceException para inspecionar a
exceção capturada.

O exemplo a seguir mostra como a classe ExceptionDispatchInfo pode ser usada e como
pode ser a saída.
C#

ExceptionDispatchInfo? edi = null;


try
{
var txt = File.ReadAllText(@"C:\temp\file.txt");
}
catch (FileNotFoundException e)
{
edi = ExceptionDispatchInfo.Capture(e);
}

// ...

Console.WriteLine("I was here.");

if (edi is not null)


edi.Throw();

Se o arquivo no código de exemplo não existir, a seguinte saída será produzida:

Saída

I was here.
Unhandled exception. System.IO.FileNotFoundException: Could not find file
'C:\temp\file.txt'.
File name: 'C:\temp\file.txt'
at Microsoft.Win32.SafeHandles.SafeFileHandle.CreateFile(String fullPath,
FileMode mode, FileAccess access, FileShare share, FileOptions options)
at Microsoft.Win32.SafeHandles.SafeFileHandle.Open(String fullPath,
FileMode mode, FileAccess access, FileShare share, FileOptions options,
Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.OSFileStreamStrategy..ctor(String path, FileMode
mode, FileAccess access, FileShare share, FileOptions options, Int64
preallocationSize, Nullable`1 unixCreateMode)
at System.IO.Strategies.FileStreamHelpers.ChooseStrategyCore(String path,
FileMode mode, FileAccess access, FileShare share, FileOptions options,
Int64 preallocationSize, Nullable`1 unixCreateMode)
at System.IO.StreamReader.ValidateArgsAndOpenPath(String path, Encoding
encoding, Int32 bufferSize)
at System.IO.File.ReadAllText(String path, Encoding encoding)
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 12
--- End of stack trace from previous location ---
at Example.ProcessFile.Main() in C:\repos\ConsoleApp1\Program.cs:line 24

Confira também
Exceções
Classe System.AccessViolationException
Artigo • 10/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Uma violação de acesso ocorre em código não gerenciado ou inseguro quando tenta ler
ou gravar na memória que não foi alocada ou para a qual não tem acesso. Isso
geralmente ocorre porque um ponteiro tem um valor ruim. Nem todas as leituras ou
gravações por ponteiros incorretos resultam em violações de acesso, de modo que uma
violação de acesso geralmente indica que várias leituras ou gravações ocorreram por
ponteiros incorretos, e que a memória pode estar corrompida. Portanto,as violações de
acesso quase sempre indicam erros graves de programação. Um
AccessViolationException identifica claramente esses erros graves.

Em programas que consistem inteiramente de código gerenciado verificável, todas as


referências são válidas ou nulas, e violações de acesso são impossíveis. Qualquer
operação que tente referenciar uma referência nula no código verificável gera uma
exceção NullReferenceException. Um AccessViolationException ocorre somente quando
o código gerenciado verificável interage com código não gerenciado ou com código
gerenciado não seguro.

Solucionar problemas de exceções de


AccessViolationException
Uma exceção AccessViolationException só pode ocorrer em código gerenciado inseguro
ou quando o código gerenciado verificável interage com código não gerenciado:

Uma violação de acesso que ocorre em código gerenciado inseguro pode ser
expressa como uma exceção NullReferenceException ou uma exceção
AccessViolationException, dependendo da plataforma.
Uma violação de acesso em código não gerenciado que o transforma em um
código gerenciado é sempre envolvida em uma exceção AccessViolationException.

Em ambos os casos, você pode identificar e corrigir a causa da exceção


AccessViolationException da seguinte maneira:

Certifique se que a memória que você está tentando acessar tenha sido alocada.
Uma exceção AccessViolationException é sempre lançada por uma tentativa de
acessar a memória protegida, ou seja, acessar a memória que não está alocada ou
que não pertence a um processo.
O gerenciamento automático de memória é um dos serviços que são fornecidos
pelo runtime .NET. Se o código gerenciado fornecer a mesma funcionalidade que
seu código não gerenciado, talvez você queira mover para o código gerenciado
para aproveitar essa funcionalidade. Para obter mais, informações, consulte
Gerenciamento Automático de Memória.

Certifique-se de que a memória que você está tentando acessar não foi
corrompida. Se várias operações de leitura ou gravação ocorreram por meio de
ponteiros incorretos, a memória pode estar corrompida. Isso geralmente ocorre ao
fazer leitura ou gravação em endereços fora de um buffer predefinido.

AccessViolationException e blocos try/catch


As exceções AccessViolationException lançadas pelo runtime .NET não serão
identificadas pela instrução catch em um manipulador de exceções estruturadas se a
exceção ocorrer fora da memória reservada pelo runtime. Para identificar essa exceção
AccessViolationException, aplique o atributo
HandleProcessCorruptedStateExceptionsAttribute ao método no qual a exceção é
lançada. Essa alteração não afeta as exceções AccessViolationException lançadas pelo
código do usuário, que podem continuar a ser capturadas por uma instrução catch .

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Classe System.Exception
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A Exception classe é a classe base para todas as exceções. Quando ocorre um erro, o
sistema ou o aplicativo em execução no momento o relata lançando uma exceção que
contém informações sobre o erro. Depois que uma exceção é lançada, ela é manipulada
pelo aplicativo ou pelo manipulador de exceção padrão.

Erros e exceções
Erros em tempo de execução podem ocorrer por vários motivos. No entanto, nem todos
os erros devem ser manipulados como exceções no seu código. Aqui estão algumas
categorias de erros que podem ocorrer em tempo de execução e as maneiras
apropriadas de responder a eles.

Erros de uso. Um erro de uso representa um erro na lógica do programa que pode
resultar em uma exceção. No entanto, o erro deve ser resolvido não por meio do
tratamento de exceções, mas modificando o código com falha. Por exemplo, a
substituição do método Object.Equals(Object) no exemplo a seguir pressupõe que
o argumento obj deve ser sempre não-nulo.

C#

using System;

public class Person1


{
private string _name;

public string Name


{
get { return _name; }
set { _name = value; }
}

public override int GetHashCode()


{
return this.Name.GetHashCode();
}

public override bool Equals(object obj)


{
// This implementation contains an error in program logic:
// It assumes that the obj argument is not null.
Person1 p = (Person1) obj;
return this.Name.Equals(p.Name);
}
}

public class UsageErrorsEx1


{
public static void Main()
{
Person1 p1 = new Person1();
p1.Name = "John";
Person1 p2 = null;

// The following throws a NullReferenceException.


Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));
}
}

A exceção NullReferenceException que resulta quando obj é null pode ser


eliminada modificando o código-fonte para testar explicitamente para null antes
de chamar a substituição Object.Equals e, em seguida, recompilar. O exemplo a
seguir contém o código-fonte corrigido que manipula um argumento null .

C#

using System;

public class Person2


{
private string _name;

public string Name


{
get { return _name; }
set { _name = value; }
}

public override int GetHashCode()


{
return this.Name.GetHashCode();
}

public override bool Equals(object obj)


{
// This implementation handles a null obj argument.
Person2 p = obj as Person2;
if (p == null)
return false;
else
return this.Name.Equals(p.Name);
}
}

public class UsageErrorsEx2


{
public static void Main()
{
Person2 p1 = new Person2();
p1.Name = "John";
Person2 p2 = null;

Console.WriteLine("p1 = p2: {0}", p1.Equals(p2));


}
}
// The example displays the following output:
// p1 = p2: False

Em vez de usar o tratamento de exceções para erros de uso, você pode usar o
método Debug.Assert para identificar erros de uso em compilações de depuração
e o método Trace.Assert para identificar erros de uso em compilações de
depuração e versão. Para obter mais informações, confira Asserções em código
gerenciado.

Erros do programa. Um erro de programa é um erro em tempo de execução que


não pode necessariamente ser evitado escrevendo código sem bugs.

Em alguns casos, um erro de programa pode refletir uma condição de erro


esperada ou de rotina. Nesse caso, convém evitar o uso de manipulação de
exceção para lidar com o erro de programa e, em vez disso, tentar novamente a
operação. Por exemplo, se for esperado que o usuário insira uma data em um
formato específico, você poderá analisar a cadeia de caracteres de data chamando
o método DateTime.TryParseExact, que retorna um valor Boolean que indica se a
operação de análise foi bem-sucedida, em vez de usar o método
DateTime.ParseExact, que lança uma exceção FormatException se a cadeia de
caracteres de data não puder ser convertida em um valor DateTime. Da mesma
forma, se um usuário tentar abrir um arquivo que não existe, você pode primeiro
chamar o método File.Exists para verificar se o arquivo existe e, se não existir,
perguntar ao usuário se ele deseja criá-lo.

Em outros casos, um erro de programa reflete uma condição de erro inesperada


que pode ser manipulada em seu código. Por exemplo, mesmo que você tenha
verificado a existência de um arquivo, ele pode ser excluído antes que você possa
abri-lo ou pode estar corrompido. Nesse caso, tentar abrir o arquivo instanciando
um objeto StreamReader ou chamando o método Open pode lançar uma exceção
FileNotFoundException. Nesses casos, você deve usar a manipulação de exceção
para se recuperar do erro.

Falhas do sistema. Uma falha do sistema é um erro em tempo de execução que


não pode ser manipulado programaticamente de forma significativa. Por exemplo,
qualquer método pode lançar uma exceção OutOfMemoryException se o CLR não
conseguir alocar memória adicional. Normalmente, as falhas do sistema não são
tratadas usando a manipulação de exceção. Em vez disso, você poderá usar um
evento como AppDomain.UnhandledException e chamar o método
Environment.FailFast para registrar informações de exceção e notificar o usuário
sobre a falha antes que o aplicativo seja encerrado.

Blocos try/catch
O CLR fornece um modelo de manipulação de exceções que se baseia na representação
de exceções como objetos e na separação do código do programa e do código de
manipulação de exceções em blocos try e blocos catch . Pode haver um ou mais
blocos catch , cada um projetado para lidar com um tipo específico de exceção, ou um
bloco projetado para capturar uma exceção mais específica do que outro bloco.

Se um aplicativo manipula exceções que ocorrem durante a execução de um bloco de


código de aplicativo, o código deve ser colocado dentro de uma instrução try e é
chamado de bloco try . O código do aplicativo que manipula exceções lançadas por um
bloco try é colocado dentro de uma instrução catch e é chamado de bloco catch .
Nenhum ou mais blocos catch são associados a um bloco try e cada bloco catch
inclui um filtro de tipo que determina os tipos de exceções que ele trata.

Quando ocorre uma exceção em um bloco try , o sistema pesquisa os blocos catch
associados na ordem em que aparecem no código do aplicativo, até localizar um bloco
catch que trata a exceção. Um bloco catch trata uma exceção de tipo T se o filtro de

tipo do bloco de captura especificar T ou qualquer tipo do qual T deriva. O sistema


para de pesquisar depois de encontrar o primeiro bloco catch que trata a exceção. Por
esse motivo, no código do aplicativo, um bloco catch que manipula um tipo deve ser
especificado antes de um bloco catch que manipula seus tipos base, conforme
demonstrado no exemplo a seguir nesta seção. Um bloco de captura que manipula
System.Exception é especificado por último.

Se nenhum dos blocos catch associados ao bloco try atual manipular a exceção e o
bloco try atual estiver aninhado em outros blocos try na chamada atual, os blocos
catch associados ao próximo bloco try delimitador serão pesquisados. Se nenhum
bloco catch para a exceção for encontrado, o sistema pesquisará os níveis de
aninhamento anteriores na chamada atual. Se nenhum bloco catch para a exceção for
encontrado na chamada atual, a exceção será passada para a pilha de chamadas e o
registro de ativação anterior será procurado por um bloco catch que manipula a
exceção. A pesquisa da pilha de chamadas continua até que a exceção seja manipulada
ou até que não existam mais registros na pilha de chamadas. Se a parte superior da
pilha de chamadas for alcançada sem localizar um bloco catch que manipula a exceção,
o manipulador de exceção padrão a manipulará e o aplicativo será encerrado.

Expressão F# try...with
F# não usa blocos catch . Em vez disso, uma exceção gerada é correspondida ao padrão
usando um único bloco with . Como essa é uma expressão, em vez de uma instrução,
todos os caminhos devem retornar o mesmo tipo. Para saber mais, consulte A expressão
try...with.

Recursos do tipo de exceção


Os tipos de exceção oferecem suporte aos seguintes recursos:

Texto legível por humanos que descreve o erro. Quando ocorre uma exceção, o
runtime disponibiliza uma mensagem de texto para informar o usuário da natureza
do erro e sugerir ações para resolver o problema. Essa mensagem de texto é
mantida na propriedade Message do objeto de exceção. Durante a criação do
objeto de exceção, você pode passar uma cadeia de caracteres de texto para o
construtor para descrever os detalhes dessa exceção específica. Se nenhum
argumento de mensagem de erro for fornecido ao construtor, a mensagem de
erro padrão será usada. Para obter mais informações, consulte a propriedade
Message.

O estado da pilha de chamadas quando a exceção foi lançada. A propriedade


StackTrace carrega um rastreamento de pilha que pode ser usado para determinar
onde o erro ocorre no código. O rastreamento de pilha lista todos os métodos
chamados e os números de linha no arquivo de origem onde as chamadas são
feitas.

Propriedades da classe de exceção


A classe Exception inclui várias propriedades que ajudam a identificar o local do código,
o tipo, o arquivo de ajuda e o motivo da exceção do código: StackTrace, InnerException,
Message, HelpLink, HResult, Source, TargetSite e Data.

Quando existe uma relação causal entre duas ou mais exceções, a propriedade
InnerException mantém essas informações. A exceção externa é lançada em resposta a
essa exceção interna. O código que manipula a exceção externa pode usar as
informações da exceção interna anterior para manipular o erro de forma mais
apropriada. Informações complementares sobre a exceção podem ser armazenadas
como uma coleção de pares chave/valor na propriedade Data.

A cadeia de caracteres da mensagem de erro que é passada para o construtor durante a


criação do objeto de exceção deve ser localizada e pode ser fornecida a partir de um
arquivo de recurso usando a classe ResourceManager. Para obter mais informações
sobre recursos localizados, consulte os tópicos Criar assemblies satélite e Empacotar e
implantar recursos.

Para fornecer ao usuário informações abrangentes sobre o motivo da exceção ter


ocorrido, a propriedade HelpLink pode conter um URL (ou URN) para um arquivo de
ajuda.

A classe Exception usa o HRESULT COR_E_EXCEPTION , que tem o valor 0x80131500.

Para obter uma lista de valores de propriedade inicial para uma instância da classe
Exception, consulte os construtores Exception.

Considerações sobre o desempenho


Lançar ou manipular uma exceção consome uma quantidade significativa de recursos
do sistema e tempo de execução. Lançar exceções apenas para manipular condições
realmente extraordinárias, não para manipular eventos previsíveis ou controle de fluxo.
Por exemplo, em alguns casos, como quando você está desenvolvendo uma biblioteca
de classes, é apropriado lançar uma exceção se um argumento de método for inválido,
porque você espera que seu método seja chamado com parâmetros válidos. Um
argumento de método inválido, se não for o resultado de um erro de uso, significa que
algo extraordinário ocorreu. Por outro lado, não lance uma exceção se a entrada de
usuário for inválida, porque você pode esperar que os usuários ocasionalmente insiram
dados inválidos. Em vez disso, forneça um mecanismo de repetição para que os usuários
possam inserir entradas válidas. Você também não deve usar exceções para manipular
erros de uso. Em vez disso, use declarações para identificar e corrigir erros de uso.

Além disso, não lance uma exceção quando um código de retorno for suficiente; não
converta um código de retorno em uma exceção; e não pegue uma exceção
rotineiramente, ignore-a e continue o processamento.
Lançar uma exceção novamente
Em muitos casos, um manipulador de exceção simplesmente deseja passar a exceção
para o chamador. Isso ocorre com mais frequência em:

Uma biblioteca de classes que, por sua vez, encapsula chamadas para métodos na
biblioteca de classes .NET ou outras bibliotecas de classes.

Um aplicativo ou biblioteca que encontra uma exceção fatal. O manipulador de


exceção pode registrar a exceção e, em seguida, lançar novamente a exceção.

A maneira recomendada de lançar uma exceção novamente é simplesmente usar a


instrução throw em C#, a função reraise em F# e a instrução Throw no Visual Basic sem
incluir uma expressão. Isso garante que todas as informações da pilha de chamadas
sejam preservadas quando a exceção for propagada para o chamador. O exemplo a
seguir ilustra essa situação. Um método de extensão de cadeia de caracteres,
FindOccurrences , encapsula uma ou mais chamadas para String.IndexOf(String, Int32)

sem validar seus argumentos previamente.

C#

using System;
using System.Collections.Generic;

public static class Library1


{
public static int[] FindOccurrences(this String s, String f)
{
var indexes = new List<int>();
int currentIndex = 0;
try
{
while (currentIndex >= 0 && currentIndex < s.Length)
{
currentIndex = s.IndexOf(f, currentIndex);
if (currentIndex >= 0)
{
indexes.Add(currentIndex);
currentIndex++;
}
}
}
catch (ArgumentNullException)
{
// Perform some action here, such as logging this exception.

throw;
}
return indexes.ToArray();
}
}

Em seguida, um chamador chama FindOccurrences duas vezes. Na segunda chamada


para FindOccurrences , o chamador passa null como a cadeia de caracteres de
pesquisa, o que faz com que o método String.IndexOf(String, Int32) lance uma exceção
ArgumentNullException. Essa exceção é manipulada pelo método FindOccurrences e
passada de volta para o chamador. Como a instrução throw é usada sem expressão, a
saída do exemplo mostra que a pilha de chamadas está preservada.

C#

public class RethrowEx1


{
public static void Main()
{
String s = "It was a cold day when...";
int[] indexes = s.FindOccurrences("a");
ShowOccurrences(s, "a", indexes);
Console.WriteLine();

String toFind = null;


try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine("Message:\n {0}\n", e.Message);
Console.WriteLine("Stack Trace:\n {0}\n", e.StackTrace);
}
}

private static void ShowOccurrences(String s, String toFind, int[]


indexes)
{
Console.Write("'{0}' occurs at the following character positions: ",
toFind);
for (int ctr = 0; ctr < indexes.Length; ctr++)
Console.Write("{0}{1}", indexes[ctr],
ctr == indexes.Length - 1 ? "" : ", ");

Console.WriteLine();
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message:
// Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32
count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)
// at Example.Main()

Por outro lado, se a exceção for lançada novamente usando esta instrução:

C#

throw e;

...então a pilha de chamadas completa não é preservada e o exemplo geraria a seguinte


saída:

Saída

'a' occurs at the following character positions: 4, 7, 15

An exception (ArgumentNullException) occurred.


Message:
Value cannot be null.
Parameter name: value

Stack Trace:
at Library.FindOccurrences(String s, String f)
at Example.Main()

Uma alternativa um pouco mais complicada é lançar uma nova exceção e preservar as
informações da pilha de chamadas da exceção original em uma exceção interna. O
chamador pode usar a propriedade InnerException da nova exceção para recuperar o
registro de ativação e outras informações sobre a exceção original. Nesse caso, a
instrução throw é:

C#

throw new ArgumentNullException("You must supply a search string.", e);

O código de usuário que manipula a exceção precisa saber que a propriedade


InnerException contém informações sobre a exceção original, como demonstra o
manipulador de exceção a seguir.

C#

try
{
indexes = s.FindOccurrences(toFind);
ShowOccurrences(s, toFind, indexes);
}
catch (ArgumentNullException e)
{
Console.WriteLine("An exception ({0}) occurred.",
e.GetType().Name);
Console.WriteLine(" Message:\n{0}", e.Message);
Console.WriteLine(" Stack Trace:\n {0}", e.StackTrace);
Exception ie = e.InnerException;
if (ie != null)
{
Console.WriteLine(" The Inner Exception:");
Console.WriteLine(" Exception Name: {0}", ie.GetType().Name);
Console.WriteLine(" Message: {0}\n", ie.Message);
Console.WriteLine(" Stack Trace:\n {0}\n", ie.StackTrace);
}
}
// The example displays the following output:
// 'a' occurs at the following character positions: 4, 7, 15
//
// An exception (ArgumentNullException) occurred.
// Message: You must supply a search string.
//
// Stack Trace:
// at Library.FindOccurrences(String s, String f)
// at Example.Main()
//
// The Inner Exception:
// Exception Name: ArgumentNullException
// Message: Value cannot be null.
// Parameter name: value
//
// Stack Trace:
// at System.String.IndexOf(String value, Int32 startIndex, Int32
count, Stri
// ngComparison comparisonType)
// at Library.FindOccurrences(String s, String f)

Esc exceções padrão


Quando você precisa lançar uma exceção, você geralmente pode usar um tipo de
exceção existente em .NET em vez de implementar uma exceção personalizada. Você
deve usar um tipo de exceção padrão nestas duas condições:
Você está lançando uma exceção causada por um erro de uso (ou seja, por um
erro na lógica do programa feito pelo desenvolvedor que está chamando seu
método). Normalmente, você lançaria uma exceção como ArgumentException,
ArgumentNullException, InvalidOperationException ou NotSupportedException. A
cadeia de caracteres fornecida ao construtor do objeto de exceção ao instanciar o
objeto de exceção deve descrever o erro para que o desenvolvedor possa corrigi-
lo. Para obter mais informações, consulte a propriedade Message.

Você está manipulando um erro que pode ser comunicado ao chamador com uma
exceção .NET existente. Você deve lançar a exceção mais derivada possível. Por
exemplo, se um método exigir que um argumento seja um membro válido de um
tipo de enumeração, você deve lançar um InvalidEnumArgumentException (a
classe mais derivada) em vez de um ArgumentException.

A tabela a seguir lista os tipos de exceção comuns e as condições sob as quais você as
lançaria.

ノ Expandir a tabela

Exceção Condição

ArgumentException Um argumento não nulo que é passado para um método é


inválido.

ArgumentNullException Um argumento que é passado para um método é null .

ArgumentOutOfRangeException Um argumento está fora do intervalo de valores válidos.

DirectoryNotFoundException Parte de um caminho de diretório não é válida.

DivideByZeroException O denominador em uma operação de divisão Decimal ou


inteira é zero.

DriveNotFoundException Uma unidade está indisponível ou é inexistente.

FileNotFoundException Um arquivo não existe.

FormatException Um valor não está em um formato apropriado para ser


convertido de uma cadeia de caracteres por um método de
conversão como Parse .

IndexOutOfRangeException Um índice está fora dos limites de uma matriz ou coleção.

InvalidOperationException Uma chamada de método é inválida no estado atual de um


objeto.

KeyNotFoundException A chave especificada para acessar um membro em uma


coleção não pode ser encontrada.
Exceção Condição

NotImplementedException Um método ou operação não foi implementado.

NotSupportedException Não há suporte para um método ou operação.

ObjectDisposedException Uma operação é executada em um objeto que foi descartado.

OverflowException Uma operação aritmética, de coerção ou de conversão resulta


em um estouro.

PathTooLongException Um caminho ou nome de arquivo excede o comprimento


máximo definido pelo sistema.

PlatformNotSupportedException Não há suporte para a operação na plataforma atual.

RankException Uma matriz com o número errado de dimensões é passada


para um método.

TimeoutException O intervalo de tempo alocado para uma operação expirou.

UriFormatException Um URI (Uniform Resource Identifier) inválido está sendo


usado.

Implementar exceções personalizadas


Nos seguintes casos, usar uma exceção .NET existente para manipular uma condição de
erro não é adequado:

Quando a exceção reflete um erro de programa exclusivo que não pode ser
mapeado para uma exceção .NET existente.

Quando a exceção requer manipulação diferente da manipulação apropriada para


uma exceção .NET existente, ou a exceção deve ser desambiguada de uma exceção
semelhante. Por exemplo, se você lançar uma exceção
ArgumentOutOfRangeException ao analisar a representação numérica de uma
cadeia de caracteres que está fora do intervalo do tipo integral de destino, você
não vai querer usar a mesma exceção para um erro que resulta do chamador não
fornecer os valores restritos apropriados ao chamar o método.

A classe Exception é a classe base para todas as exceções em .NET. Muitas classes
derivadas dependem do comportamento herdado dos membros da classe Exception,
elas não substituem os membros da Exception, nem definem nenhum membro único.

Para definir sua própria classe de exceção:


1. Defina uma classe que herda de Exception. Se necessário, defina os membros
exclusivos necessários para sua classe para fornecer informações adicionais sobre a
exceção. Por exemplo, a classe ArgumentException inclui uma propriedade
ParamName que especifica o nome do parâmetro cujo argumento causou a
exceção, e a propriedade RegexMatchTimeoutException inclui uma propriedade
MatchTimeout que indica o intervalo de tempo limite.

2. Se necessário, substitua os membros herdados cuja funcionalidade você deseja


alterar ou modificar. Observe que a maioria das classes derivadas existentes de
Exception não substituem o comportamento dos membros herdados.

3. Determine se o objeto de exceção personalizado é serializável. A serialização


permite que você salve informações sobre a exceção e permite que as informações
de exceção sejam compartilhadas por um servidor e um proxy de cliente em um
contexto remoto. Para tornar o objeto de exceção serializável, marque-o com o
atributo SerializableAttribute.

4. Defina os construtores de sua classe de exceção. Normalmente, as classes de


exceção têm um ou mais dos seguintes construtores:

Exception(), que usa valores padrão para inicializar as propriedades de um


novo objeto de exceção.

Exception(String), que inicializa um novo objeto de exceção com uma


mensagem de erro especificada.

Exception(String, Exception), que inicializa um novo objeto de exceção com


uma mensagem de erro especificada e a exceção interna.

Exception(SerializationInfo, StreamingContext), que é um construtor


protected que inicializa um novo objeto de exceção a partir de dados

serializados. Você deve implementar esse construtor se tiver optado por


tornar seu objeto de exceção serializável.

O exemplo a seguir ilustra o uso de uma classe de exceção personalizada. Ele define
uma exceção NotPrimeException que é lançada quando um cliente tenta recuperar uma
sequência de números primos especificando um número inicial que não é primo. A
exceção define uma nova propriedade, NonPrime , que retorna o número não primo que
causou a exceção. Além de implementar um construtor protegido sem parâmetros e um
construtor com parâmetros SerializationInfo e StreamingContext para serialização, a
classe NotPrimeException define três construtores adicionais para dar suporte à
propriedade NonPrime . Cada construtor chama um construtor de classe base, além de
preservar o valor do número não primo. A classe NotPrimeException também é marcada
com o atributo SerializableAttribute.

C#

using System;
using System.Runtime.Serialization;

[Serializable()]
public class NotPrimeException : Exception
{
private int notAPrime;

protected NotPrimeException()
: base()
{ }

public NotPrimeException(int value) :


base(String.Format("{0} is not a prime number.", value))
{
notAPrime = value;
}

public NotPrimeException(int value, string message)


: base(message)
{
notAPrime = value;
}

public NotPrimeException(int value, string message, Exception


innerException) :
base(message, innerException)
{
notAPrime = value;
}

protected NotPrimeException(SerializationInfo info,


StreamingContext context)
: base(info, context)
{ }

public int NonPrime


{ get { return notAPrime; } }
}

A classe PrimeNumberGenerator mostrada no exemplo a seguir usa o Crivo de


Eratóstenes para calcular a sequência de números primos de 2 a um limite especificado
pelo cliente na chamada para seu construtor de classe. O método GetPrimesFrom
retorna todos os números primos que são maiores ou iguais a um limite inferior
especificado, mas lança um NotPrimeException se esse limite inferior não for um
número primo.

C#

using System;
using System.Collections.Generic;

[Serializable]
public class PrimeNumberGenerator
{
private const int START = 2;
private int maxUpperBound = 10000000;
private int upperBound;
private bool[] primeTable;
private List<int> primes = new List<int>();

public PrimeNumberGenerator(int upperBound)


{
if (upperBound > maxUpperBound)
{
string message = String.Format(
"{0} exceeds the maximum upper bound of {1}.",
upperBound, maxUpperBound);
throw new ArgumentOutOfRangeException(message);
}
this.upperBound = upperBound;
// Create array and mark 0, 1 as not prime (True).
primeTable = new bool[upperBound + 1];
primeTable[0] = true;
primeTable[1] = true;

// Use Sieve of Eratosthenes to determine prime numbers.


for (int ctr = START; ctr <= (int)Math.Ceiling(Math.Sqrt(upperBound));
ctr++)
{
if (primeTable[ctr]) continue;

for (int multiplier = ctr; multiplier <= upperBound / ctr;


multiplier++)
if (ctr * multiplier <= upperBound) primeTable[ctr * multiplier]
= true;
}
// Populate array with prime number information.
int index = START;
while (index != -1)
{
index = Array.FindIndex(primeTable, index, (flag) => !flag);
if (index >= 1)
{
primes.Add(index);
index++;
}
}
}

public int[] GetAllPrimes()


{
return primes.ToArray();
}

public int[] GetPrimesFrom(int prime)


{
int start = primes.FindIndex((value) => value == prime);
if (start < 0)
throw new NotPrimeException(prime, String.Format("{0} is not a
prime number.", prime));
else
return primes.FindAll((value) => value >= prime).ToArray();
}
}

O exemplo a seguir faz duas chamadas para o método GetPrimesFrom com números não
primos, um dos quais cruza os limites do domínio do aplicativo. Em ambos os casos, a
exceção é lançada e manipulada com êxito no código do cliente.

C#

using System;
using System.Reflection;

class Example1
{
public static void Main()
{
int limit = 10000000;
PrimeNumberGenerator primes = new PrimeNumberGenerator(limit);
int start = 1000001;
try
{
int[] values = primes.GetPrimesFrom(start);
Console.WriteLine("There are {0} prime numbers from {1} to {2}",
start, limit);
}
catch (NotPrimeException e)
{
Console.WriteLine("{0} is not prime", e.NonPrime);
Console.WriteLine(e);
Console.WriteLine("--------");
}

AppDomain domain = AppDomain.CreateDomain("Domain2");


PrimeNumberGenerator gen =
(PrimeNumberGenerator)domain.CreateInstanceAndUnwrap(
typeof(Example).Assembly.FullName,
"PrimeNumberGenerator", true,
BindingFlags.Default, null,
new object[] { 1000000 }, null,
null);
try
{
start = 100;
Console.WriteLine(gen.GetPrimesFrom(start));
}
catch (NotPrimeException e)
{
Console.WriteLine("{0} is not prime", e.NonPrime);
Console.WriteLine(e);
Console.WriteLine("--------");
}
}
}

Exemplos
O exemplo a seguir demonstra um bloco catch ( with em F#) que é definido para
manipular erros ArithmeticException. Esse bloco catch também detecta erros
DivideByZeroException, porque DivideByZeroException deriva de ArithmeticException e
não há nenhum bloco catch explicitamente definido para erros DivideByZeroException.

C#

using System;

class ExceptionTestClass
{
public static void Main()
{
int x = 0;
try
{
int y = 100 / x;
}
catch (ArithmeticException e)
{
Console.WriteLine($"ArithmeticException Handler: {e}");
}
catch (Exception e)
{
Console.WriteLine($"Generic Exception Handler: {e}");
}
}
}
/*
This code example produces the following results:
ArithmeticException Handler: System.DivideByZeroException: Attempted to
divide by zero.
at ExceptionTestClass.Main()

*/

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Propriedade System.Exception.Data
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Use o System.Collections.IDictionary objeto retornado pela Data propriedade para


armazenar e recuperar informações suplementares relevantes para a exceção. As
informações estão na forma de um número arbitrário de pares chave/valor definidos
pelo usuário. O componente-chave de cada par chave/valor é tipicamente uma cadeia
de caracteres de identificação, enquanto o componente de valor do par pode ser
qualquer tipo de objeto.

Segurança do par chave/valor


Os pares chave/valor armazenados na coleção retornada Data pela propriedade não são
seguros. Se o aplicativo chamar uma série aninhada de rotinas e cada rotina contiver
manipuladores de exceção, a pilha de chamadas resultante conterá uma hierarquia
desses manipuladores de exceção. Se uma rotina de nível inferior lançar uma exceção,
qualquer manipulador de exceção de nível superior na hierarquia de pilha de chamadas
poderá ler e/ou modificar os pares chave/valor armazenados na coleção por qualquer
outro manipulador de exceção. Isso significa que você deve garantir que as informações
nos pares chave/valor não sejam confidenciais e que seu aplicativo funcione
corretamente se as informações nos pares chave/valor estiverem corrompidas.

Principais conflitos
Um conflito de chave ocorre quando diferentes manipuladores de exceção especificam
a mesma chave para acessar um par chave/valor. Tenha cuidado ao desenvolver seu
aplicativo porque a consequência de um conflito de chave é que os manipuladores de
exceção de nível inferior podem se comunicar inadvertidamente com manipuladores de
exceção de nível superior, e essa comunicação pode causar erros sutis de programa. No
entanto, se você for cauteloso, poderá usar conflitos de chave para aprimorar seu
aplicativo.

Evite conflitos de chave


Evite conflitos de chave adotando uma convenção de nomenclatura para gerar chaves
exclusivas para pares chave/valor. Por exemplo, uma convenção de nomenclatura pode
gerar uma chave que consiste no nome delimitado por período do seu aplicativo, o
método que fornece informações suplementares para o par e um identificador
exclusivo.

Suponha que dois aplicativos, chamados Produtos e Fornecedores, cada um tenha um


método chamado Vendas. O método Sales no aplicativo Produtos fornece o número de
identificação (a unidade de manutenção de estoque ou SKU) de um produto. O método
de vendas no aplicativo Fornecedores fornece o número de identificação, ou SID, de um
fornecedor. Consequentemente, a convenção de nomenclatura para este exemplo
produz as chaves, "Products.Sales.SKU" e "Suppliers.Sales.SID".

Explorar conflitos de chave


Explorar conflitos de chave usando a presença de uma ou mais chaves especiais e pré-
organizadas para controlar o processamento. Suponha que, em um cenário, o
manipulador de exceção de nível mais alto na hierarquia de pilha de chamadas capture
todas as exceções lançadas por manipuladores de exceção de nível inferior. Se existir
um par chave/valor com uma chave especial, o manipulador de exceção de alto nível
formatará os pares chave/valor restantes no IDictionary objeto de alguma maneira não
padrão, caso contrário, os pares chave/valor restantes serão formatados de alguma
maneira normal.

Agora suponha que, em outro cenário, o manipulador de exceção em cada nível da


hierarquia de pilha de chamadas capture a exceção lançada pelo próximo manipulador
de exceção de nível inferior. Além disso, cada manipulador de exceção sabe que a Data
coleção retornada pela propriedade contém um conjunto de pares chave/valor que
podem ser acessados com um conjunto preorganizado de chaves.

Cada manipulador de exceção usa o conjunto preorganizado de chaves para atualizar o


componente de valor do par chave/valor correspondente com informações exclusivas
desse manipulador de exceções. Depois que o processo de atualização for concluído, o
manipulador de exceção lançará a exceção para o próximo manipulador de exceção de
nível superior. Finalmente, o manipulador de exceção de nível mais alto acessa os pares
chave/valor e exibe as informações de atualização consolidadas de todos os
manipuladores de exceção de nível inferior.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
Selecione um link para fornecer
comentários:
A fonte deste conteúdo pode
ser encontrada no GitHub, onde  Abrir um problema de
você também pode criar e documentação
revisar problemas e solicitações
de pull. Para obter mais  Fornecer comentários sobre o
informações, confira o nosso produto
guia para colaboradores.
Propriedade System.Exception.Message
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

As mensagens de erro têm como alvo o desenvolvedor que está manipulando a


exceção. O texto da Message propriedade deve descrever completamente o erro e,
quando possível, também deve explicar como corrigir o erro. Os manipuladores de
exceção de nível superior podem exibir a mensagem para os usuários finais, portanto,
você deve garantir que ela esteja gramaticalmente correta e que cada frase da
mensagem termine com um ponto. Não use pontos de interrogação ou pontos de
exclamação. Se seu aplicativo usa mensagens de exceção localizadas, você deve garantir
que elas sejam traduzidas com precisão.

) Importante

Não divulgue informações confidenciais em mensagens de exceção sem verificar as


permissões apropriadas.

O valor da Message propriedade é incluído nas informações retornadas pelo ToString. A


Message propriedade é definida somente ao criar um Exceptionarquivo . Se nenhuma
mensagem foi fornecida ao construtor para a instância atual, o sistema fornece uma
mensagem padrão que é formatada usando a cultura do sistema atual.

Exemplos
O exemplo de código a seguir lança e, em seguida, captura uma Exception exceção e
exibe a mensagem de texto da exceção usando a Message propriedade.

C#

using System;

namespace NDP_UE_CS
{
// Derive an exception; the constructor sets the HelpLink and
// Source properties.
class LogTableOverflowException : Exception
{
const string overflowMessage = "The log table has overflowed.";

public LogTableOverflowException(
string auxMessage, Exception inner) :
base(String.Format("{0} - {1}",
overflowMessage, auxMessage), inner)
{
this.HelpLink = "https://learn.microsoft.com";
this.Source = "Exception_Class_Samples";
}
}

class LogTable
{
public LogTable(int numElements)
{
logArea = new string[numElements];
elemInUse = 0;
}

protected string[] logArea;


protected int elemInUse;

// The AddRecord method throws a derived exception if


// the array bounds exception is caught.
public int AddRecord(string newRecord)
{
try
{
logArea[elemInUse] = newRecord;
return elemInUse++;
}
catch (Exception e)
{
throw new LogTableOverflowException(
String.Format("Record \"{0}\" was not logged.",
newRecord), e);
}
}
}

class OverflowDemo
{
// Create a log table and force an overflow.
public static void Main()
{
LogTable log = new LogTable(4);

Console.WriteLine(
"This example of \n Exception.Message, \n" +
" Exception.HelpLink, \n Exception.Source, \n" +
" Exception.StackTrace, and \n Exception." +
"TargetSite \ngenerates the following output.");

try
{
for (int count = 1; ; count++)
{
log.AddRecord(
String.Format(
"Log record number {0}", count));
}
}
catch (Exception ex)
{
Console.WriteLine("\nMessage ---\n{0}", ex.Message);
Console.WriteLine(
"\nHelpLink ---\n{0}", ex.HelpLink);
Console.WriteLine("\nSource ---\n{0}", ex.Source);
Console.WriteLine(
"\nStackTrace ---\n{0}", ex.StackTrace);
Console.WriteLine(
"\nTargetSite ---\n{0}", ex.TargetSite);
}
}
}
}

/*
This example of
Exception.Message,
Exception.HelpLink,
Exception.Source,
Exception.StackTrace, and
Exception.TargetSite
generates the following output.

Message ---
The log table has overflowed. - Record "Log record number 5" was not logged.

HelpLink ---
https://learn.microsoft.com

Source ---
Exception_Class_Samples

StackTrace ---
at NDP_UE_CS.LogTable.AddRecord(String newRecord)
at NDP_UE_CS.OverflowDemo.Main()

TargetSite ---
Int32 AddRecord(System.String)
*/

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
 Abrir um problema de
revisar problemas e solicitações
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Classe System.InvalidCastException
Artigo • 09/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O .NET dá suporte à conversão automática de tipos derivados para seus tipos base e de
volta para o tipo derivado, bem como de tipos que apresentam interfaces para objetos
de interface e vice-versa. Ele também inclui uma variedade de mecanismos que dão
suporte a conversões personalizadas. Para obter mais informações, confira Conversão de
tipo no .NET.

Uma exceção InvalidCastException é lançada quando a conversão de uma instância de


um tipo para outro tipo não é compatível. Por exemplo, tentar converter um valor Char
em um valor DateTime gera uma exceção InvalidCastException. Ela é diferente de uma
exceção OverflowException, que é gerada quando uma conversão de um tipo para
outro é compatível, mas o valor do tipo de origem está fora do intervalo do tipo de
destino. Uma exceção InvalidCastException é causada por erro do desenvolvedor e não
deve ser manipulada em um bloco try/catch . Em vez disso, a causa da exceção deve
ser eliminada.

Para obter informações sobre conversões compatíveis com o sistema, consulte a classe
Convert. Para erros que ocorrem quando o tipo de destino pode armazenar valores de
tipo de origem, mas não é grande o suficiente para armazenar um valor de origem
específico, consulte a exceção OverflowException.

7 Observação

Em muitos casos, o compilador de linguagem detecta que não existe conversão


entre o tipo de origem e o tipo de destino e emite um erro de compilador.

Algumas das condições sob as quais uma tentativa de conversão gera uma exceção
InvalidCastException são discutidas nas seções a seguir.

Para que uma conversão de referência explícita seja bem-sucedida, o valor de origem
precisará ser null , ou o tipo de objeto referenciado pelo argumento de origem
precisará ser convertido no tipo de destino por uma conversão de referência implícita.

As seguintes instruções de IL (linguagem intermediária) geram uma exceção


InvalidCastException:
castclass
refanyval

unbox

InvalidCastException usa o HRESULT COR_E_INVALIDCAST , que tem o valor 0x80004002.

Para obter uma lista de valores de propriedade inicial para uma instância do
InvalidCastException, consulte o InvalidCastException construtores.

Tipos primitivos e IConvertible


Você chama direta ou indiretamente a implementação IConvertible de um tipo primitivo
que não dá suporte a uma conversão específica. Por exemplo, tentar converter um valor
Boolean em um Char ou um valor DateTime em um Int32 gera uma exceção
InvalidCastException. O exemplo a seguir chama os métodos
Boolean.IConvertible.ToChar e Convert.ToChar(Boolean) para converter um valor
Boolean em um Char. Em ambos os casos, a chamada de método gera uma exceção
InvalidCastException.

C#

using System;

public class IConvertibleEx


{
public static void Main()
{
bool flag = true;
try
{
IConvertible conv = flag;
Char ch = conv.ToChar(null);
Console.WriteLine("Conversion succeeded.");
}
catch (InvalidCastException)
{
Console.WriteLine("Cannot convert a Boolean to a Char.");
}

try
{
Char ch = Convert.ToChar(flag);
Console.WriteLine("Conversion succeeded.");
}
catch (InvalidCastException)
{
Console.WriteLine("Cannot convert a Boolean to a Char.");
}
}
}
// The example displays the following output:
// Cannot convert a Boolean to a Char.
// Cannot convert a Boolean to a Char.

Como a conversão não é compatível, não há nenhuma solução alternativa.

O método Convert.ChangeType
Você chamou o método Convert.ChangeType para converter um objeto de um tipo para
outro, mas um ou ambos os tipos não implementam a interface IConvertible.

Na maioria dos casos, como a conversão não é compatível, não há nenhuma solução
alternativa. Em alguns casos, uma solução alternativa possível é atribuir manualmente
valores de propriedade do tipo de origem a propriedades semelhantes de um tipo de
destino.

Estreitando conversões e implementações


IConvertible
Os operadores de estreitamento definem as conversões explícitas compatíveis com um
tipo. Um operador de conversão em C# ou o método de conversão CType no Visual
Basic (se Option Strict está ativado) é necessário para executar a conversão.

No entanto, se nem o tipo de origem nem o tipo de destino definirem uma conversão
explícita ou de estreitamento entre os dois tipos, e a implementação IConvertible de um
ou ambos os tipos não der suporte a uma conversão do tipo de origem para o tipo de
destino, uma exceção InvalidCastException será gerada.

Na maioria dos casos, como a conversão não é compatível, não há nenhuma solução
alternativa.

Downcasting
Você está fazendo downcasting, ou seja, tentando converter uma instância de um tipo
base em um dos respectivos tipos derivados. No exemplo a seguir, a tentativa de
converter um objeto Person em um objeto PersonWithID falha.

C#
using System;

public class Person


{
String _name;

public String Name


{
get { return _name; }
set { _name = value; }
}
}

public class PersonWithId : Person


{
String _id;

public string Id
{
get { return _id; }
set { _id = value; }
}
}

public class Example


{
public static void Main()
{
Person p = new Person();
p.Name = "John";
try {
PersonWithId pid = (PersonWithId) p;
Console.WriteLine("Conversion succeeded.");
}
catch (InvalidCastException) {
Console.WriteLine("Conversion failed.");
}

PersonWithId pid1 = new PersonWithId();


pid1.Name = "John";
pid1.Id = "246";
Person p1 = pid1;
try {
PersonWithId pid1a = (PersonWithId) p1;
Console.WriteLine("Conversion succeeded.");
}
catch (InvalidCastException) {
Console.WriteLine("Conversion failed.");
}

Person p2 = null;
try {
PersonWithId pid2 = (PersonWithId) p2;
Console.WriteLine("Conversion succeeded.");
}
catch (InvalidCastException) {
Console.WriteLine("Conversion failed.");
}
}
}
// The example displays the following output:
// Conversion failed.
// Conversion succeeded.
// Conversion succeeded.

Como mostra o exemplo, o downcast será bem-sucedido somente se o objeto Person


tiver sido criado por um upcast de um objeto PersonWithId para um objeto Person , ou
se o objeto Person for null .

Conversão de um objeto de interface


Você está tentando converter um objeto de interface em um tipo que implementa essa
interface, mas o tipo de destino não é o mesmo tipo ou uma classe base do tipo do qual
o objeto de interface foi originalmente derivado. O exemplo a seguir gera uma exceção
InvalidCastException quando tenta converter um objeto IFormatProvider em um objeto
DateTimeFormatInfo. A conversão falha porque, embora a classe DateTimeFormatInfo
implemente a interface IFormatProvider, o objeto DateTimeFormatInfo não está
relacionado à classe CultureInfo da qual o objeto de interface foi derivado.

C#

using System;
using System.Globalization;

public class InterfaceEx


{
public static void Main()
{
var culture = CultureInfo.InvariantCulture;
IFormatProvider provider = culture;

DateTimeFormatInfo dt = (DateTimeFormatInfo)provider;
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidCastException:
// Unable to cast object of type //System.Globalization.CultureInfo//
to
// type //System.Globalization.DateTimeFormatInfo//.
// at Example.Main()
Como a mensagem de exceção indica, a conversão será bem-sucedida somente se o
objeto de interface for convertido novamente em uma instância do tipo original, neste
caso, um arquivo CultureInfo. A conversão também será bem-sucedida se o objeto de
interface for convertido em uma instância de um tipo base do tipo original.

Conversões de cadeia de caracteres


Você está tentando converter um valor ou um objeto na representação de cadeia de
caracteres dele usando um operador de conversão em C#. No exemplo a seguir, a
tentativa de converter um valor Char em uma cadeia de caracteres e a tentativa de
converter um inteiro em uma cadeia de caracteres geram uma exceção
InvalidCastException.

C#

public class StringEx


{
public static void Main()
{
object value = 12;
// Cast throws an InvalidCastException exception.
string s = (string)value;
}
}

7 Observação

O uso do operador CStr do Visual Basic para converter um valor de um tipo


primitivo em uma cadeia de caracteres é bem-sucedido. A operação não gera uma
exceção InvalidCastException.

Para converter com êxito uma instância de qualquer tipo na representação de cadeia de
caracteres dela, chame o método ToString dela, como ocorre no exemplo a seguir. O
método ToString está sempre presente, uma vez que o método ToString é definido
pela classe Object e, portanto, é herdado ou substituído por todos os tipos gerenciados.

C#

using System;

public class ToStringEx2


{
public static void Main()
{
object value = 12;
string s = value.ToString();
Console.WriteLine(s);
}
}
// The example displays the following output:
// 12

Migração do Visual Basic 6.0


Você está atualizando um aplicativo do Visual Basic 6.0 com uma chamada para um
evento personalizado em um controle de usuário para o .NET do Visual Basic, e uma
exceção InvalidCastException é gerada com a mensagem: "A conversão especificada não
é válida". Para eliminar essa exceção, altere a linha de código em seu formulário (como
Form1 )

VB

Call UserControl11_MyCustomEvent(UserControl11, New


UserControl1.MyCustomEventEventArgs(5))

e substitua-a pela seguinte linha de código:

VB

Call UserControl11_MyCustomEvent(UserControl11(0), New


UserControl1.MyCustomEventEventArgs(5))

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Classe
System.InvalidOperationException
Artigo • 05/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

InvalidOperationException é utilizado nos casos em que a falha na invocação de um


método é causada por outros motivos diferentes de argumentos inválidos.
Normalmente, ele é gerado quando o estado de um objeto não pode dar suporte a
chamada do método. Por exemplo, uma exceção InvalidOperationException é gerada
por métodos como:

IEnumerator.MoveNext se os objetos de uma coleção forem modificados depois


que o enumerador for criado. Para obter mais informações, confira Alterando uma
coleção ao iterá-la.
ResourceSet.GetString se o conjunto de recursos for fechado antes de a chamada
de método ser feita.
XContainer.Add, se o objeto ou objetos a serem adicionados resultarem em um
documento XML estruturado incorretamente.
Um método que tenta manipular a interface do usuário a partir de um thread que
não é o thread principal ou da interface do usuário.

) Importante

Como a exceção InvalidOperationException pode ser gerada em uma grande


variedade de circunstâncias, é importante ler a mensagem de exceção retornada
pela propriedade Message.

InvalidOperationException usa o HRESULT COR_E_INVALIDOPERATION , que tem o valor


0x80131509.

Para obter uma lista de valores de propriedade inicial para uma instância do
InvalidOperationException, consulte o InvalidOperationException construtores.

Causas comuns de exceções


InvalidOperationException
As seções a seguir mostram como alguns casos comuns em que a exceção
InvalidOperationException é gerada em um aplicativo. O modo como você lida com o
problema depende da situação específica. No entanto, o mais comum é que a exceção
resulte de erro do desenvolvedor, e a exceção InvalidOperationException pode ser
antecipada e evitada.

Atualização de um thread da interface do usuário a partir


de um thread que não é da interface do usuário
Muitas vezes, os threads de trabalho são usados para executar algum trabalho em
segundo plano que envolve a coleta de dados a serem exibidos na interface de usuário
de um aplicativo. Porém, A maioria das estruturas de aplicativos de GUI (interface
gráfica do usuário) para .NET, como Windows Forms e Windows Presentation
Foundation (WPF), permite acessar objetos de GUI somente a partir do thread que cria e
gerencia a interface do usuário (o thread principal ou da interface do usuário). Um
InvalidOperationException é gerado quando você tenta acessar um elemento da
interface do usuário a partir de um thread que não seja o thread da interface do usuário.
O texto da mensagem de exceção é mostrado na tabela a seguir.

ノ Expandir a tabela

Tipo de aplicativo Mensagem

Aplicativo WPF O thread de chamada não pode acessar esse objeto porque ele
pertence a outro thread.

Aplicativo UWP O aplicativo chamou uma interface que foi organizada para um thread
diferente.

Aplicativo A operação entre threads não é válida: controle 'TextBox1' acessado de


Windows Forms um thread diferente do thread em que foi criado.

As estruturas de interface do usuário do .NET implementam um padrão dispatcher que


inclui um método para verificar se uma chamada para um membro de um elemento da
interface do usuário está sendo executada no thread da interface do usuário e outros
métodos para agendar a chamada no thread da interface do usuário:

Em aplicativos WPF, chame o método Dispatcher.CheckAccess para determinar se


um método está sendo executado em um thread que não seja da interface do
usuário. Ele retorna true se o método estiver sendo executado no thread da
interface do usuário e false caso contrário. Chame uma das sobrecargas
do método Dispatcher.Invoke para agendar a chamada no thread da interface do
usuário.
Em aplicativos UWP, verifique a propriedade CoreDispatcher.HasThreadAccess
para determinar se um método está sendo executado em um thread que não seja
da interface do usuário. Chame o método CoreDispatcher.RunAsync para executar
um delegado que atualiza o thread da interface do usuário.
Em aplicativos Windows Forms, use a propriedade Control.InvokeRequired para
determinar se um método está sendo executado em um thread que não seja da
interface do usuário. Chame uma das sobrecargas do método Control.Invoke para
executar um delegado que atualiza o thread da interface do usuário.

Os exemplos a seguir ilustram a exceção InvalidOperationException que é gerada


quando você tenta atualizar um elemento da interface do usuário em um thread
diferente do thread que o criou. Cada exemplo exige que você crie dois controles:

Um controle de caixa de texto chamado textBox1 . Em um aplicativo Windows


Forms, você deve definir sua propriedade Multiline como true .
Um controle de botão chamado threadExampleBtn . O exemplo fornece um
manipulador, ThreadsExampleBtn_Click , para o evento Click do botão.

Em cada caso, o manipulador de eventos threadExampleBtn_Click chama o método


DoSomeWork duas vezes. A primeira chamada é executada de forma síncrona e é bem-

sucedida. Mas a segunda chamada, por ser executada de forma assíncrona em um


thread do pool de threads, tenta atualizar a interface do usuário a partir de um thread
que não é da interface do usuário. Isso resulta em uma exceção
InvalidOperationException.

Aplicativos WPF

C#

private async void threadExampleBtn_Click(object sender, RoutedEventArgs e)


{
textBox1.Text = String.Empty;

textBox1.Text = "Simulating work on UI thread.\n";


DoSomeWork(20);
textBox1.Text += "Work completed...\n";

textBox1.Text += "Simulating work on non-UI thread.\n";


await Task.Run(() => DoSomeWork(1000));
textBox1.Text += "Work completed...\n";
}

private async void DoSomeWork(int milliseconds)


{
// Simulate work.
await Task.Delay(milliseconds);
// Report completion.
var msg = String.Format("Some work completed in {0} ms.\n",
milliseconds);
textBox1.Text += msg;
}

A versão a seguir do método DoSomeWork elimina a exceção em um aplicativo WPF.

C#

private async void DoSomeWork(int milliseconds)


{
// Simulate work.
await Task.Delay(milliseconds);

// Report completion.
bool uiAccess = textBox1.Dispatcher.CheckAccess();
String msg = String.Format("Some work completed in {0} ms. on {1}UI
thread\n",
milliseconds, uiAccess ? String.Empty : "non-
");
if (uiAccess)
textBox1.Text += msg;
else
textBox1.Dispatcher.Invoke(() => { textBox1.Text += msg; });
}

Aplicativos do Windows Forms

C#

List<String> lines = new List<String>();

private async void threadExampleBtn_Click(object sender, EventArgs e)


{
textBox1.Text = String.Empty;
lines.Clear();

lines.Add("Simulating work on UI thread.");


textBox1.Lines = lines.ToArray();
DoSomeWork(20);

lines.Add("Simulating work on non-UI thread.");


textBox1.Lines = lines.ToArray();
await Task.Run(() => DoSomeWork(1000));

lines.Add("ThreadsExampleBtn_Click completes. ");


textBox1.Lines = lines.ToArray();
}
private async void DoSomeWork(int milliseconds)
{
// simulate work
await Task.Delay(milliseconds);

// report completion
lines.Add(String.Format("Some work completed in {0} ms on UI thread.",
milliseconds));
textBox1.Lines = lines.ToArray();
}

A versão a seguir do método DoSomeWork elimina a exceção em um aplicativo Windows


Forms.

C#

private async void DoSomeWork(int milliseconds)


{
// simulate work
await Task.Delay(milliseconds);

// Report completion.
bool uiMarshal = textBox1.InvokeRequired;
String msg = String.Format("Some work completed in {0} ms. on {1}UI
thread\n",
milliseconds, uiMarshal ? String.Empty :
"non-");
lines.Add(msg);

if (uiMarshal) {
textBox1.Invoke(new Action(() => { textBox1.Lines = lines.ToArray();
}));
}
else {
textBox1.Lines = lines.ToArray();
}
}

Alteração de uma coleção durante a iteração


A instrução foreach em C#, for...in em F# ou For Each no Visual Basic é usada para
iterar os membros de uma coleção e para ler ou modificar seus elementos individuais.
No entanto, ele não pode ser utilizado para adicionar ou remover itens da coleção. Isso
gera uma exceção InvalidOperationException com uma mensagem semelhante a "A
coleção foi modificada; a operação de enumeração não pode ser executada."
O exemplo a seguir itera uma coleção de números inteiros e tenta adicionar o quadrado
de cada número inteiro à coleção. O exemplo gera um InvalidOperationException com a
primeira chamada ao método List<T>.Add.

C#

using System;
using System.Collections.Generic;

public class IteratingEx1


{
public static void Main()
{
var numbers = new List<int>() { 1, 2, 3, 4, 5 };
foreach (var number in numbers)
{
int square = (int)Math.Pow(number, 2);
Console.WriteLine("{0}^{1}", number, square);
Console.WriteLine("Adding {0} to the collection...\n", square);
numbers.Add(square);
}
}
}
// The example displays the following output:
// 1^1
// Adding 1 to the collection...
//
//
// Unhandled Exception: System.InvalidOperationException: Collection was
modified;
// enumeration operation may not execute.
// at
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource
resource)
// at System.Collections.Generic.List`1.Enumerator.MoveNextRare()
// at Example.Main()

Você pode eliminar a exceção de duas maneiras, dependendo da lógica do aplicativo:

Se for necessário adicionar elementos à coleção durante a iteração, você poderá


iterá-la por índice usando a instrução for ( for..to em F#) em vez de foreach ,
for...in ou For Each . O exemplo a seguir usa a instrução for para adicionar o

quadrado dos números da coleção à coleção.

C#

using System;
using System.Collections.Generic;

public class IteratingEx2


{
public static void Main()
{
var numbers = new List<int>() { 1, 2, 3, 4, 5 };

int upperBound = numbers.Count - 1;


for (int ctr = 0; ctr <= upperBound; ctr++)
{
int square = (int)Math.Pow(numbers[ctr], 2);
Console.WriteLine("{0}^{1}", numbers[ctr], square);
Console.WriteLine("Adding {0} to the collection...\n",
square);
numbers.Add(square);
}

Console.WriteLine("Elements now in the collection: ");


foreach (var number in numbers)
Console.Write("{0} ", number);
}
}
// The example displays the following output:
// 1^1
// Adding 1 to the collection...
//
// 2^4
// Adding 4 to the collection...
//
// 3^9
// Adding 9 to the collection...
//
// 4^16
// Adding 16 to the collection...
//
// 5^25
// Adding 25 to the collection...
//
// Elements now in the collection:
// 1 2 3 4 5 1 4 9 16 25

Observe que você deve estabelecer o número de iterações antes de iterar a


coleção usando um contador dentro do loop que sairá do loop adequadamente,
iterando para trás, de Count - 1 a 0 ou, como no exemplo, atribuindo o número de
elementos da matriz a uma variável e usando-a para estabelecer o limite superior
do loop. Caso contrário, se um elemento for adicionado à coleção em cada
iteração, o resultado será um loop infinito.

Se não for necessário adicionar elementos à coleção durante a iteração, você


poderá armazenar os elementos a serem adicionados em uma coleção temporária
que será adicionada quando a iteração da coleção for concluída. O exemplo a
seguir usa essa abordagem para adicionar o quadrado dos números em uma
coleção a uma coleção temporária e, em seguida, combinar as coleções em um
único objeto de matriz.

C#

using System;
using System.Collections.Generic;

public class IteratingEx3


{
public static void Main()
{
var numbers = new List<int>() { 1, 2, 3, 4, 5 };
var temp = new List<int>();

// Square each number and store it in a temporary collection.


foreach (var number in numbers)
{
int square = (int)Math.Pow(number, 2);
temp.Add(square);
}

// Combine the numbers into a single array.


int[] combined = new int[numbers.Count + temp.Count];
Array.Copy(numbers.ToArray(), 0, combined, 0, numbers.Count);
Array.Copy(temp.ToArray(), 0, combined, numbers.Count,
temp.Count);

// Iterate the array.


foreach (var value in combined)
Console.Write("{0} ", value);
}
}
// The example displays the following output:
// 1 2 3 4 5 1 4 9 16 25

Classificação de uma matriz ou coleção cujos objetos não


podem ser comparados
Os métodos de classificação de uso geral, como o método Array.Sort(Array) ou o
método List<T>.Sort(), geralmente exigem que pelo menos um dos objetos a serem
classificados implemente a interface IComparable<T> ou IComparable. Caso contrário, a
coleção ou matriz não poderá ser classificada, e o método gerará uma exceção
InvalidOperationException. O exemplo a seguir define uma classe Person , armazena
dois objetos Person em um objeto List<T> genérico e tenta classificá-los. Como mostra
a saída do exemplo, a chamada ao método List<T>.Sort() gera um
InvalidOperationException.
C#

using System;
using System.Collections.Generic;

public class Person1


{
public Person1(string fName, string lName)
{
FirstName = fName;
LastName = lName;
}

public string FirstName { get; set; }


public string LastName { get; set; }
}

public class ListSortEx1


{
public static void Main()
{
var people = new List<Person1>();

people.Add(new Person1("John", "Doe"));


people.Add(new Person1("Jane", "Doe"));
people.Sort();
foreach (var person in people)
Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException: Failed to
compare two elements in the array. --->
// System.ArgumentException: At least one object must implement
IComparable.
// at System.Collections.Comparer.Compare(Object a, Object b)
// at System.Collections.Generic.ArraySortHelper`1.SwapIfGreater(T[]
keys, IComparer`1 comparer, Int32 a, Int32 b)
// at
System.Collections.Generic.ArraySortHelper`1.DepthLimitedQuickSort(T[] keys,
Int32 left, Int32 right, IComparer`1 comparer, Int32 depthLimit)
// at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys,
Int32 index, Int32 length, IComparer`1 comparer)
// --- End of inner exception stack trace ---
// at System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys,
Int32 index, Int32 length, IComparer`1 comparer)
// at System.Array.Sort[T](T[] array, Int32 index, Int32 length,
IComparer`1 comparer)
// at System.Collections.Generic.List`1.Sort(Int32 index, Int32 count,
IComparer`1 comparer)
// at Example.Main()

Você pode eliminar a exceção de três maneiras:


Se você puder ser o proprietário do tipo que está tentando classificar (ou seja, se
controlar o código-fonte), poderá modificá-lo para implementar a interface
IComparable<T> ou IComparable. Isso exige que você implemente o método
IComparable<T>.CompareTo ou CompareTo. Acrescentar uma implementação de
interface a um tipo existente não é uma alteração interruptiva.

O exemplo a seguir usa essa abordagem para fornecer uma implementação


IComparable<T> para a classe Person . Você ainda pode chamar o método de
classificação geral da coleção ou matriz e, como mostra o resultado do exemplo, a
coleção é classificada com êxito.

C#

using System;
using System.Collections.Generic;

public class Person2 : IComparable<Person>


{
public Person2(String fName, String lName)
{
FirstName = fName;
LastName = lName;
}

public String FirstName { get; set; }


public String LastName { get; set; }

public int CompareTo(Person other)


{
return String.Format("{0} {1}", LastName, FirstName).
CompareTo(String.Format("{0} {1}", other.LastName,
other.FirstName));
}
}

public class ListSortEx2


{
public static void Main()
{
var people = new List<Person2>();

people.Add(new Person2("John", "Doe"));


people.Add(new Person2("Jane", "Doe"));
people.Sort();
foreach (var person in people)
Console.WriteLine("{0} {1}", person.FirstName,
person.LastName);
}
}
// The example displays the following output:
// Jane Doe
// John Doe

Se não for possível modificar o código-fonte do tipo que você está tentando
classificar, é possível definir uma classe de classificação para fins especiais que
implemente a interface IComparer<T>. Você pode chamar uma sobrecarga do
método Sort que inclui um parâmetro IComparer<T>. Essa abordagem é
especialmente útil se você quiser desenvolver uma classe de classificação
especializada que possa classificar objetos com base em vários critérios.

O exemplo a seguir usa a abordagem ao desenvolver uma classe PersonComparer


personalizada que é usada para classificar coleções Person . Em seguida, ele passa
uma instância dessa classe para o método List<T>.Sort(IComparer<T>).

C#

using System;
using System.Collections.Generic;

public class Person3


{
public Person3(String fName, String lName)
{
FirstName = fName;
LastName = lName;
}

public String FirstName { get; set; }


public String LastName { get; set; }
}

public class PersonComparer : IComparer<Person3>


{
public int Compare(Person3 x, Person3 y)
{
return String.Format("{0} {1}", x.LastName, x.FirstName).
CompareTo(String.Format("{0} {1}", y.LastName,
y.FirstName));
}
}

public class ListSortEx3


{
public static void Main()
{
var people = new List<Person3>();

people.Add(new Person3("John", "Doe"));


people.Add(new Person3("Jane", "Doe"));
people.Sort(new PersonComparer());
foreach (var person in people)
Console.WriteLine("{0} {1}", person.FirstName,
person.LastName);
}
}
// The example displays the following output:
// Jane Doe
// John Doe

Se não for possível modificar o código-fonte do tipo que está tentando classificar,
você poderá criar um delegado Comparison<T> para realizar a classificação. A
assinatura do delegado é

C#

int Comparison<T>(T x, T y)

O exemplo a seguir usa a abordagem definindo um método PersonComparison que


corresponde à assinatura do delegado Comparison<T>. Em seguida, ele passa
esse delegado para o método List<T>.Sort(Comparison<T>).

C#

using System;
using System.Collections.Generic;

public class Person


{
public Person(String fName, String lName)
{
FirstName = fName;
LastName = lName;
}

public String FirstName { get; set; }


public String LastName { get; set; }
}

public class ListSortEx4


{
public static void Main()
{
var people = new List<Person>();

people.Add(new Person("John", "Doe"));


people.Add(new Person("Jane", "Doe"));
people.Sort(PersonComparison);
foreach (var person in people)
Console.WriteLine("{0} {1}", person.FirstName,
person.LastName);
}

public static int PersonComparison(Person x, Person y)


{
return String.Format("{0} {1}", x.LastName, x.FirstName).
CompareTo(String.Format("{0} {1}", y.LastName,
y.FirstName));
}
}
// The example displays the following output:
// Jane Doe
// John Doe

Convertendo um Anulável<T> que é nulo em seu tipo


subjacente
A tentativa de converter um valor Nullable<T> que é null em seu tipo subjacente gera
uma exceção InvalidOperationException e exibe a mensagem de erro: "O objeto
anulável deve ter um valor.

O exemplo a seguir gera uma exceção InvalidOperationException quando tenta iterar


uma matriz que inclui um valor Nullable(Of Integer) .

C#

using System;
using System.Linq;

public class NullableEx1


{
public static void Main()
{
var queryResult = new int?[] { 1, 2, null, 4 };
var map = queryResult.Select(nullableInt => (int)nullableInt);

// Display list.
foreach (var num in map)
Console.Write("{0} ", num);
Console.WriteLine();
}
}
// The example displays the following output:
// 1 2
// Unhandled Exception: System.InvalidOperationException: Nullable object
must have a value.
// at
System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource
resource)
// at Example.<Main>b__0(Nullable`1 nullableInt)
// at System.Linq.Enumerable.WhereSelectArrayIterator`2.MoveNext()
// at Example.Main()

Para evitar a exceção:

Use a propriedade Nullable<T>.HasValue para selecionar apenas os elementos que


não sejam null .
Chame uma das sobrecargas Nullable<T>.GetValueOrDefault para fornecer um
valor padrão para um valor null .

O exemplo a seguir faz as duas coisas para evitar a exceção InvalidOperationException.

C#

using System;
using System.Linq;

public class NullableEx2


{
public static void Main()
{
var queryResult = new int?[] { 1, 2, null, 4 };
var numbers = queryResult.Select(nullableInt =>
(int)nullableInt.GetValueOrDefault());

// Display list using Nullable<int>.HasValue.


foreach (var number in numbers)
Console.Write("{0} ", number);
Console.WriteLine();

numbers = queryResult.Select(nullableInt => (int)


(nullableInt.HasValue ? nullableInt : -1));
// Display list using Nullable<int>.GetValueOrDefault.
foreach (var number in numbers)
Console.Write("{0} ", number);
Console.WriteLine();
}
}
// The example displays the following output:
// 1 2 0 4
// 1 2 -1 4

Chamar um método System.Linq.Enumerable em uma


coleção vazia
Os métodos Enumerable.Aggregate, Enumerable.Average, Enumerable.First,
Enumerable.Last, Enumerable.Max, Enumerable.Min, Enumerable.Single e
Enumerable.SingleOrDefault realizam operações em uma sequência e retornam um
único resultado. Algumas sobrecargas desses métodos geram uma exceção
InvalidOperationException quando a sequência está vazia, enquanto outras sobrecargas
retornam null . O método Enumerable.SingleOrDefault também gera uma exceção
InvalidOperationException quando a sequência contém mais de um elemento.

7 Observação

A maioria dos métodos que geram uma exceção InvalidOperationException são


sobrecargas. Certifique-se de entender o comportamento da sobrecarga que você
escolher.

A tabela a seguir lista as mensagens de exceção dos objetos de exceção


InvalidOperationException gerados por chamadas para alguns métodos
System.Linq.Enumerable.

ノ Expandir a tabela

Método Mensagem

Aggregate A sequência não contém elementos


Average
Last
Max
Min

First A sequência não contém nenhum elemento correspondente

Single A sequência contém mais de um elemento correspondente


SingleOrDefault

A forma como você elimina ou trata a exceção depende das suposições do aplicativo e
do método específico que você chama.

Quando você deliberadamente chama um desses métodos sem verificar se há uma


sequência vazia, você está assumindo que a sequência não está vazia e que uma
sequência vazia é uma ocorrência inesperada. Nesse caso, é adequado capturar ou
gerar novamente a exceção.

Se a falha na verificação de uma sequência vazia foi inadvertida, você poderá


chamar uma das sobrecargas da sobrecarga Enumerable.Any para determinar se
uma sequência contém algum elemento.

 Dica
Chamar o método Enumerable.Any<TSource>(IEnumerable<TSource>,
Func<TSource,Boolean>) antes de gerar uma sequência pode melhorar o
desempenho se os dados a serem processados contiverem um grande
número de elementos ou se a operação que gera a sequência for cara.

Se você chamou um método como Enumerable.First, Enumerable.Last ou


Enumerable.Single, poderá substituir por um método alternativo, como
Enumerable.FirstOrDefault, Enumerable.LastOrDefault ou
Enumerable.SingleOrDefault, que retornará um valor padrão em vez de um
membro da sequência.

Os exemplos fornecem detalhes adicionais.

O exemplo a seguir usa o método Enumerable.Average para calcular a média de uma


sequência cujos valores são maiores que 4. Como nenhum valor da matriz original
excede 4, nenhum valor será incluído na sequência e o método gerará uma exceção
InvalidOperationException.

C#

using System;
using System.Linq;

public class Example


{
public static void Main()
{
int[] data = { 1, 2, 3, 4 };
var average = data.Where(num => num > 4).Average();
Console.Write("The average of numbers greater than 4 is {0}",
average);
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException: Sequence
contains no elements
// at System.Linq.Enumerable.Average(IEnumerable`1 source)
// at Example.Main()

A exceção pode ser eliminada chamando o método Any para determinar se a sequência
contém algum elemento antes de chamar o método que processa a sequência, como
mostra o exemplo a seguir.

C#

using System;
using System.Linq;
public class EnumerableEx2
{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };
var moreThan4 = dbQueryResults.Where(num => num > 4);

if (moreThan4.Any())
Console.WriteLine("Average value of numbers greater than 4:
{0}:",
moreThan4.Average());
else
// handle empty collection
Console.WriteLine("The dataset has no values greater than 4.");
}
}
// The example displays the following output:
// The dataset has no values greater than 4.

O método Enumerable.First retorna o primeiro item de uma sequência ou o primeiro


elemento de uma sequência que satisfaz uma condição especificada. Se a sequência
estiver vazia e, portanto, não tiver um primeiro elemento, ele gerará uma exceção
InvalidOperationException.

No exemplo a seguir, o método Enumerable.First<TSource>(IEnumerable<TSource>,


Func<TSource,Boolean>) gera uma exceção InvalidOperationException porque a matriz
dbQueryResults não contém um elemento maior que 4.

C#

using System;
using System.Linq;

public class EnumerableEx3


{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };

var firstNum = dbQueryResults.First(n => n > 4);

Console.WriteLine("The first value greater than 4 is {0}",


firstNum);
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains no matching element
// at System.Linq.Enumerable.First[TSource](IEnumerable`1 source,
Func`2 predicate)
// at Example.Main()

Você pode chamar o método Enumerable.FirstOrDefault em vez de Enumerable.First


para retornar um valor especificado ou padrão. Se o método não encontrar um primeiro
elemento na sequência, ele retornará o valor padrão para esse tipo de dados. O valor
padrão é null para um tipo de referência, zero para um tipo de dados numéricos e
DateTime.MinValue para o tipo DateTime.

7 Observação

A interpretação do valor retornado pelo método Enumerable.FirstOrDefault


geralmente é complicada pelo fato de que o valor padrão do tipo pode ser um
valor válido na sequência. Nesse caso, você pode chamar o método
Enumerable.Any para determinar se a sequência tem membros válidos antes de
chamar o método Enumerable.First.

O exemplo a seguir chama o método Enumerable.FirstOrDefault<TSource>


(IEnumerable<TSource>, Func<TSource,Boolean>) para evitar a exceção
InvalidOperationException gerada no exemplo anterior.

C#

using System;
using System.Linq;

public class EnumerableEx4


{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };

var firstNum = dbQueryResults.FirstOrDefault(n => n > 4);

if (firstNum == 0)
Console.WriteLine("No value is greater than 4.");
else
Console.WriteLine("The first value greater than 4 is {0}",
firstNum);
}
}
// The example displays the following output:
// No value is greater than 4.
Chame Enumerable.Single ou
Enumerable.SingleOrDefault em uma sequência sem um
elemento
O método Enumerable.Single retorna o único elemento de uma sequência ou o único
elemento de uma sequência que atende a uma condição especificada. Se não houver
elementos na sequência ou se houver mais de um elemento, o método gerará uma
exceção InvalidOperationException.

Você pode usar o método Enumerable.SingleOrDefault para retornar um valor padrão


em vez de gerar uma exceção quando a sequência não contiver elementos. No entanto,
o método Enumerable.SingleOrDefault ainda gerará uma exceção
InvalidOperationException quando a sequência contiver mais de um elemento.

A tabela a seguir lista as mensagens de exceção dos objetos de exceção


InvalidOperationException gerados por chamadas aos métodos Enumerable.Single e
Enumerable.SingleOrDefault.

ノ Expandir a tabela

Método Mensagem

Single A sequência não contém nenhum elemento correspondente

Single A sequência contém mais de um elemento correspondente


SingleOrDefault

No exemplo a seguir, a chamada para o método Enumerable.Single gera uma exceção


InvalidOperationException porque a sequência não tem um elemento maior que 4.

C#

using System;
using System.Linq;

public class EnumerableEx5


{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };

var singleObject = dbQueryResults.Single(value => value > 4);

// Display results.
Console.WriteLine("{0} is the only value greater than 4",
singleObject);
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains no matching element
// at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source,
Func`2 predicate)
// at Example.Main()

O exemplo a seguir tenta evitar a exceção InvalidOperationException gerada quando


uma sequência está vazia, chamando o método Enumerable.SingleOrDefault. No
entanto, como essa sequência retorna vários elementos cujo valor é maior que 2, ela
também gera uma exceção InvalidOperationException.

C#

using System;
using System.Linq;

public class EnumerableEx6


{
public static void Main()
{
int[] dbQueryResults = { 1, 2, 3, 4 };

var singleObject = dbQueryResults.SingleOrDefault(value => value >


2);

if (singleObject != 0)
Console.WriteLine("{0} is the only value greater than 2",
singleObject);
else
// Handle an empty collection.
Console.WriteLine("No value is greater than 2");
}
}
// The example displays the following output:
// Unhandled Exception: System.InvalidOperationException:
// Sequence contains more than one matching element
// at System.Linq.Enumerable.SingleOrDefault[TSource](IEnumerable`1
source, Func`2 predicate)
// at Example.Main()

A chamada do método Enumerable.Single pressupõe que uma sequência ou a


sequência que atende aos critérios especificados contém apenas um elemento.
Enumerable.SingleOrDefault pressupõe uma sequência com zero ou um resultado, mas
não mais do que isso. Se essa suposição for deliberada de sua parte e essas condições
não forem atendidas, é apropriado gerar novamente ou capturar o
InvalidOperationException resultante. Caso contrário, ou se você espera que condições
inválidas ocorram com alguma frequência, deve considerar o uso de outro método
Enumerable, como FirstOrDefault ou Where.

Acesso dinâmico ao campo de domínio entre aplicativos


A instrução OpCodes.Ldflda Linguagem Intermediária da Microsoft (MSIL) gera uma
exceção InvalidOperationException se o objeto que contém o campo cujo endereço
você está tentando recuperar não estiver dentro do domínio do aplicativo no qual o
código está sendo executado. O endereço de um campo só pode ser acessado a partir
do domínio do aplicativo no qual ele reside.

Gerar uma exceção InvalidOperationException


Você deve gerar uma exceção InvalidOperationException somente quando o estado do
seu objeto, por algum motivo, não der suporte a uma chamada de método específica.
Ou seja, a chamada do método é válida em algumas circunstâncias ou contextos, mas é
inválida em outros.

Se a falha na invocação do método for devida a argumentos inválidos, então


ArgumentException ou uma de suas classes derivadas, ArgumentNullException ou
ArgumentOutOfRangeException, deverá ser gerada em seu lugar.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Classe
System.NotImplementedException
Artigo • 05/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A exceção NotImplementedException é gerada quando um determinado método,


acessador get ou acessador set está presente como membro de um tipo, mas não é
implementado.

NotImplementedException usa a implementação Object.Equals padrão, que oferece


suporte à igualdade de referência. Para obter uma lista de valores de propriedade para
uma instância do NotImplementedException, consulte os construtores
NotImplementedException.

Gerar a exceção
Você pode optar por gerar uma exceção NotImplementedException em propriedades
ou métodos em seus próprios tipos quando esse membro ainda estiver em
desenvolvimento e só for implementado no código de produção posteriormente. Em
outras palavras, uma exceção NotImplementedException deve ser sinônimo de "em
desenvolvimento".

Gerar a exceção
A exceção NotImplementedException indica que o método ou a propriedade que você
está tentando invocar não tem implementação e, portanto, não fornece nenhuma
funcionalidade. Como resultado, você não deve gerar esse erro em um bloco try/catch .
Em vez disso, você deve remover a invocação de membro do seu código. Você pode
incluir uma chamada para o membro quando ela for implementada na versão de
produção de uma biblioteca.

Em alguns casos, uma exceção NotImplementedException pode não ser usada para
indicar a funcionalidade que ainda está em desenvolvimento em uma biblioteca de pré-
produção. No entanto, isso ainda indica que a funcionalidade não está disponível e você
deve remover a invocação de membro do seu código.
NotImplementedException e outros tipos de
exceção
O .NET também inclui dois outros tipos de exceção NotSupportedException e
PlatformNotSupportedException, que indicam que não existe implementação para um
membro específico de um tipo. Você deve gerar um desses em vez de uma exceção
NotImplementedException sob as seguintes condições:

Gere uma exceção PlatformNotSupportedException em plataformas nas quais a


funcionalidade não é suportada se você tiver criado um tipo com um ou mais
membros que estão disponíveis em algumas plataformas ou versões, mas não em
outras.

Gere uma exceção NotSupportedException se a implementação de um membro da


interface ou uma substituição de um método de classe base abstrato não for
possível.

Por exemplo, o método Convert.ToInt32(DateTime) gera uma exceção


NotSupportedException porque não existe nenhuma conversão significativa entre
uma data e hora e um inteiro assinado de 32 bits. O método deve estar presente
nesse caso porque a classe Convert implementa a interface IConvertible.

Você também deve gerar uma exceção NotSupportedException se tiver implementado


uma classe base abstrata e adicionar um novo membro a ela que deve ser substituído
por classes derivadas. Nesse caso, tornar o membro abstrato faz com que as subclasses
existentes falhem ao carregar.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Classe System.NotSupportedException
Artigo • 07/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

NotSupportedException indica que não existe implementação para um método ou


propriedade invocada.

NotSupportedException usa o HRESULT COR_E_NOTSUPPORTED , que tem o valor


0x80131515.

Para obter uma lista de valores de propriedade inicial para uma instância do
NotSupportedException, consulte o NotSupportedException construtores.

Lançar uma exceção NotSupportedException


Você pode considerar gerar uma exceção NotSupportedException nos seguintes casos:

Você está implementando uma interface de uso geral, e vários dos métodos não
têm nenhuma implementação significativa. Por exemplo, se você estiver criando
um tipo de data e hora que implemente a interface IConvertible, gerará uma
exceção NotSupportedException para a maioria das conversões.

Você herdou de uma classe abstrata que exige a substituição de vários métodos.
No entanto, você só está preparado para fornecer uma implementação para um
subconjunto deles. Para os métodos que você decidir não implementar, você pode
optar por gerar um NotSupportedException.

Você está definindo um tipo de uso geral com um estado que permite operações
condicionalmente. Por exemplo, seu tipo pode ser somente leitura ou leitura e
gravação. Nesse caso:

Se o objeto for somente leitura, a tentativa de atribuir valores às propriedades


de uma instância ou de chamar métodos que modificam o estado da instância
deverá gerar uma exceção NotSupportedException.

Você deve implementar uma propriedade que retorne um valor Boolean que
indique se uma determinada funcionalidade está disponível. Por exemplo, para
um tipo que pode ser somente leitura ou leitura/gravação, você poderia
implementar uma propriedade IsReadOnly que indica se o conjunto de
métodos de leitura/gravação está disponível ou não.
Tratar uma exceção NotSupportedException
A exceção NotSupportedException indica que um método não tem implementação
e que você não deve chamá-lo. Você não deve lidar com a exceção. Em vez disso, o que
você deve fazer depende da causa da exceção: se uma implementação estiver
completamente ausente ou se a invocação do membro é inconsistente com a finalidade
de um objeto (como uma chamada ao método FileStream.Write em um objeto
FileStream somente leitura).

Não foi fornecida uma implementação porque a operação não pode ser realizada de
forma significativa. Essa é uma exceção comum quando você está chamando métodos
em um objeto que fornece implementações para os métodos de uma classe base
abstrata ou que implementa uma interface de uso geral, e o método não tem
implementação significativa.

Por exemplo, a classe Convert implementa a interface IConvertible, o que significa que
ela deve incluir um método para converter cada tipo primitivo em cada outro tipo
primitivo. Muitas dessas conversões, no entanto, não são possíveis. Como resultado,
uma chamada ao método Convert.ToBoolean(DateTime), por exemplo, gera uma
exceção NotSupportedException porque não há conversão possível entre um valor
DateTime e um Boolean

Para eliminar a exceção, você deve eliminar a chamada do método.

A chamada do método não dá suporte para o estado do objeto. Você está tentando
invocar um membro cuja funcionalidade não está disponível devido ao estado do
objeto. Você pode eliminar a exceção de uma das três maneiras:

Você conhece o estado do objeto de antemão, mas invocou um método ou uma


propriedade sem suporte. Nesse caso, a invocação do membro é um erro, e você
pode eliminá-lo.

Você conhece o estado do objeto antecipadamente (geralmente porque seu


código o instanciou), mas o objeto está mal configurado. O exemplo a seguir
ilustra esse problema. Ele cria um objeto FileStream somente leitura e, em seguida,
tenta gravar nele.

C#

using System;
using System.IO;
using System.Text;
using System.Threading.Tasks;

public class Example


{
public static async Task Main()
{
Encoding enc = Encoding.Unicode;
String value = "This is a string to persist.";
Byte[] bytes = enc.GetBytes(value);

FileStream fs = new FileStream(@".\TestFile.dat",


FileMode.Open,
FileAccess.Read);
Task t = fs.WriteAsync(enc.GetPreamble(), 0,
enc.GetPreamble().Length);
Task t2 = t.ContinueWith((a) => fs.WriteAsync(bytes, 0,
bytes.Length));
await t2;
fs.Close();
}
}
// The example displays the following output:
// Unhandled Exception: System.NotSupportedException: Stream does
not support writing.
// at System.IO.Stream.BeginWriteInternal(Byte[] buffer, Int32
offset, Int32 count, AsyncCallback callback, Object state
// , Boolean serializeAsynchronously)
// at System.IO.FileStream.BeginWrite(Byte[] array, Int32 offset,
Int32 numBytes, AsyncCallback userCallback, Object sta
// teObject)
// at System.IO.Stream.<>c.<BeginEndWriteAsync>b__53_0(Stream
stream, ReadWriteParameters args, AsyncCallback callback,
// Object state)
// at
System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance,TArgs]
(TInstance thisRef, TArgs args, Func`5 beginMet
// hod, Func`3 endMethod)
// at System.IO.Stream.BeginEndWriteAsync(Byte[] buffer, Int32
offset, Int32 count)
// at System.IO.FileStream.WriteAsync(Byte[] buffer, Int32
offset, Int32 count, CancellationToken cancellationToken)
// at System.IO.Stream.WriteAsync(Byte[] buffer, Int32 offset,
Int32 count)
// at Example.Main()

Você pode eliminar a exceção garantindo que o objeto instanciado seja compatível
com a funcionalidade pretendida. O exemplo a seguir aborda o problema do
objeto FileStream somente leitura, fornecendo os argumentos corretos ao
construtor FileStream.FileStream(String, FileMode, FileAccess).

Você não sabe o estado do objeto com antecedência e o objeto não dá suporte
para uma determinada operação. Na maioria dos casos, o objeto deve incluir uma
propriedade ou método que indique se ele dá suporte para um determinado
conjunto de operações. Você pode eliminar a exceção verificando o valor do
objeto e invocando o membro somente se for apropriado.

O exemplo a seguir define um método DetectEncoding que gera uma exceção


NotSupportedException quando tenta ler do início de um fluxo não dá suporte ao
acesso de leitura.

C#

using System;
using System.IO;
using System.Threading.Tasks;

public class TestPropEx1


{
public static async Task Main()
{
String name = @".\TestFile.dat";
var fs = new FileStream(name,
FileMode.Create,
FileAccess.Write);
Console.WriteLine("Filename: {0}, Encoding: {1}",
name, await
FileUtilities1.GetEncodingType(fs));
}
}

public class FileUtilities1


{
public enum EncodingType
{ None = 0, Unknown = -1, Utf8 = 1, Utf16 = 2, Utf32 = 3 }

public async static Task<EncodingType> GetEncodingType(FileStream


fs)
{
Byte[] bytes = new Byte[4];
int bytesRead = await fs.ReadAsync(bytes, 0, 4);
if (bytesRead < 2)
return EncodingType.None;

if (bytesRead >= 3 & (bytes[0] == 0xEF && bytes[1] == 0xBB &&


bytes[2] == 0xBF))
return EncodingType.Utf8;

if (bytesRead == 4)
{
var value = BitConverter.ToUInt32(bytes, 0);
if (value == 0x0000FEFF | value == 0xFEFF0000)
return EncodingType.Utf32;
}

var value16 = BitConverter.ToUInt16(bytes, 0);


if (value16 == (ushort)0xFEFF | value16 == (ushort)0xFFFE)
return EncodingType.Utf16;

return EncodingType.Unknown;
}
}
// The example displays the following output:
// Unhandled Exception: System.NotSupportedException: Stream does
not support reading.
// at System.IO.FileStream.BeginRead(Byte[] array, Int32 offset,
Int32 numBytes, AsyncCallback callback, Object state)
// at System.IO.Stream.<>c.<BeginEndReadAsync>b__46_0(Stream
stream, ReadWriteParameters args, AsyncCallback callback, Object state)
// at
System.Threading.Tasks.TaskFactory`1.FromAsyncTrim[TInstance, TArgs]
(TInstance thisRef, TArgs args, Func`5 beginMethod, Func`3 endMethod)
// at System.IO.Stream.BeginEndReadAsync(Byte[] buffer, Int32
offset, Int32 count)
// at System.IO.FileStream.ReadAsync(Byte[] buffer, Int32 offset,
Int32 count, CancellationToken cancellationToken)
// at System.IO.Stream.ReadAsync(Byte[] buffer, Int32 offset,
Int32 count)
// at FileUtilities.GetEncodingType(FileStream fs) in
C:\Work\docs\program.cs:line 26
// at Example.Main() in C:\Work\docs\program.cs:line 13
// at Example.<Main>()

Você pode eliminar a exceção examinando o valor da propriedade


FileStream.CanRead e saindo do método se o fluxo for somente leitura.

C#

public static async Task<EncodingType> GetEncodingType(FileStream


fs)
{
if (!fs.CanRead)
return EncodingType.Unknown;

Byte[] bytes = new Byte[4];


int bytesRead = await fs.ReadAsync(bytes, 0, 4);
if (bytesRead < 2)
return EncodingType.None;

if (bytesRead >= 3 & (bytes[0] == 0xEF && bytes[1] == 0xBB &&


bytes[2] == 0xBF))
return EncodingType.Utf8;

if (bytesRead == 4)
{
var value = BitConverter.ToUInt32(bytes, 0);
if (value == 0x0000FEFF | value == 0xFEFF0000)
return EncodingType.Utf32;
}
var value16 = BitConverter.ToUInt16(bytes, 0);
if (value16 == (ushort)0xFEFF | value16 == (ushort)0xFFFE)
return EncodingType.Utf16;

return EncodingType.Unknown;
}
}
// The example displays the following output:
// Filename: .\TestFile.dat, Encoding: Unknown

Tipos de exceção relacionados


A exceção NotSupportedException está intimamente relacionada a dois outros tipos de
exceção;

NotImplementedException

Essa exceção é gerada quando um método poderia ser implementado, mas não é,
seja porque o membro será implementado em uma versão posterior, porque o
membro não está disponível em uma plataforma específica ou porque o membro
pertence a uma classe abstrata e uma classe derivada deve fornecer uma
implementação.

InvalidOperationException

Essa exceção é gerada em cenários nos quais, em geral, às vezes é possível que o
objeto execute a operação solicitada, e o estado do objeto determina se a
operação pode ser executada.

Notas sobre o .NET Compact Framework


Ao trabalhar com o .NET Compact Framework e utilizar P/Invoke em uma função nativa,
essa exceção poderá ser gerada se:

A declaração em código gerenciado está incorreta.


O .NET Compact Framework não dá suporte para o que você está tentando fazer.
Os nomes de DLL são danificados na exportação.

Se uma exceção NotSupportedException for gerada, verifique:

Para quaisquer violações das restrições de P/Invoke do .NET Compact Framework.


Para alguns argumentos que exigem memória alocada previamente. Se eles
existirem, você deverá passar uma referência para uma variável existente.
Se os nomes das funções exportadas estão corretos. Isso pode ser verificado com
DumpBin.exe.
Se você não está tentando passar argumentos demais.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Classe
System.TypeInitializationException
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Quando um inicializador da classe não inicializa um tipo, uma


TypeInitializationException é criada e é passada uma referência à exceção gerada pelo
inicializador da classe do tipo. A propriedade InnerException de
TypeInitializationException contém a exceção subjacente.

Normalmente, a exceção TypeInitializationException reflete uma condição catastrófica (o


runtime não consegue instanciar um tipo) que impede que um aplicativo continue.
Geralmente, o TypeInitializationException é lançado em resposta a alguma alteração no
ambiente de execução do aplicativo. Consequentemente, a não ser possivelmente para
solucionar problemas de código de depuração, a exceção não deve ser tratada em um
bloco try / catch . Em vez disso, a causa da exceção deve ser investigada e eliminada.

TypeInitializationException usa o HRESULT COR_E_TYPEINITIALIZATION , que tem o valor


0x80131534.

Para obter uma lista de valores de propriedade inicial para uma instância do
TypeInitializationException, consulte o TypeInitializationException construtores.

As seções a seguir descrevem algumas das situações em que uma exceção


TypeInitializationException é lançada.

Construtores estáticos
Um construtor estático, se existir, é chamado automaticamente pelo runtime antes de
criar uma nova instância de um tipo. Os construtores estáticos podem ser
explicitamente definidos por um desenvolvedor. Se um construtor estático não estiver
explicitamente definido, os compiladores criarão automaticamente um para inicializar
qualquer membro do tipo static (em C# ou F#) ou Shared (no Visual Basic). Para obter
mais informações sobre construtores estáticos, consulte Construtores Estáticos.

Geralmente, uma exceção TypeInitializationException é lançada quando um construtor


estático não consegue instanciar um tipo. A propriedade InnerException indica por que
o construtor estático não pôde instanciar o tipo. Algumas das causas mais comuns de
uma exceção TypeInitializationException são:

Uma exceção sem tratamento em um construtor estático

Se uma exceção for lançada em um construtor estático, essa exceção será


encapsulada em uma exceção TypeInitializationException e o tipo não poderá ser
instanciado.

O que geralmente dificulta a solução dessa exceção é o fato de que os


construtores estáticos nem sempre são definidos explicitamente no código-fonte.
Um construtor estático existe em um tipo se:

Ele foi explicitamente definido como um membro de um tipo.

O tipo tem static (em C# ou F#) ou Shared (no Visual Basic) variáveis que são
declaradas e inicializadas em um único comando. Nesse caso, o compilador de
linguagem gera um construtor estático para o tipo. Você pode inspecioná-lo
usando um utilitário como IL Disassembler. Por exemplo, quando os
compiladores C# e VB compilam o exemplo a seguir, eles geram a IL para um
construtor estático semelhante a este:

il

.method private specialname rtspecialname static


void .cctor() cil managed
{
// Code size 12 (0xc)
.maxstack 8
IL_0000: ldc.i4.3
IL_0001: newobj instance void TestClass::.ctor(int32)
IL_0006: stsfld class TestClass Example::test
IL_000b: ret
} // end of method Example::.cctor

O exemplo a seguir mostra uma exceção TypeInitializationException lançada por


um construtor estático gerado pelo compilador. A classe Example inclui um campo
static (em C#) ou Shared (no Visual Basic) do tipo TestClass que é instanciado

passando-se um valor de 3 para seu construtor de classe. Esse valor, entretanto, é


ilegal; somente valores de 0 ou 1 são permitidos. Como resultado, o construtor da
classe TestClass lança um ArgumentOutOfRangeException. Como essa exceção
não é identificada, ela é envolvida em uma exceção TypeInitializationException.

C#
using System;

public class Example


{
private static TestClass test = new TestClass(3);

public static void Main()


{
Example ex = new Example();
Console.WriteLine(test.Value);
}
}

public class TestClass


{
public readonly int Value;

public TestClass(int value)


{
if (value < 0 || value > 1) throw new
ArgumentOutOfRangeException(nameof(value));
Value = value;
}
}
// The example displays the following output:
// Unhandled Exception: System.TypeInitializationException:
// The type initializer for 'Example' threw an exception. --->
// System.ArgumentOutOfRangeException: Specified argument was out
of the range of valid values.
// at TestClass..ctor(Int32 value)
// at Example..cctor()
// --- End of inner exception stack trace ---
// at Example.Main()

Observe que a mensagem de exceção exibe informações sobre a propriedade


InnerException.

Falta um assembly ou um arquivo de dados

Uma causa comum de uma exceção TypeInitializationException é a ausência de um


assembly ou arquivo de dados presente nos ambientes de desenvolvimento e
teste de um aplicativo em seu ambiente de runtime. Por exemplo, você pode
compilar o exemplo a seguir para um assembly chamado Missing1a.dll usando
esta sintaxe de linha de comando:

C#

csc -t:library Missing1a.cs


C#

using System;

public class InfoModule


{
private DateTime firstUse;
private int ctr = 0;

public InfoModule(DateTime dat)


{
firstUse = dat;
}

public int Increment()


{
return ++ctr;
}

public DateTime GetInitializationTime()


{
return firstUse;
}
}

Em seguida, você pode compilar o exemplo a seguir em um executável chamado


Missing1.exe, incluindo uma referência a Missing1a.dll:

C#

csc Missing1.cs /r:Missing1a.dll

No entanto, se você renomear, mover ou excluir o Missing1a.dll e executar o


exemplo, ele lançará uma exceção TypeInitializationException e exibirá a saída
mostrada no exemplo. Observe que a mensagem de exceção inclui informações
sobre a propriedade InnerException. Nesse caso, a exceção interna é uma
FileNotFoundException que é lançada porque o runtime não consegue encontrar o
assembly dependente.

C#

using System;

public class MissingEx1


{
public static void Main()
{
Person p = new Person("John", "Doe");
Console.WriteLine(p);
}
}

public class Person


{
static readonly InfoModule s_infoModule;

readonly string _fName;


readonly string _lName;

static Person()
{
s_infoModule = new InfoModule(DateTime.UtcNow);
}

public Person(string fName, string lName)


{
_fName = fName;
_lName = lName;
s_infoModule.Increment();
}

public override string ToString()


{
return string.Format("{0} {1}", _fName, _lName);
}
}
// The example displays the following output if missing1a.dll is
renamed or removed:
// Unhandled Exception: System.TypeInitializationException:
// The type initializer for 'Person' threw an exception. --->
// System.IO.FileNotFoundException: Could not load file or
assembly
// 'Missing1a, Version=0.0.0.0, Culture=neutral,
PublicKeyToken=null'
// or one of its dependencies. The system cannot find the file
specified.
// at Person..cctor()
// --- End of inner exception stack trace ---
// at Person..ctor(String fName, String lName)
// at Example.Main()

7 Observação

Neste exemplo, uma exceção TypeInitializationException foi lançada porque


um assembly não pôde ser carregado. A exceção também pode ser lançada se
um construtor estático tentar abrir um arquivo de dados, como um arquivo de
configuração, um arquivo XML ou um arquivo contendo dados serializados,
que não possa ser encontrado.
Valores de tempo limite para correspondência
de expressão regular
Você pode definir o valor de tempo limite padrão para uma operação de padrões
correspondentes de expressão regular por domínio de aplicativo. O tempo limite é
definido por uma especificação de um valor TimeSpan para a propriedade
"REGEX_DEFAULT_MATCH_TIMEOUT" para o método AppDomain.SetData. O intervalo
de tempo deve ser um objeto TimeSpan válido que seja maior que zero e menor que
aproximadamente 24 dias. Se esses requisitos não forem atendidos, a tentativa de
definir o valor de tempo limite padrão lança uma exceção
ArgumentOutOfRangeException, que, por sua vez, é encapsulada em uma exceção
TypeInitializationException.

O exemplo a seguir mostra a TypeInitializationException que é lançada quando o valor


atribuído à propriedade "REGEX_DEFAULT_MATCH_TIMEOUT" for inválido. Para eliminar
a exceção, defina a propriedade "REGEX_DEFAULT_MATCH_TIMEOUT" como um valor
TimeSpan maior que zero e menor que aproximadamente 24 dias.

C#

using System;
using System.Text.RegularExpressions;

public class RegexEx1


{
public static void Main()
{
AppDomain domain = AppDomain.CurrentDomain;
// Set a timeout interval of -2 seconds.
domain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT",
TimeSpan.FromSeconds(-2));

Regex rgx = new Regex("[aeiouy]");


Console.WriteLine("Regular expression pattern: {0}",
rgx.ToString());
Console.WriteLine("Timeout interval for this regex: {0} seconds",
rgx.MatchTimeout.TotalSeconds);
}
}
// The example displays the following output:
// Unhandled Exception: System.TypeInitializationException:
// The type initializer for 'System.Text.RegularExpressions.Regex'
threw an exception. --->
// System.ArgumentOutOfRangeException: Specified argument was out of
the range of valid values.
// Parameter name: AppDomain data 'REGEX_DEFAULT_MATCH_TIMEOUT'
contains an invalid value or
// object for specifying a default matching timeout for
System.Text.RegularExpressions.Regex.
// at System.Text.RegularExpressions.Regex.InitDefaultMatchTimeout()
// at System.Text.RegularExpressions.Regex..cctor()
// --- End of inner exception stack trace ---
// at System.Text.RegularExpressions.Regex..ctor(String pattern)
// at Example.Main()

Calendários e dados culturais


Se você tentar instanciar um calendário, mas o runtime não conseguir instanciar o
objeto CultureInfo que corresponde a esse calendário, ele lançará uma exceção
TypeInitializationException. Essa exceção pode ser lançada pelos seguintes construtores
de classe de calendário:

O construtor sem parâmetros da classe JapaneseCalendar.


O construtor sem parâmetros da classe KoreanCalendar.
O construtor sem parâmetros da classe TaiwanCalendar.

Como os dados culturais para essas culturas devem estar disponíveis em todos os
sistemas, você raramente, ou nunca, encontrará essa exceção.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Numéricos no .NET
Artigo • 05/09/2023

O .NET fornece um intervalo de inteiros numéricos e primitivos de ponto flutuante, bem como:

System.Half, que representa um número de ponto flutuante de meia precisão.


System.Decimal, que representa um número de ponto flutuante decimal.
System.Numerics.BigInteger, que é um tipo integral sem limite superior ou inferior teórico.
System.Numerics.Complex, que representa números complexos.
Um conjunto de tipos habilitados para SIMD no namespace System.Numerics.

Tipos de inteiro
O .NET oferece suporte a tipos inteiros assinados e não assinados de 8 bits, 16 bits, 32 bits, 64 bits e 128 bits, listados
nas tabelas a seguir.

Tipos inteiros com sinal

Tipo Tamanho Valor mínimo Valor máximo


(em
bytes)

System.Int16 2 -32,768 32.767

System.Int32 4 -2,147,483,648 2\.147.483.647

System.Int64 8 -9,223,372,036,854,775,808 9,223,372,036,854,775,807

System.Int128 16 −170,141,183,460,469,231,731,687,303,715,884,105,728 170,141,183,460,469,231,731,687,303,715,884,105,727

System.SByte 1 -128 127

System.IntPtr 4 -2,147,483,648 2\.147.483.647


(em processo
de 32 bits)

System.IntPtr 8 -9,223,372,036,854,775,808 9,223,372,036,854,775,807


(em processo
de 64 bits)

Tipos inteiros sem sinal

Tipo Tamanho (em Valor Valor máximo


bytes) mínimo

System.Byte 1 0 255

System.UInt16 2 0 65.535

System.UInt32 4 0 4,294,967,295

System.UInt64 8 0 18,446,744,073,709,551,615

System.UInt128 16 0 340,282,366,920,938,463,463,374,607,431,768,211,455

System.UIntPtr (em processo de 32 4 0 4,294,967,295


bits)

System.UIntPtr (em processo de 64 8 0 18,446,744,073,709,551,615


bits)
Cada tipo inteiro dá suporte a um conjunto de operadores aritméticos padrão. A classe System.Math fornece métodos
para um conjunto mais amplo de funções matemáticas.

Você também pode trabalhar com os bits individuais nos valores usando a classe System.BitConverter.

7 Observação

Os tipos de inteiro sem sinal não estão em conformidade com CLS. Para obter mais informações, consulte
Independência de linguagem e componentes de linguagem independente.

BigInteger
A estrutura System.Numerics.BigInteger é um tipo imutável que representa um inteiro arbitrariamente grande cujo
valor, em teoria, não tem limites superiores ou inferiores. Os métodos do tipo BigInteger são muito semelhantes aos
dos outros tipos integrais.

Tipos de ponto flutuante


O .NET inclui os seguintes tipos de ponto flutuante:

Tipo Tamanho (em bytes) Intervalo aproximado Primitivo? Observações

System.Half 2 ±65504 No Introduzido no .NET 5

System.Single 4 ±3.4 x 1038 Sim

System.Double 8 ±1.7 × 10308 Sim

System.Decimal 16 ±7.9228 x 1028 No

Os tipos Half, Single e Double dão suporte a valores especiais que representam não é um número e infinito. Por
exemplo, o tipo Double fornece os seguintes valores: Double.NaN, Double.NegativeInfinity e Double.PositiveInfinity.
Você usa os métodos Double.IsNaN, Double.IsInfinity, Double.IsPositiveInfinity e Double.IsNegativeInfinity para testar
esses valores especiais.

Cada tipo de ponto flutuante dá suporte a um conjunto de operadores aritméticos padrão. A classe System.Math
fornece métodos para um conjunto mais amplo de funções matemáticas. O .NET Core 2.0 e posterior inclui a classe
System.MathF que fornece métodos que aceitam argumentos do tipo Single.

Também é possível trabalhar com os bits individuais em valores Double, Single e Half usando a classe
System.BitConverter. A estrutura System.Decimal tem seus próprios métodos Decimal.GetBits e Decimal(Int32[]), para
trabalhar com os bits individuais de um valor decimal, assim como seu próprio conjunto de métodos para executar
algumas operações matemáticas adicionais.

Os tipos Double, Single e Half devem ser usados para valores que, devido à sua própria natureza, são imprecisos (por
exemplo, a distância entre duas estrelas) e para aplicativos em que um alto grau de precisão e erro de
arredondamento pequeno não são necessários. Use o tipo System.Decimal para casos que exigem maior precisão e os
erros de arredondamento devem ser minimizados.

7 Observação

O tipo Decimal não elimina a necessidade de arredondamento. Em vez disso, ele minimiza erros devido a
arredondamento.
Complex
A estrutura System.Numerics.Complex representa um número complexo, ou seja, um número com uma parte de
número real e uma parte de número imaginário. Dá suporte a um conjunto padrão de aritmética, de comparação, de
igualdade, de conversões explícita e implícita, bem como a métodos matemáticos, algébricos e trigonométricos.

Tipos habilitados para SIMD


O namespace System.Numerics inclui um conjunto de tipos habilitados para SIMD do .NET. Operações SIMD (Single
Instruction Multiple Data) podem ser paralelizadas no nível de hardware. Isso aumenta a taxa de transferência dos
cálculos vetorizadas, que são comuns em aplicativos matemáticos, científicos e gráficos.

Os tipos habilitados para SIMD do .NET incluem o seguinte:

Os tipos Vector2, Vector3 e Vector4, que representam vetores com 2, 3 e 4 valores de Single.

Dois tipos de matriz, Matrix3x2, que representa uma matriz 3x2, e Matrix4x4, que representa uma matriz 4x4.

O tipo Plane representa um plano no espaço tridimensional.

O tipo Quaternion, que representa um vetor usado para codificar rotações físicas tridimensionais.

O tipo Vector<T>, que representa um vetor de um tipo numérico especificado e fornece um amplo conjunto de
operadores que se beneficiam de suporte a SIMD. A contagem de uma instância Vector<T> é corrigida, mas seu
valor Vector<T>.Count depende da CPU do computador em que o código é executado.

7 Observação

O tipo Vector<T> está incluído no .NET Core e no .NET 5+, mas não no .NET Framework. Se estiver usando
o .NET Framework, instale o pacote System.Numerics.Vectors NuGet para obter acesso a esse tipo.

Os tipos habilitados para SIMD são implementados de modo que possam ser usados com hardware não habilitados
para SIMD ou compiladores JIT. Para aproveitar as instruções SIMD, os aplicativos de 64 bits deverão ser executados
pelo runtime que utiliza o compilador RyuJIT, incluído no .NET Core e no .NET Framework 4.6 e versões posteriores.
Ele adiciona suporte a SIMD quando tem processadores de 64 bits como destino.

Para obter mais informações, consulte Usar tipos numéricos acelerados por SIMD.

Confira também
Cadeias de caracteres de formato numérico padrão
Tipos numéricos de ponto flutuante em C#
System.Boolean struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Uma Boolean instância pode ter um de dois valores: true ou false .

A Boolean estrutura fornece métodos que oferecem suporte às seguintes tarefas:

Convertendo valores booleanos em cadeias de caracteres: ToString


Analisando cadeias de caracteres para convertê-las em valores booleanos: Parse e
TryParse
Comparando valores: CompareTo e Equals

Este artigo explica essas tarefas e outros detalhes de uso.

Formatar valores booleanos


A representação de cadeia de caracteres de a Boolean é "True" para um valor ou "False"
para um false true valor. A representação de cadeia de caracteres de um Boolean valor
é definida pelos campos somente TrueString leitura e FalseString .

Use o ToString método para converter valores booleanos em cadeias de caracteres. A


estrutura booleana inclui duas ToString sobrecargas: o método sem ToString()
parâmetros e o método, que inclui um parâmetro que controla a
ToString(IFormatProvider) formatação. No entanto, como esse parâmetro é ignorado, as
duas sobrecargas produzem cadeias de caracteres idênticas. O
ToString(IFormatProvider) método não oferece suporte à formatação sensível à cultura.

O exemplo a seguir ilustra a formatação com o ToString método. Observe que os


exemplos de C# e VB usam o recurso de formatação composta, enquanto o exemplo de
F# usa interpolação de cadeia de caracteres. Em ambos os casos, o ToString método é
chamado implicitamente.

C#

using System;

public class Example10


{
public static void Main()
{
bool raining = false;
bool busLate = true;

Console.WriteLine("It is raining: {0}", raining);


Console.WriteLine("The bus is late: {0}", busLate);
}
}
// The example displays the following output:
// It is raining: False
// The bus is late: True

Como a Boolean estrutura pode ter apenas dois valores, é fácil adicionar formatação
personalizada. Para formatação personalizada simples na qual outros literais de cadeia
de caracteres são substituídos por "True" e "False", você pode usar qualquer recurso de
avaliação condicional suportado por sua linguagem, como o operador condicional em
C# ou o operador If no Visual Basic. O exemplo a seguir usa essa técnica para formatar
Boolean valores como "Sim" e "Não" em vez de "Verdadeiro" e "Falso".

C#

using System;

public class Example11


{
public static void Main()
{
bool raining = false;
bool busLate = true;

Console.WriteLine("It is raining: {0}",


raining ? "Yes" : "No");
Console.WriteLine("The bus is late: {0}",
busLate ? "Yes" : "No");
}
}
// The example displays the following output:
// It is raining: No
// The bus is late: Yes

Para operações de formatação personalizadas mais complexas, incluindo formatação


sensível à cultura, você pode chamar o String.Format(IFormatProvider, String, Object[])
método e fornecer uma ICustomFormatter implementação. O exemplo a seguir
implementa as interfaces e para fornecer cadeias de caracteres booleanas sensíveis à
cultura para as ICustomFormatter culturas inglês (Estados Unidos), francês (França) e
IFormatProvider russo (Rússia).

C#
using System;
using System.Globalization;

public class Example4


{
public static void Main()
{
String[] cultureNames = { "", "en-US", "fr-FR", "ru-RU" };
foreach (var cultureName in cultureNames) {
bool value = true;
CultureInfo culture =
CultureInfo.CreateSpecificCulture(cultureName);
BooleanFormatter formatter = new BooleanFormatter(culture);

string result = string.Format(formatter, "Value for '{0}': {1}",


culture.Name, value);
Console.WriteLine(result);
}
}
}

public class BooleanFormatter : ICustomFormatter, IFormatProvider


{
private CultureInfo culture;

public BooleanFormatter() : this(CultureInfo.CurrentCulture)


{ }

public BooleanFormatter(CultureInfo culture)


{
this.culture = culture;
}

public Object GetFormat(Type formatType)


{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(string fmt, Object arg, IFormatProvider


formatProvider)
{
// Exit if another format provider is used.
if (! formatProvider.Equals(this)) return null;

// Exit if the type to be formatted is not a Boolean


if (! (arg is Boolean)) return null;

bool value = (bool) arg;


switch (culture.Name) {
case "en-US":
return value.ToString();
case "fr-FR":
if (value)
return "vrai";
else
return "faux";
case "ru-RU":
if (value)
return "верно";
else
return "неверно";
default:
return value.ToString();
}
}
}
// The example displays the following output:
// Value for '': True
// Value for 'en-US': True
// Value for 'fr-FR': vrai
// Value for 'ru-RU': верно

Opcionalmente, você pode usar arquivos de recurso para definir cadeias de caracteres
booleanas específicas da cultura.

Converter para e de valores booleanos


A Boolean estrutura implementa a IConvertible interface. Como resultado, você pode
usar a Convert classe para executar conversões entre um Boolean valor e qualquer outro
tipo primitivo no .NET, ou você pode chamar as Boolean implementações explícitas da
estrutura. No entanto, as conversões entre a Boolean e os seguintes tipos não são
suportadas, portanto, os métodos de conversão correspondentes lançam uma
InvalidCastException exceção:

Conversão entre Boolean e (o Convert.ToBoolean(Char) e


CharConvert.ToChar(Boolean) métodos).

Conversão entre Boolean e (o Convert.ToBoolean(DateTime) e


DateTimeConvert.ToDateTime(Boolean) métodos).

Todas as conversões de números integrais ou de ponto flutuante para valores


booleanos convertem valores diferentes de zero em true e valores zero em false . O
exemplo a seguir ilustra isso chamando sobrecargas selecionadas da Convert.ToBoolean
classe.

C#
using System;

public class Example2


{
public static void Main()
{
Byte byteValue = 12;
Console.WriteLine(Convert.ToBoolean(byteValue));
Byte byteValue2 = 0;
Console.WriteLine(Convert.ToBoolean(byteValue2));
int intValue = -16345;
Console.WriteLine(Convert.ToBoolean(intValue));
long longValue = 945;
Console.WriteLine(Convert.ToBoolean(longValue));
SByte sbyteValue = -12;
Console.WriteLine(Convert.ToBoolean(sbyteValue));
double dblValue = 0;
Console.WriteLine(Convert.ToBoolean(dblValue));
float sngValue = .0001f;
Console.WriteLine(Convert.ToBoolean(sngValue));
}
}
// The example displays the following output:
// True
// False
// True
// True
// True
// False
// True

Ao converter de valores booleanos para numéricos, os Convert métodos de conversão


da classe convertem true para 1 e false para 0. No entanto, as funções de conversão
do Visual Basic convertem true em 255 (para conversões em Byte valores) ou -1 (para
todas as outras conversões numéricas). O exemplo a seguir converte em valores
numéricos true usando um Convert método e, no caso do exemplo do Visual Basic,
usando o próprio operador de conversão da linguagem Visual Basic.

C#

using System;

public class Example3


{
public static void Main()
{
bool flag = true;

byte byteValue;
byteValue = Convert.ToByte(flag);
Console.WriteLine("{0} -> {1}", flag, byteValue);

sbyte sbyteValue;
sbyteValue = Convert.ToSByte(flag);
Console.WriteLine("{0} -> {1}", flag, sbyteValue);

double dblValue;
dblValue = Convert.ToDouble(flag);
Console.WriteLine("{0} -> {1}", flag, dblValue);

int intValue;
intValue = Convert.ToInt32(flag);
Console.WriteLine("{0} -> {1}", flag, intValue);
}
}
// The example displays the following output:
// True -> 1
// True -> 1
// True -> 1
// True -> 1

Para conversões de valores de cadeia de Boolean caracteres, consulte a seção Formatar


valores booleanos. Para conversões de cadeias de caracteres em Boolean valores,
consulte a seção Analisar valores booleanos.

Analisar valores booleanos


A Boolean estrutura inclui dois métodos Parse de análise estática e TryParse, que
convertem uma cadeia de caracteres em um valor booleano. A representação de cadeia
de caracteres de um valor booleano é definida pelos equivalentes sem diferenciação de
maiúsculas e minúsculas TrueString dos valores dos campos e , que são "True" e
FalseString "False", respectivamente. Em outras palavras, as únicas cadeias de caracteres
que analisam com êxito são "True", "False", "true", "false" ou algum equivalente de
maiúsculas e minúsculas. Não é possível analisar com êxito cadeias de caracteres
numéricas como "0" ou "1". Caracteres de espaço em branco à esquerda ou à direita
não são considerados ao executar a comparação de cadeia de caracteres.

O exemplo a seguir usa os Parse métodos e TryParse para analisar um número de


cadeias de caracteres. Observe que somente os equivalentes que não diferenciam
maiúsculas de minúsculas de "True" e "False" podem ser analisados com êxito.

C#

using System;

public class Example7


{
public static void Main()
{
string[] values = { null, String.Empty, "True", "False",
"true", "false", " true ",
"TrUe", "fAlSe", "fa lse", "0",
"1", "-1", "string" };
// Parse strings using the Boolean.Parse method.
foreach (var value in values) {
try {
bool flag = Boolean.Parse(value);
Console.WriteLine("'{0}' --> {1}", value, flag);
}
catch (ArgumentException) {
Console.WriteLine("Cannot parse a null string.");
}
catch (FormatException) {
Console.WriteLine("Cannot parse '{0}'.", value);
}
}
Console.WriteLine();
// Parse strings using the Boolean.TryParse method.
foreach (var value in values) {
bool flag = false;
if (Boolean.TryParse(value, out flag))
Console.WriteLine("'{0}' --> {1}", value, flag);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
}
// The example displays the following output:
// Cannot parse a null string.
// Cannot parse ''.
// 'True' --> True
// 'False' --> False
// 'true' --> True
// 'false' --> False
// ' true ' --> True
// 'TrUe' --> True
// 'fAlSe' --> False
// Cannot parse 'fa lse'.
// Cannot parse '0'.
// Cannot parse '1'.
// Cannot parse '-1'.
// Cannot parse 'string'.
//
// Unable to parse ''
// Unable to parse ''
// 'True' --> True
// 'False' --> False
// 'true' --> True
// 'false' --> False
// ' true ' --> True
// 'TrUe' --> True
// 'fAlSe' --> False
// Cannot parse 'fa lse'.
// Unable to parse '0'
// Unable to parse '1'
// Unable to parse '-1'
// Unable to parse 'string'

Se você estiver programando no Visual Basic, você pode usar a função para converter a
CBool representação de cadeia de caracteres de um número em um valor booleano. "0"

é convertido em , e a representação de cadeia de caracteres de qualquer valor diferente


de zero é convertida em false true . Se você não estiver programando no Visual Basic,
você deve converter sua sequência numérica em um número antes de convertê-lo em
um Booleano. O exemplo a seguir ilustra isso convertendo uma matriz de inteiros em
valores booleanos.

C#

using System;

public class Example8


{
public static void Main()
{
String[] values = { "09", "12.6", "0", "-13 " };
foreach (var value in values) {
bool success, result;
int number;
success = Int32.TryParse(value, out number);
if (success) {
// The method throws no exceptions.
result = Convert.ToBoolean(number);
Console.WriteLine("Converted '{0}' to {1}", value, result);
}
else {
Console.WriteLine("Unable to convert '{0}'", value);
}
}
}
}
// The example displays the following output:
// Converted '09' to True
// Unable to convert '12.6'
// Converted '0' to False
// Converted '-13 ' to True

Comparar valores booleanos


Como os valores booleanos são ou , há poucos motivos para chamar explicitamente o
CompareTo método, que indica se uma instância é maior que, menor ou
true false igual a um valor especificado. Normalmente, para comparar duas variáveis

booleanas, você chama o método ou usa o Equals operador equality do idioma.

No entanto, quando você deseja comparar uma variável booleana com o valor booleano
literal ou false , não é necessário fazer uma comparação explícita, porque o resultado
da avaliação de um valor booleano é esse valor true booleano. Por exemplo, as duas
expressões a seguir são equivalentes, mas a segunda é mais compacta. No entanto,
ambas as técnicas oferecem desempenho comparável.

C#

if (booleanValue == true) {

C#

if (booleanValue) {

Trabalhar com booleanos como valores


binários
Um valor booleano ocupa um byte de memória, como mostra o exemplo a seguir. O
exemplo C# deve ser compilado com o /unsafe switch.

C#

using System;

public struct BoolStruct


{
public bool flag1;
public bool flag2;
public bool flag3;
public bool flag4;
public bool flag5;
}

public class Example9


{
public static void Main()
{
unsafe {
BoolStruct b = new BoolStruct();
bool* addr = (bool*) &b;
Console.WriteLine("Size of BoolStruct: {0}", sizeof(BoolStruct));
Console.WriteLine("Field offsets:");
Console.WriteLine(" flag1: {0}", (bool*) &b.flag1 - addr);
Console.WriteLine(" flag1: {0}", (bool*) &b.flag2 - addr);
Console.WriteLine(" flag1: {0}", (bool*) &b.flag3 - addr);
Console.WriteLine(" flag1: {0}", (bool*) &b.flag4 - addr);
Console.WriteLine(" flag1: {0}", (bool*) &b.flag5 - addr);
}
}
}
// The example displays the following output:
// Size of BoolStruct: 5
// Field offsets:
// flag1: 0
// flag1: 1
// flag1: 2
// flag1: 3
// flag1: 4

O bit de ordem baixa do byte é usado para representar seu valor. Um valor de 1
representa e um valor de 0 representa true false .

 Dica

Você pode usar a System.Collections.Specialized.BitVector32 estrutura para


trabalhar com conjuntos de valores booleanos.

Você pode converter um valor booleano em sua representação binária chamando o


BitConverter.GetBytes(Boolean) método. O método retorna uma matriz de bytes com
um único elemento. Para restaurar um valor booleano de sua representação binária,
você pode chamar o BitConverter.ToBoolean(Byte[], Int32) método.

O exemplo a seguir chama o método para converter um valor booleano em sua


representação binária e exibe os bits individuais do valor e, em seguida, chama o
método para restaurar o BitConverter.GetBytesBitConverter.ToBoolean valor de sua
representação binária.

C#

using System;

public class Example1


{
public static void Main()
{
bool[] flags = { true, false };
foreach (var flag in flags)
{
// Get binary representation of flag.
Byte value = BitConverter.GetBytes(flag)[0];
Console.WriteLine("Original value: {0}", flag);
Console.WriteLine("Binary value: {0} ({1})", value,
GetBinaryString(value));
// Restore the flag from its binary representation.
bool newFlag = BitConverter.ToBoolean(new Byte[] { value }, 0);
Console.WriteLine("Restored value: {0}\n", flag);
}
}

private static string GetBinaryString(Byte value)


{
string retVal = Convert.ToString(value, 2);
return new string('0', 8 - retVal.Length) + retVal;
}
}
// The example displays the following output:
// Original value: True
// Binary value: 1 (00000001)
// Restored value: True
//
// Original value: False
// Binary value: 0 (00000000)
// Restored value: False

Executar operações com valores booleanos


Esta seção ilustra como os valores booleanos são usados em aplicativos. A primeira
seção discute seu uso como sinalizador. O segundo ilustra seu uso para operações
aritméticas.

Valores booleanos como sinalizadores


Variáveis booleanas são mais comumente usadas como sinalizadores, para sinalizar a
presença ou ausência de alguma condição. Por exemplo, no String.Compare(String,
String, Boolean) método, o parâmetro final, , ignoreCase é um sinalizador que indica se a
comparação de duas cadeias de caracteres não diferencia maiúsculas de minúsculas ( is
) ou diferencia maiúsculas de minúsculas ( ignoreCase ignoreCase is true false ). O valor
do sinalizador pode então ser avaliado em uma instrução condicional.

O exemplo a seguir usa um aplicativo de console simples para ilustrar o uso de variáveis
booleanas como sinalizadores. O aplicativo aceita parâmetros de linha de comando que
permitem que a saída seja redirecionada para um arquivo especificado (a opção) e que
permitem que a saída seja enviada para um arquivo especificado e para o console (a
/f /b opção). O aplicativo define um sinalizador nomeado para indicar se a saída deve

ser enviada para um arquivo e um sinalizador nomeado isRedirected isBoth para


indicar que a saída deve ser enviada para o console. O exemplo F# usa uma função
recursiva para analisar os argumentos.

C#

using System;
using System.IO;
using System.Threading;

public class Example5


{
public static void Main()
{
// Initialize flag variables.
bool isRedirected = false;
bool isBoth = false;
String fileName = "";
StreamWriter sw = null;

// Get any command line arguments.


String[] args = Environment.GetCommandLineArgs();
// Handle any arguments.
if (args.Length > 1) {
for (int ctr = 1; ctr < args.Length; ctr++) {
String arg = args[ctr];
if (arg.StartsWith("/") || arg.StartsWith("-")) {
switch (arg.Substring(1).ToLower())
{
case "f":
isRedirected = true;
if (args.Length < ctr + 2) {
ShowSyntax("The /f switch must be followed by a
filename.");
return;
}
fileName = args[ctr + 1];
ctr++;
break;
case "b":
isBoth = true;
break;
default:
ShowSyntax(String.Format("The {0} switch is not
supported",
args[ctr]));
return;
}
}
}
}

// If isBoth is True, isRedirected must be True.


if (isBoth && ! isRedirected) {
ShowSyntax("The /f switch must be used if /b is used.");
return;
}

// Handle output.
if (isRedirected) {
sw = new StreamWriter(fileName);
if (!isBoth)
Console.SetOut(sw);
}
String msg = String.Format("Application began at {0}", DateTime.Now);
Console.WriteLine(msg);
if (isBoth) sw.WriteLine(msg);
Thread.Sleep(5000);
msg = String.Format("Application ended normally at {0}",
DateTime.Now);
Console.WriteLine(msg);
if (isBoth) sw.WriteLine(msg);
if (isRedirected) sw.Close();
}

private static void ShowSyntax(String errMsg)


{
Console.WriteLine(errMsg);
Console.WriteLine("\nSyntax: Example [[/f <filename> [/b]]\n");
}
}

Booleanos e operações aritméticas


Um valor booleano às vezes é usado para indicar a presença de uma condição que
aciona um cálculo matemático. Por exemplo, uma hasShippingCharge variável pode
servir como um sinalizador para indicar se deseja adicionar taxas de frete a um valor de
fatura.

Como uma operação com um valor não tem efeito sobre o resultado de uma operação,
não é necessário converter o booleano em um false valor integral para usar na
operação matemática. Em vez disso, você pode usar a lógica condicional.

O exemplo a seguir calcula um valor que consiste em um subtotal, uma taxa de frete e
uma taxa de serviço opcional. A hasServiceCharge variável determina se a taxa de
serviço é aplicada. Em vez de converter hasServiceCharge para um valor numérico e
multiplicá-lo pelo valor da taxa de serviço, o exemplo usa lógica condicional para
adicionar o valor da taxa de serviço, se aplicável.

C#

using System;
public class Example6
{
public static void Main()
{
bool[] hasServiceCharges = { true, false };
Decimal subtotal = 120.62m;
Decimal shippingCharge = 2.50m;
Decimal serviceCharge = 5.00m;

foreach (var hasServiceCharge in hasServiceCharges) {


Decimal total = subtotal + shippingCharge +
(hasServiceCharge ? serviceCharge : 0);
Console.WriteLine("hasServiceCharge = {1}: The total is {0:C2}.",
total, hasServiceCharge);
}
}
}
// The example displays output like the following:
// hasServiceCharge = True: The total is $128.12.
// hasServiceCharge = False: The total is $123.12.

Booleanos e interoperabilidade
Embora o empacotamento de tipos de dados de base para COM seja geralmente
simples, o tipo de Boolean dados é uma exceção. Você pode aplicar o atributo para
marshal o MarshalAsAttributeBoolean tipo para qualquer uma das seguintes
representações:

ノ Expandir a tabela

Tipo de enumeração Formato não gerenciado

UnmanagedType.Bool Um valor inteiro de 4 bytes, onde qualquer valor diferente de zero


representa e 0 representa true false . Esse é o formato padrão de
um campo em uma estrutura e de um BooleanBoolean parâmetro
em chamadas de chamada de plataforma.

UnmanagedType.U1 Um valor inteiro de 1 byte, onde 1 representa e 0 representa


true false .

UnmanagedType.VariantBool Um valor inteiro de 2 bytes, onde -1 representa e 0 representa


true false . Esse é o formato padrão de um Boolean parâmetro
em chamadas de interoperabilidade COM.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
Selecione um link para fornecer
A fonte deste conteúdo pode comentários:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações
documentação
de pull. Para obter mais
informações, confira o nosso
 Fornecer comentários sobre o
guia para colaboradores.
produto
Estrutura System.Byte
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Byte é um tipo de valor imutável que representa inteiros não assinados com valores que
variam de 0 (que é representado pela constante) a 255 (que é representado pela
Byte.MinValueByte.MaxValue constante). O .NET também inclui um tipo de valor inteiro
de 8 bits assinado, , SByteque representa valores que variam de -128 a 127.

Instanciar um valor de Byte


Você pode instanciar um Byte valor de várias maneiras:

Você pode declarar uma Byte variável e atribuir-lhe um valor inteiro literal que
esteja dentro do intervalo do tipo de Byte dados. O exemplo a seguir declara duas
Byte variáveis e atribui valores a elas dessa maneira.

C#

byte value1 = 64;


byte value2 = 255;

Você pode atribuir um valor numérico não byte a um byte. Essa é uma conversão
de estreitamento, portanto, requer um operador de conversão em C# e F# ou um
método de conversão no Visual Basic se Option Strict estiver ativado. Se o valor
de não-byte for um , ou Decimal valor que inclui um Singlecomponente
fracionário, Doubleo processamento de sua parte fracionária dependerá do
compilador que executa a conversão. O exemplo a seguir atribui vários valores
numéricos a Byte variáveis.

C#

int int1 = 128;


try
{
byte value1 = (byte)int1;
Console.WriteLine(value1);
}
catch (OverflowException)
{
Console.WriteLine("{0} is out of range of a byte.", int1);
}
double dbl2 = 3.997;
try
{
byte value2 = (byte)dbl2;
Console.WriteLine(value2);
}
catch (OverflowException)
{
Console.WriteLine("{0} is out of range of a byte.", dbl2);
}
// The example displays the following output:
// 128
// 3

Você pode chamar um método da Convert classe para converter qualquer tipo
com suporte em um Byte valor. Isso é possível porque Byte suporta a IConvertible
interface. O exemplo a seguir ilustra a conversão de uma matriz de Int32 valores
em Byte valores.

C#

int[] numbers = { Int32.MinValue, -1, 0, 121, 340, Int32.MaxValue };


byte result;
foreach (int number in numbers)
{
try
{
result = Convert.ToByte(number);
Console.WriteLine("Converted the {0} value {1} to the {2} value
{3}.",
number.GetType().Name, number,
result.GetType().Name, result);
}
catch (OverflowException)
{
Console.WriteLine("The {0} value {1} is outside the range of
the Byte type.",
number.GetType().Name, number);
}
}
// The example displays the following output:
// The Int32 value -2147483648 is outside the range of the Byte
type.
// The Int32 value -1 is outside the range of the Byte type.
// Converted the Int32 value 0 to the Byte value 0.
// Converted the Int32 value 121 to the Byte value 121.
// The Int32 value 340 is outside the range of the Byte type.
// The Int32 value 2147483647 is outside the range of the Byte
type.
Você pode chamar o Parse método or TryParse para converter a representação de
cadeia de caracteres de um valor em um ByteBytearquivo . A cadeia de caracteres
pode conter dígitos decimais ou hexadecimais. O exemplo a seguir ilustra a
operação de análise usando uma cadeia decimal e uma cadeia hexadecimal.

C#

string string1 = "244";


try
{
byte byte1 = Byte.Parse(string1);
Console.WriteLine(byte1);
}
catch (OverflowException)
{
Console.WriteLine("'{0}' is out of range of a byte.", string1);
}
catch (FormatException)
{
Console.WriteLine("'{0}' is out of range of a byte.", string1);
}

string string2 = "F9";


try
{
byte byte2 = Byte.Parse(string2,

System.Globalization.NumberStyles.HexNumber);
Console.WriteLine(byte2);
}
catch (OverflowException)
{
Console.WriteLine("'{0}' is out of range of a byte.", string2);
}
catch (FormatException)
{
Console.WriteLine("'{0}' is out of range of a byte.", string2);
}
// The example displays the following output:
// 244
// 249

Executar operações em valores de Byte


O Byte tipo suporta operações matemáticas padrão, como adição, subtração, divisão,
multiplicação, subtração, negação e negação unária. Como os outros tipos integrais, o
Byte tipo também suporta os operadores bitwise AND , , , OR XOR left shift e right shift.
Você pode usar os operadores numéricos padrão para comparar dois Byte valores ou
pode chamar o CompareTo método ou Equals .

Também é possível chamar os membros da classe Math para realizar uma ampla
variedade de operações numéricas, inclusive obter o valor absoluto de um número,
calcular o quociente e o restante da divisão integral, determinando o valor máximo ou
mínimo de dois inteiros, obter o sinal de um número e arredondar um número.

Representar um byte como uma cadeia de


caracteres
O Byte tipo fornece suporte completo para cadeias de caracteres de formato numérico
padrão e personalizado. (Para obter mais informações, consulte Tipos de formatação,
cadeias de caracteres de formato numérico padrão e cadeias de caracteres de formato
numérico personalizado.) No entanto, mais comumente, os valores de byte são
representados como valores de um dígito a três dígitos sem qualquer formatação
adicional ou como valores hexadecimais de dois dígitos.

Para formatar um Byte valor como uma cadeia de caracteres integral sem zeros à
esquerda, você pode chamar o método sem ToString() parâmetros. Usando o
especificador de formato "D", você também pode incluir um número especificado de
zeros à esquerda na representação de cadeia de caracteres. Usando o especificador de
formato "X", você pode representar um Byte valor como uma cadeia de caracteres
hexadecimal. O exemplo a seguir formata os elementos em uma matriz de Byte valores
dessas três maneiras.

C#

byte[] numbers = { 0, 16, 104, 213 };


foreach (byte number in numbers)
{
// Display value using default formatting.
Console.Write("{0,-3} --> ", number.ToString());
// Display value with 3 digits and leading zeros.
Console.Write(number.ToString("D3") + " ");
// Display value with hexadecimal.
Console.Write(number.ToString("X2") + " ");
// Display value with four hexadecimal digits.
Console.WriteLine(number.ToString("X4"));
}
// The example displays the following output:
// 0 --> 000 00 0000
// 16 --> 016 10 0010
// 104 --> 104 68 0068
// 213 --> 213 D5 00D5
Você também pode formatar um Byte valor como uma cadeia de caracteres binária,
octal, decimal ou hexadecimal chamando o método e fornecendo a base como o
ToString(Byte, Int32) segundo parâmetro do método. O exemplo a seguir chama esse
método para exibir as representações binárias, octais e hexadecimais de uma matriz de
valores de byte.

C#

byte[] numbers = { 0, 16, 104, 213 };


Console.WriteLine("{0} {1,8} {2,5} {3,5}",
"Value", "Binary", "Octal", "Hex");
foreach (byte number in numbers)
{
Console.WriteLine("{0,5} {1,8} {2,5} {3,5}",
number, Convert.ToString(number, 2),
Convert.ToString(number, 8),
Convert.ToString(number, 16));
}
// The example displays the following output:
// Value Binary Octal Hex
// 0 0 0 0
// 16 10000 20 10
// 104 1101000 150 68
// 213 11010101 325 d5

Trabalhar com valores de Byte não decimais


Além de trabalhar com bytes individuais como valores decimais, convém executar
operações bit a bit com valores de byte ou trabalhar com matrizes de bytes ou com as
representações binárias ou hexadecimais de valores de byte. Por exemplo, sobrecargas
do BitConverter.GetBytes método podem converter cada um dos tipos de dados
primitivos em uma matriz de bytes, e o BigInteger.ToByteArray método converte um
BigInteger valor em uma matriz de bytes.

Byte os valores são representados em 8 bits apenas por sua magnitude, sem um bit de
sinal. Isso é importante ter em mente quando você executa operações bit a bit em Byte
valores ou quando trabalha com bits individuais. Para executar uma operação numérica,
booleana ou de comparação em quaisquer dois valores não decimais, ambos os valores
devem usar a mesma representação.

Quando uma operação é executada em dois Byte valores, os valores compartilham a


mesma representação, portanto, o resultado é preciso. Isso é ilustrado no exemplo a
seguir, que mascara o bit de ordem mais baixa de um Byte valor para garantir que ele
seja uniforme.
C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
string[] values = { Convert.ToString(12, 16),
Convert.ToString(123, 16),
Convert.ToString(245, 16) };

byte mask = 0xFE;


foreach (string value in values) {
Byte byteValue = Byte.Parse(value, NumberStyles.AllowHexSpecifier);
Console.WriteLine("{0} And {1} = {2}", byteValue, mask,
byteValue & mask);
}
}
}
// The example displays the following output:
// 12 And 254 = 12
// 123 And 254 = 122
// 245 And 254 = 244

Por outro lado, quando você trabalha com bits não assinados e assinados, as operações
bit a bit são complicadas pelo fato de que os SByte valores usam representação de sinal
e magnitude para valores positivos e representação de complemento de dois para
valores negativos. Para executar uma operação bit a bit significativa, os valores devem
ser convertidos em duas representações equivalentes e as informações sobre o bit de
sinal devem ser preservadas. O exemplo a seguir faz isso para mascarar os bits 2 e 4 de
uma matriz de valores assinados e não assinados de 8 bits.

C#

using System;
using System.Collections.Generic;
using System.Globalization;

public struct ByteString


{
public string Value;
public int Sign;
}

public class Example1


{
public static void Main()
{
ByteString[] values = CreateArray(-15, 123, 245);
byte mask = 0x14; // Mask all bits but 2 and 4.

foreach (ByteString strValue in values)


{
byte byteValue = Byte.Parse(strValue.Value,
NumberStyles.AllowHexSpecifier);
Console.WriteLine("{0} ({1}) And {2} ({3}) = {4} ({5})",
strValue.Sign * byteValue,
Convert.ToString(byteValue, 2),
mask, Convert.ToString(mask, 2),
(strValue.Sign & Math.Sign(mask)) * (byteValue
& mask),
Convert.ToString(byteValue & mask, 2));
}
}

private static ByteString[] CreateArray(params int[] values)


{
List<ByteString> byteStrings = new List<ByteString>();

foreach (object value in values)


{
ByteString temp = new ByteString();
int sign = Math.Sign((int)value);
temp.Sign = sign;

// Change two's complement to magnitude-only representation.


temp.Value = Convert.ToString(((int)value) * sign, 16);

byteStrings.Add(temp);
}
return byteStrings.ToArray();
}
}
// The example displays the following output:
// -15 (1111) And 20 (10100) = 4 (100)
// 123 (1111011) And 20 (10100) = 16 (10000)
// 245 (11110101) And 20 (10100) = 20 (10100)

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.
 Fornecer comentários sobre o
produto
System.Decimal struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Decimal tipo de valor representa números decimais que variam de positivo


79.228.162.514.264.337.593.543.950.335 a negativo
79.228.162.514.264.337.593.543.950.335. O valor padrão de a Decimal é 0. O Decimal
tipo de valor é apropriado para cálculos financeiros que exigem um grande número de
dígitos integrais e fracionários significativos e nenhum erro de arredondamento. O
Decimal tipo não elimina a necessidade de arredondamento. Em vez disso, ele minimiza
erros devido a arredondamento. Por exemplo, o código a seguir produz um resultado
de
0,9999999999999999999999999999999999999999999999999999999999999999999999
999999999999999999999999999999999999999999999999999999

C#

decimal dividend = Decimal.One;


decimal divisor = 3;
// The following displays 0.9999999999999999999999999999 to the console
Console.WriteLine(dividend/divisor * divisor);

Quando o resultado da divisão e multiplicação é passado para o método, o resultado


não sofre perda de precisão, como mostra o Round código a seguir.

C#

decimal dividend = Decimal.One;


decimal divisor = 3;
// The following displays 1.00 to the console
Console.WriteLine(Math.Round(dividend/divisor * divisor, 2));

Um número decimal é um valor de ponto flutuante que consiste em um sinal, um valor


numérico onde cada dígito no valor varia de 0 a 9 e um fator de escala que indica a
posição de um ponto decimal flutuante que separa as partes integral e fracionária do
valor numérico.

A representação binária de um valor é de 128 bits que consiste em um número inteiro


de 96 bits e um Decimal conjunto de sinalizadores de 32 bits que representam coisas
como o sinal e o fator de escala usados para especificar qual parte dele é uma fração
decimal. Portanto, a representação binária de um Decimal valor a forma, ((-2 96 a 2 96) /
96-1

10(0 a 28)), onde -(2 96-1) é igual a , e 2 é igual a .MinValueMaxValue Para obter mais
informações sobre a representação binária de Decimal valores e um exemplo, consulte o
construtor e o Decimal(Int32[])GetBits método.

O fator de dimensionamento também preserva quaisquer zeros à direita em um Decimal


número. Os zeros à direita não afetam o valor de um Decimal número em operações
aritméticas ou de comparação. No entanto, zeros à direita podem ser revelados ToString
pelo método se uma cadeia de caracteres de formato apropriado for aplicada.

Considerações de conversão
Esse tipo fornece métodos que convertem Decimal valores de e para SByte, , , , ,
UInt32ByteUInt16, Int64Int16Int32e UInt64 valores. As conversões desses tipos integrais
para Decimal estão ampliando as conversões que nunca perdem informações ou lançam
exceções.

As conversões de para qualquer um dos tipos integrais são conversões de Decimal


estreitamento que arredondam o valor para o Decimal valor inteiro mais próximo em
direção a zero. Algumas linguagens, como C#, também oferecem suporte à conversão
de Decimal valores em Char valores. Se o resultado dessas conversões não puder ser
representado no tipo de destino, uma OverflowException exceção será lançada.

O Decimal tipo também fornece métodos que convertem Decimal valores de e para e
Double valoresSingle. Conversões de Decimal ou SingleDouble estão restringindo
conversões que podem perder precisão, mas não informações sobre a magnitude do
valor convertido. A conversão não lança uma exceção.

Conversões de Single ou Double para Decimal lançar uma OverflowException exceção


se o resultado da conversão não puder ser representado como um Decimalarquivo .

Executar operações em valores decimais


O Decimal tipo suporta operações matemáticas padrão, como adição, subtração,
divisão, multiplicação e negação unária. Você também pode trabalhar diretamente com
a representação binária de um Decimal valor chamando o GetBits método.

Para comparar dois Decimal valores, você pode usar os operadores de comparação
numérica padrão ou pode chamar o CompareTo método ou Equals .

Você também pode chamar os Math membros da classe para executar uma ampla gama
de operações numéricas, incluindo obter o valor absoluto de um número, determinar o
valor máximo ou mínimo de dois Decimal valores, obter o sinal de um número e
arredondar um número.

Exemplos
O exemplo de código a seguir demonstra o uso de Decimal.

C#

/// <summary>
/// Keeping my fortune in Decimals to avoid the round-off errors.
/// </summary>
class PiggyBank {
protected decimal MyFortune;

public void AddPenny() {


MyFortune = Decimal.Add(MyFortune, .01m);
}

public decimal Capacity {


get {
return Decimal.MaxValue;
}
}

public decimal Dollars {


get {
return Decimal.Floor(MyFortune);
}
}

public decimal Cents {


get {
return Decimal.Subtract(MyFortune, Decimal.Floor(MyFortune));
}
}

public override string ToString() {


return MyFortune.ToString("C")+" in piggy bank";
}
}

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais
documentação
informações, confira o nosso
guia para colaboradores.
 Fornecer comentários sobre o
produto
System.Double struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Double tipo de valor representa um número de 64 bits de precisão dupla com valores
que variam de 1,79769313486232e308 negativo a 1,79769313486232e308, bem como
zero positivo ou negativo, PositiveInfinity, NegativeInfinitye não um número (NaN).
Destina-se a representar valores que são extremamente grandes (como distâncias entre
planetas ou galáxias) ou extremamente pequenos (como a massa molecular de uma
substância em quilogramas) e que muitas vezes são imprecisos (como a distância da
Terra a outro sistema solar). O Double tipo está em conformidade com o padrão IEC
60559:1989 (IEEE 754) para aritmética de ponto flutuante binário.

Representação de ponto flutuante e precisão


O Double tipo de dados armazena valores de ponto flutuante de precisão dupla em um
formato binário de 64 bits, conforme mostrado na tabela a seguir:

ノ Expandir a tabela

Parte Bits

Significand ou mantissa 0-51

Expoente 52-62

Sinal (0 = Positivo, 1 = Negativo) 63

Assim como as frações decimais são incapazes de representar com precisão alguns
valores fracionários (como 1/3 ou Math.PI), as frações binárias são incapazes de
representar alguns valores fracionários. Por exemplo, 1/10, que é representado
precisamente por .1 como uma fração decimal, é representado por .001100110011
como uma fração binária, com o padrão "0011" repetindo ao infinito. Nesse caso, o
valor de ponto flutuante fornece uma representação imprecisa do número que ele
representa. A realização de operações matemáticas adicionais no valor original do
ponto flutuante geralmente tende a aumentar sua falta de precisão. Por exemplo, se
compararmos o resultado de multiplicar .1 por 10 e adicionar .1 a .1 nove vezes,
veremos que a adição, por ter envolvido mais oito operações, produziu o resultado
menos preciso. Observe que essa disparidade é aparente somente se exibirmos os dois
Double valores usando a cadeia de caracteres de formato numérico padrão "R", que se
necessário exibe todos os 17 dígitos de precisão suportados Double pelo tipo.

C#

using System;

public class Example13


{
public static void Main()
{
Double value = .1;
Double result1 = value * 10;
Double result2 = 0;
for (int ctr = 1; ctr <= 10; ctr++)
result2 += value;

Console.WriteLine(".1 * 10: {0:R}", result1);


Console.WriteLine(".1 Added 10 times: {0:R}", result2);
}
}
// The example displays the following output:
// .1 * 10: 1
// .1 Added 10 times: 0.99999999999999989

Como alguns números não podem ser representados exatamente como valores binários
fracionários, os números de ponto flutuante só podem se aproximar de números reais.

Todos os números de ponto flutuante também têm um número limitado de dígitos


significativos, o que também determina com que precisão um valor de ponto flutuante
se aproxima de um número real. Um Double valor tem até 15 dígitos decimais de
precisão, embora um máximo de 17 dígitos seja mantido internamente. Isso significa
que algumas operações de ponto flutuante podem não ter a precisão necessária para
alterar um valor de ponto flutuante. O exemplo a seguir ilustra esse cenário. Ele define
um valor de ponto flutuante muito grande e, em seguida, adiciona o produto de
Double.Epsilon e um quatrilhão a ele. O produto, no entanto, é muito pequeno para
modificar o valor original do ponto flutuante. Seu dígito menos significativo é
milésimos, enquanto o dígito mais significativo no produto é 10-309.

C#

using System;

public class Example14


{
public static void Main()
{
Double value = 123456789012.34567;
Double additional = Double.Epsilon * 1e15;
Console.WriteLine("{0} + {1} = {2}", value, additional,
value + additional);
}
}
// The example displays the following output:
// 123456789012.346 + 4.94065645841247E-309 = 123456789012.346

A precisão limitada de um número de ponto flutuante tem várias consequências:

Dois números de ponto flutuante que pareçam iguais para uma determinada
precisão podem não ser comparados como iguais porque seus dígitos menos
significantes são diferentes. No exemplo a seguir, uma série de números é somada
e seu total é comparado com o total esperado. Embora os dois valores pareçam
ser os mesmos, uma chamada para o Equals método indica que eles não são.

C#

using System;

public class Example10


{
public static void Main()
{
Double[] values = { 10.0, 2.88, 2.88, 2.88, 9.0 };
Double result = 27.64;
Double total = 0;
foreach (var value in values)
total += value;

if (total.Equals(result))
Console.WriteLine("The sum of the values equals the
total.");
else
Console.WriteLine("The sum of the values ({0}) does not
equal the total ({1}).",
total, result);
}
}
// The example displays the following output:
// The sum of the values (36.64) does not equal the total (36.64).
//
// If the index items in the Console.WriteLine statement are changed to
{0:R},
// the example displays the following output:
// The sum of the values (27.639999999999997) does not equal the
total (27.64).

Se você alterar os itens de formato na Console.WriteLine(String, Object, Object)


instrução de e {1} para e {1:R} para {0:R} exibir todos os dígitos significativos
dos dois valores, ficará claro que os dois Double valores são desiguais devido a
uma perda de precisão durante as operações de {0} adição. Nesse caso, o
problema pode ser resolvido chamando o Math.Round(Double, Int32) método
para arredondar os Double valores para a precisão desejada antes de realizar a
comparação.

Uma operação matemática ou de comparação que usa um número de ponto


flutuante pode não produzir o mesmo resultado se um número decimal for usado,
porque o número de ponto flutuante binário pode não ser igual ao número
decimal. Um exemplo anterior ilustrou isso exibindo o resultado de multiplicar .1
por 10 e adicionar .1 vezes.

Quando a precisão em operações numéricas com valores fracionários é


importante, você pode usar o Decimal tipo em vez do Double tipo. Quando a
precisão em operações numéricas com valores integrais além do intervalo dos
Int64 tipos ou UInt64 for importante, use o BigInteger tipo.

Um valor pode não ser de ida e volta se um número de ponto flutuante estiver
envolvido. Um valor é dito para ida e volta se uma operação converte um número
de ponto flutuante original em outra forma, uma operação inversa transforma a
forma convertida de volta em um número de ponto flutuante e o número final de
ponto flutuante não é igual ao número de ponto flutuante original. A viagem de
ida e volta pode falhar porque um ou mais dígitos menos significativos são
perdidos ou alterados em uma conversão. No exemplo a seguir, três Double
valores são convertidos em cadeias de caracteres e salvos em um arquivo. Como a
saída mostra, no entanto, mesmo que os valores pareçam ser idênticos, os valores
restaurados não são iguais aos valores originais.

C#

using System;
using System.IO;

public class Example11


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\Doubles.dat");
Double[] values = { 2.2 / 1.01, 1.0 / 3, Math.PI };
for (int ctr = 0; ctr < values.Length; ctr++)
{
sw.Write(values[ctr].ToString());
if (ctr != values.Length - 1)
sw.Write("|");
}
sw.Close();
Double[] restoredValues = new Double[values.Length];
StreamReader sr = new StreamReader(@".\Doubles.dat");
string temp = sr.ReadToEnd();
string[] tempStrings = temp.Split('|');
for (int ctr = 0; ctr < tempStrings.Length; ctr++)
restoredValues[ctr] = Double.Parse(tempStrings[ctr]);

for (int ctr = 0; ctr < values.Length; ctr++)


Console.WriteLine("{0} {2} {1}", values[ctr],
restoredValues[ctr],
values[ctr].Equals(restoredValues[ctr]) ?
"=" : "<>");
}
}
// The example displays the following output:
// 2.17821782178218 <> 2.17821782178218
// 0.333333333333333 <> 0.333333333333333
// 3.14159265358979 <> 3.14159265358979

Nesse caso, os valores podem ser arredondados com êxito usando a cadeia de
caracteres de formato numérico padrão "G17" para preservar a precisão total dos
Double valores, como mostra o exemplo a seguir.

C#

using System;
using System.IO;

public class Example12


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\Doubles.dat");
Double[] values = { 2.2 / 1.01, 1.0 / 3, Math.PI };
for (int ctr = 0; ctr < values.Length; ctr++)
sw.Write("{0:G17}{1}", values[ctr], ctr < values.Length - 1
? "|" : "");

sw.Close();

Double[] restoredValues = new Double[values.Length];


StreamReader sr = new StreamReader(@".\Doubles.dat");
string temp = sr.ReadToEnd();
string[] tempStrings = temp.Split('|');
for (int ctr = 0; ctr < tempStrings.Length; ctr++)
restoredValues[ctr] = Double.Parse(tempStrings[ctr]);

for (int ctr = 0; ctr < values.Length; ctr++)


Console.WriteLine("{0} {2} {1}", values[ctr],
restoredValues[ctr],
values[ctr].Equals(restoredValues[ctr]) ?
"=" : "<>");
}
}
// The example displays the following output:
// 2.17821782178218 = 2.17821782178218
// 0.333333333333333 = 0.333333333333333
// 3.14159265358979 = 3.14159265358979

) Importante

Quando usado com um Double valor, o especificador de formato "R" em


alguns casos falha ao realizar com êxito a viagem de ida e volta do valor
original. Para garantir que Double os valores sejam bem-sucedidos de ida e
volta, use o especificador de formato "G17".

Single os valores têm menos precisão do que Double os valores. Um Single valor
que é convertido em um aparentemente equivalente Double muitas vezes não é
igual ao Double valor devido a diferenças na precisão. No exemplo a seguir, o
resultado de operações de divisão idênticas é atribuído a um Double e a valor
Single . Depois que o Single valor é convertido em um Double, uma comparação
dos dois valores mostra que eles são desiguais.

C#

using System;

public class Example9


{
public static void Main()
{
Double value1 = 1 / 3.0;
Single sValue2 = 1 / 3.0f;
Double value2 = (Double)sValue2;
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.33333333333333331 = 0.3333333432674408: False

Para evitar esse problema, use o no lugar do tipo de Single dados ou use o
DoubleRound método para que ambos os valores tenham a mesma precisão.

Além disso, o resultado de operações aritméticas e de atribuição com Double valores


pode diferir ligeiramente por plataforma devido à perda de precisão do Double tipo. Por
exemplo, o resultado da atribuição de um valor literal Double pode diferir nas versões
de 32 bits e 64 bits do .NET. O exemplo a seguir ilustra essa diferença quando o valor
literal -4.42330604244772E-305 e uma variável cujo valor é -4.42330604244772E-305
são atribuídos a uma Double variável. Note que o resultado do método, Parse(String)
neste caso, não sofre de perda de precisão.

C#

double value = -4.42330604244772E-305;

double fromLiteral = -4.42330604244772E-305;


double fromVariable = value;
double fromParse = Double.Parse("-4.42330604244772E-305");

Console.WriteLine("Double value from literal: {0,29:R}", fromLiteral);


Console.WriteLine("Double value from variable: {0,28:R}", fromVariable);
Console.WriteLine("Double value from Parse method: {0,24:R}", fromParse);
// On 32-bit versions of the .NET Framework, the output is:
// Double value from literal: -4.42330604244772E-305
// Double value from variable: -4.42330604244772E-305
// Double value from Parse method: -4.42330604244772E-305
//
// On other versions of the .NET Framework, the output is:
// Double value from literal: -4.4233060424477198E-305
// Double value from variable: -4.4233060424477198E-305
// Double value from Parse method: -4.42330604244772E-305

Teste de igualdade
Para serem considerados iguais, dois Double valores devem representar valores
idênticos. No entanto, devido a diferenças de precisão entre valores, ou devido a uma
perda de precisão por um ou ambos os valores, os valores de ponto flutuante que se
espera que sejam idênticos muitas vezes acabam por ser desiguais devido a diferenças
nos seus dígitos menos significativos. Como resultado, chamadas para o método para
determinar se dois valores são iguais, ou chamadas para o EqualsCompareTo método
para determinar a relação entre dois Double valores, geralmente produzem resultados
inesperados. Isso fica evidente no exemplo a seguir, onde dois valores aparentemente
iguais Double acabam sendo desiguais porque o primeiro tem 15 dígitos de precisão,
enquanto o segundo tem 17.

C#

using System;

public class Example


{
public static void Main()
{
double value1 = .333333333333333;
double value2 = 1.0/3;
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.333333333333333 = 0.33333333333333331: False

Valores calculados que seguem caminhos de código diferentes e que são manipulados
de maneiras diferentes geralmente se mostram desiguais. No exemplo a seguir, um
Double valor é quadrado e, em seguida, a raiz quadrada é calculada para restaurar o
valor original. Um segundo Double é multiplicado por 3,51 e quadrado antes que a raiz
quadrada do resultado seja dividida por 3,51 para restaurar o valor original. Embora os
dois valores pareçam ser idênticos, uma chamada para o Equals(Double) método indica
que eles não são iguais. Usar a cadeia de caracteres de formato padrão "R" para
retornar uma cadeia de caracteres de resultado que exibe todos os dígitos significativos
de cada valor Double mostra que o segundo valor é .0000000000001 menor que o
primeiro.

C#

using System;

public class Example1


{
public static void Main()
{
double value1 = 100.10142;
value1 = Math.Sqrt(Math.Pow(value1, 2));
double value2 = Math.Pow(value1 * 3.51, 2);
value2 = Math.Sqrt(value2) / 3.51;
Console.WriteLine("{0} = {1}: {2}\n",
value1, value2, value1.Equals(value2));
Console.WriteLine("{0:R} = {1:R}", value1, value2);
}
}
// The example displays the following output:
// 100.10142 = 100.10142: False
//
// 100.10142 = 100.10141999999999

Nos casos em que uma perda de precisão provavelmente afetará o resultado de uma
comparação, você pode adotar qualquer uma das seguintes alternativas para chamar o
Equals método ou CompareTo :

Chame o Math.Round método para garantir que ambos os valores tenham a


mesma precisão. O exemplo a seguir modifica um exemplo anterior para usar essa
abordagem para que dois valores fracionários sejam equivalentes.

C#

using System;

public class Example2


{
public static void Main()
{
double value1 = .333333333333333;
double value2 = 1.0 / 3;
int precision = 7;
value1 = Math.Round(value1, precision);
value2 = Math.Round(value2, precision);
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.3333333 = 0.3333333: True

O problema da precisão ainda se aplica ao arredondamento dos valores médios.


Para obter mais informações, consulte o método Math.Round(Double, Int32,
MidpointRounding).

Teste de igualdade aproximada em vez de igualdade. Isso requer que você defina
uma quantidade absoluta pela qual os dois valores podem diferir, mas ainda assim
serem iguais, ou que você defina uma quantidade relativa pela qual o valor menor
pode divergir do valor maior.

2 Aviso

Double.Epsilon às vezes é usado como uma medida absoluta da distância


entre dois Double valores ao testar a igualdade. No entanto, Double.Epsilon
mede o menor valor possível que pode ser adicionado ou subtraído de um
Double cujo valor é zero. Para a maioria dos valores positivos e negativos
Double , o valor de Double.Epsilon é muito pequeno para ser detectado.
Portanto, com exceção dos valores que são zero, não recomendamos seu uso
em testes de igualdade.

O exemplo a seguir usa a última abordagem para definir um IsApproximatelyEqual


método que testa a diferença relativa entre dois valores. Ele também contrasta o
resultado das chamadas com o método e o IsApproximatelyEqual Equals(Double)
método.
C#

using System;

public class Example3


{
public static void Main()
{
double one1 = .1 * 10;
double one2 = 0;
for (int ctr = 1; ctr <= 10; ctr++)
one2 += .1;

Console.WriteLine("{0:R} = {1:R}: {2}", one1, one2,


one1.Equals(one2));
Console.WriteLine("{0:R} is approximately equal to {1:R}: {2}",
one1, one2,
IsApproximatelyEqual(one1, one2,
.000000001));
}

static bool IsApproximatelyEqual(double value1, double value2,


double epsilon)
{
// If they are equal anyway, just return True.
if (value1.Equals(value2))
return true;

// Handle NaN, Infinity.


if (Double.IsInfinity(value1) | Double.IsNaN(value1))
return value1.Equals(value2);
else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
return value1.Equals(value2);

// Handle zero to avoid division by zero


double divisor = Math.Max(value1, value2);
if (divisor.Equals(0))
divisor = Math.Min(value1, value2);

return Math.Abs((value1 - value2) / divisor) <= epsilon;


}
}
// The example displays the following output:
// 1 = 0.99999999999999989: False
// 1 is approximately equal to 0.99999999999999989: True

Valores de ponto flutuante e exceções


Ao contrário das operações com tipos integrais, que lançam exceções em casos de
estouro ou operações ilegais, como divisão por zero, as operações com valores de
ponto flutuante não lançam exceções. Em vez disso, em situações excepcionais, o
resultado de uma operação de ponto flutuante é zero, infinito positivo, infinito negativo
ou não um número (NaN):

Se o resultado de uma operação de ponto flutuante for muito pequeno para o


formato de destino, o resultado será zero. Isso pode ocorrer quando dois números
muito pequenos são multiplicados, como mostra o exemplo a seguir.

C#

using System;

public class Example6


{
public static void Main()
{
Double value1 = 1.1632875981534209e-225;
Double value2 = 9.1642346778e-175;
Double result = value1 * value2;
Console.WriteLine("{0} * {1} = {2}", value1, value2, result);
Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0));
}
}
// The example displays the following output:
// 1.16328759815342E-225 * 9.1642346778E-175 = 0
// 0 = 0: True

Se a magnitude do resultado de uma operação de ponto flutuante exceder o


intervalo do formato de destino, o resultado da operação será PositiveInfinity ou
NegativeInfinity, conforme apropriado para o sinal do resultado. O resultado de
uma operação que estoura é , e o resultado de uma operação que estoura
Double.MaxValueDouble.MinValue é PositiveInfinityNegativeInfinity, como mostra
o exemplo a seguir.

C#

using System;

public class Example7


{
public static void Main()
{
Double value1 = 4.565e153;
Double value2 = 6.9375e172;
Double result = value1 * value2;
Console.WriteLine("PositiveInfinity: {0}",
Double.IsPositiveInfinity(result));
Console.WriteLine("NegativeInfinity: {0}\n",
Double.IsNegativeInfinity(result));
value1 = -value1;
result = value1 * value2;
Console.WriteLine("PositiveInfinity: {0}",
Double.IsPositiveInfinity(result));
Console.WriteLine("NegativeInfinity: {0}",
Double.IsNegativeInfinity(result));
}
}

// The example displays the following output:


// PositiveInfinity: True
// NegativeInfinity: False
//
// PositiveInfinity: False
// NegativeInfinity: True

PositiveInfinity também resulta de uma divisão por zero com um dividendo


positivo, e NegativeInfinity resulta de uma divisão por zero com um dividendo
negativo.

Se uma operação de ponto flutuante for inválida, o resultado da operação será


NaN. Por exemplo, NaN resultados das seguintes operações:

Divisão por zero com dividendo zero. Note que outros casos de divisão por zero
resultam em um PositiveInfinity ou NegativeInfinityoutro .

Qualquer operação de ponto flutuante com uma entrada inválida. Por exemplo,
chamar o método com um valor negativo retorna NaN, assim como chamar o
Math.AcosMath.Sqrt método com um valor maior que um ou menor que um
negativo.

Qualquer operação com um argumento cujo valor seja Double.NaN.

Conversões de tipo
A Double estrutura não define nenhum operador de conversão explícito ou implícito,
em vez disso, as conversões são implementadas pelo compilador.

A conversão do valor de qualquer tipo numérico primitivo em a é uma conversão de


ampliação e, portanto, não requer um operador de conversão explícito ou chamada
para um método de conversão, a Double menos que um compilador exija isso
explicitamente. Por exemplo, o compilador C# requer um operador de conversão para
conversões de Decimal para Double, enquanto o compilador do Visual Basic não. O
exemplo a seguir converte o valor mínimo ou máximo de outros tipos numéricos
primitivos em um Doublearquivo .
C#

using System;

public class Example4


{
public static void Main()
{
dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
Decimal.MaxValue, Int16.MinValue, Int16.MaxValue,
Int32.MinValue, Int32.MaxValue, Int64.MinValue,
Int64.MaxValue, SByte.MinValue, SByte.MaxValue,
Single.MinValue, Single.MaxValue,
UInt16.MinValue,
UInt16.MaxValue, UInt32.MinValue,
UInt32.MaxValue,
UInt64.MinValue, UInt64.MaxValue };
double dblValue;
foreach (var value in values)
{
if (value.GetType() == typeof(Decimal))
dblValue = (Double)value;
else
dblValue = value;
Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
value, value.GetType().Name,
dblValue, dblValue.GetType().Name);
}
}
}
// The example displays the following output:
// 0 (Byte) --> 0 (Double)
// 255 (Byte) --> 255 (Double)
// -79228162514264337593543950335 (Decimal) --> -7.9228162514264338E+28
(Double)
// 79228162514264337593543950335 (Decimal) --> 7.9228162514264338E+28
(Double)
// -32768 (Int16) --> -32768 (Double)
// 32767 (Int16) --> 32767 (Double)
// -2147483648 (Int32) --> -2147483648 (Double)
// 2147483647 (Int32) --> 2147483647 (Double)
// -9223372036854775808 (Int64) --> -9.2233720368547758E+18 (Double)
// 9223372036854775807 (Int64) --> 9.2233720368547758E+18 (Double)
// -128 (SByte) --> -128 (Double)
// 127 (SByte) --> 127 (Double)
// -3.402823E+38 (Single) --> -3.4028234663852886E+38 (Double)
// 3.402823E+38 (Single) --> 3.4028234663852886E+38 (Double)
// 0 (UInt16) --> 0 (Double)
// 65535 (UInt16) --> 65535 (Double)
// 0 (UInt32) --> 0 (Double)
// 4294967295 (UInt32) --> 4294967295 (Double)
// 0 (UInt64) --> 0 (Double)
// 18446744073709551615 (UInt64) --> 1.8446744073709552E+19 (Double)
Além disso, os Single valores Single.NaN, e convertem em Double.NaN, e
Single.NegativeInfinityDouble.NegativeInfinity,
Single.PositiveInfinityDouble.PositiveInfinityrespectivamente.

Observe que a conversão do valor de alguns tipos numéricos em um Double valor pode
envolver uma perda de precisão. Como o exemplo ilustra, uma perda de precisão é
possível ao converter Decimal, Int64e UInt64 valores em Double valores.

A conversão de um valor em um valor de qualquer outro tipo de dados numérico


primitivo é uma conversão de estreitamento e requer um operador de conversão (em
C#), um método de conversão (no Visual Basic) ou uma chamada para um
DoubleConvert método. Os valores que estão fora do intervalo do tipo de dados de
destino, que são definidos pelas propriedades e MaxValue do tipo de destino,
comportam-se MinValue conforme mostrado na tabela a seguir.

ノ Expandir a tabela

Tipo de Resultado
destino

Qualquer tipo Uma OverflowException exceção se a conversão ocorrer em um contexto


integral verificado.

Se a conversão ocorrer em um contexto não verificado (o padrão em C#), a


operação de conversão será bem-sucedida, mas o valor estourará.

Decimal Uma exceção OverflowException.

Single Single.NegativeInfinity para valores negativos.

Single.PositiveInfinity para valores positivos.

Além disso, , e Double.NegativeInfinity lançar um para conversões em inteiros em um


contexto verificado, Double.NaNDouble.PositiveInfinitymas esses valores transbordam
quando convertidos em inteiros em um OverflowException contexto não verificado. Para
conversões para Decimal, eles sempre lançam um OverflowExceptionarquivo . Para
conversões em , elas convertem em SingleSingle.NaN, e Single.NegativeInfinity,
Single.PositiveInfinityrespectivamente.

Uma perda de precisão pode resultar da conversão de um Double valor em outro tipo
numérico. No caso de conversão para qualquer um dos tipos integrais, como mostra a
saída do exemplo, o componente fracionário é perdido quando o Double valor é
arredondado (como no Visual Basic) ou truncado (como em C#). Para conversões para
Decimal e Single valores, o Double valor pode não ter uma representação precisa no
tipo de dados de destino.
O exemplo a seguir converte vários Double valores em vários outros tipos numéricos. As
conversões ocorrem em um contexto verificado no Visual Basic (o padrão), em C#
(devido à palavra-chave verificada) e em F# (devido ao módulo Verificado ). A saída do
exemplo mostra o resultado para conversões em um contexto verificado e não
verificado. Você pode executar conversões em um contexto não verificado no Visual
Basic compilando com a opção do compilador, em C# comentando a instrução e em F#
comentando a /removeintchecks+ checked open Checked instrução.

C#

using System;

public class Example5


{
public static void Main()
{
Double[] values = { Double.MinValue, -67890.1234, -12345.6789,
12345.6789, 67890.1234, Double.MaxValue,
Double.NaN, Double.PositiveInfinity,
Double.NegativeInfinity };
checked
{
foreach (var value in values)
{
try
{
Int64 lValue = (long)value;
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
lValue, lValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Int64.",
value);
}
try
{
UInt64 ulValue = (ulong)value;
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
ulValue, ulValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to UInt64.",
value);
}
try
{
Decimal dValue = (decimal)value;
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dValue, dValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Decimal.",
value);
}
try
{
Single sValue = (float)value;
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
sValue, sValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Single.",
value);
}
Console.WriteLine();
}
}
}
}
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -1.79769313486232E+308 to Int64.
// Unable to convert -1.79769313486232E+308 to UInt64.
// Unable to convert -1.79769313486232E+308 to Decimal.
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
// -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.1234 to UInt64.
// -67890.1234 (Double) --> -67890.1234 (Decimal)
// -67890.1234 (Double) --> -67890.13 (Single)
//
// -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.6789 to UInt64.
// -12345.6789 (Double) --> -12345.6789 (Decimal)
// -12345.6789 (Double) --> -12345.68 (Single)
//
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
// 12345.6789 (Double) --> 12345.6789 (Decimal)
// 12345.6789 (Double) --> 12345.68 (Single)
//
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
// 67890.1234 (Double) --> 67890.1234 (Decimal)
// 67890.1234 (Double) --> 67890.13 (Single)
//
// Unable to convert 1.79769313486232E+308 to Int64.
// Unable to convert 1.79769313486232E+308 to UInt64.
// Unable to convert 1.79769313486232E+308 to Decimal.
// 1.79769313486232E+308 (Double) --> Infinity (Single)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Double) --> NaN (Single)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Double) --> Infinity (Single)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Double) --> -Infinity (Single)
// The example displays the following output for conversions performed
// in an unchecked context:
// -1.79769313486232E+308 (Double) --> -9223372036854775808
(0x8000000000000000) (Int64)
// -1.79769313486232E+308 (Double) --> 9223372036854775808
(0x8000000000000000) (UInt64)
// Unable to convert -1.79769313486232E+308 to Decimal.
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
//
// -67890.1234 (Double) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.1234 (Double) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE)
(UInt64)
// -67890.1234 (Double) --> -67890.1234 (Decimal)
// -67890.1234 (Double) --> -67890.13 (Single)
//
// -12345.6789 (Double) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.6789 (Double) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7)
(UInt64)
// -12345.6789 (Double) --> -12345.6789 (Decimal)
// -12345.6789 (Double) --> -12345.68 (Single)
//
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (Int64)
// 12345.6789 (Double) --> 12345 (0x0000000000003039) (UInt64)
// 12345.6789 (Double) --> 12345.6789 (Decimal)
// 12345.6789 (Double) --> 12345.68 (Single)
//
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (Int64)
// 67890.1234 (Double) --> 67890 (0x0000000000010932) (UInt64)
// 67890.1234 (Double) --> 67890.1234 (Decimal)
// 67890.1234 (Double) --> 67890.13 (Single)
//
// 1.79769313486232E+308 (Double) --> -9223372036854775808
(0x8000000000000000) (Int64)
// 1.79769313486232E+308 (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 1.79769313486232E+308 to Decimal.
// 1.79769313486232E+308 (Double) --> Infinity (Single)
//
// NaN (Double) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Double) --> NaN (Single)
//
// Infinity (Double) --> -9223372036854775808 (0x8000000000000000)
(Int64)
// Infinity (Double) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Double) --> Infinity (Single)
//
// -Infinity (Double) --> -9223372036854775808 (0x8000000000000000)
(Int64)
// -Infinity (Double) --> 9223372036854775808 (0x8000000000000000)
(UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Double) --> -Infinity (Single)

Para obter mais informações sobre a conversão de tipos numéricos, consulte Conversão
de tipo no .NET e Tabelas de conversão de tipo.

Funcionalidade de ponto flutuante


A Double estrutura e os tipos relacionados fornecem métodos para executar operações
nas seguintes áreas:

Comparação de valores. Você pode chamar o método para determinar se dois


valores são iguais ou o EqualsCompareTo método para determinar a relação entre
dois Double valores.

A Double estrutura também suporta um conjunto completo de operadores de


comparação. Por exemplo, você pode testar a igualdade ou a desigualdade, ou
determinar se um valor é maior ou igual a outro. Se um dos operandos for um tipo
numérico diferente de um , ele será convertido em um DoubleDouble antes de
realizar a comparação.

2 Aviso

Devido a diferenças na precisão, dois Double valores que você espera que
sejam iguais podem se tornar desiguais, o que afeta o resultado da
comparação. Consulte a seção Testar para igualdade para obter mais
informações sobre como comparar dois Double valores.

Você também pode chamar os IsNaNmétodos , IsInfinity, IsPositiveInfinitye


IsNegativeInfinity para testar esses valores especiais.
Operações matemáticas. Operações aritméticas comuns, como adição, subtração,
multiplicação e divisão, são implementadas por compiladores de linguagem e
instruções Common Intermediate Language (CIL), em vez de por Double métodos.
Se um dos operandos em uma operação matemática for um tipo numérico
diferente de um , ele será convertido em um DoubleDouble antes de executar a
operação. O resultado da operação também é um Double valor.

Outras operações matemáticas podem ser executadas chamando static ( Shared


no Visual Basic) métodos na System.Math classe. Ele inclui métodos adicionais
comumente usados para aritmética (como , e ), geometria (como e ) e
Math.SinMath.Sqrtcálculo (como Math.AbsMath.CosMath.Log). Math.Sign

Você também pode manipular os bits individuais em um Double valor. O


BitConverter.DoubleToInt64Bits método preserva o padrão de bits de um valor em
um Double inteiro de 64 bits. O BitConverter.GetBytes(Double) método retorna seu
padrão de bits em uma matriz de bytes.

Arredondamento. O arredondamento é frequentemente usado como uma técnica


para reduzir o impacto das diferenças entre os valores causadas por problemas de
representação e precisão do ponto flutuante. Você pode arredondar um Double
valor chamando o Math.Round método.

Formatação. Você pode converter um Double valor em sua representação de


cadeia de caracteres chamando o método ou usando o ToString recurso de
formatação composta. Para obter informações sobre como as cadeias de
caracteres de formato controlam a representação de cadeias de caracteres de
valores de ponto flutuante, consulte os tópicos Cadeias de caracteres de formato
numérico padrão e Cadeias de caracteres de formato numérico personalizado.

Analisando cadeias de caracteres. Você pode converter a representação de cadeia


de caracteres de um valor de ponto flutuante em um Double valor chamando o
Parse método ou TryParse . Se a operação de análise falhar, o método lançará uma
exceção, enquanto o ParseTryParse método retornará false .

Conversão de tipos. A Double estrutura fornece uma implementação de interface


explícita para a IConvertible interface, que oferece suporte à conversão entre
quaisquer dois tipos de dados padrão do .NET Framework. Os compiladores de
linguagem também suportam a conversão implícita de valores de todos os outros
tipos numéricos padrão em Double valores. A conversão de um valor de qualquer
tipo numérico padrão em um é uma conversão de alargamento e não requer o
usuário de um Double operador de fundição ou método de conversão,
No entanto, a conversão de e Single valores pode envolver uma perda de Int64
precisão. A tabela a seguir lista as diferenças de precisão para cada um desses
tipos:

ノ Expandir a tabela

Tipo Máxima precisão Precisão interna

Double 15 17

Int64 19 dígitos decimais 19 dígitos decimais

Single 7 dígitos decimais 9 dígitos decimais

O problema da precisão afeta Single mais frequentemente os valores que são


convertidos em Double valores. No exemplo a seguir, dois valores produzidos por
operações de divisão idênticas são desiguais porque um dos valores é um valor de
ponto flutuante de precisão única convertido em um Doublearquivo .

C#

using System;

public class Example13


{
public static void Main()
{
Double value = .1;
Double result1 = value * 10;
Double result2 = 0;
for (int ctr = 1; ctr <= 10; ctr++)
result2 += value;

Console.WriteLine(".1 * 10: {0:R}", result1);


Console.WriteLine(".1 Added 10 times: {0:R}", result2);
}
}
// The example displays the following output:
// .1 * 10: 1
// .1 Added 10 times: 0.99999999999999989

Exemplos
O exemplo de código a seguir ilustra o uso de Double:

C#
// The Temperature class stores the temperature as a Double
// and delegates most of the functionality to the Double
// implementation.
public class Temperature : IComparable, IFormattable
{
// IComparable.CompareTo implementation.
public int CompareTo(object obj) {
if (obj == null) return 1;

Temperature temp = obj as Temperature;


if (obj != null)
return m_value.CompareTo(temp.m_value);
else
throw new ArgumentException("object is not a Temperature");
}

// IFormattable.ToString implementation.
public string ToString(string format, IFormatProvider provider) {
if( format != null ) {
if( format.Equals("F") ) {
return String.Format("{0}'F", this.Value.ToString());
}
if( format.Equals("C") ) {
return String.Format("{0}'C", this.Celsius.ToString());
}
}

return m_value.ToString(format, provider);


}

// Parses the temperature from a string in the form


// [ws][sign]digits['F|'C][ws]
public static Temperature Parse(string s, NumberStyles styles,
IFormatProvider provider) {
Temperature temp = new Temperature();

if( s.TrimEnd(null).EndsWith("'F") ) {
temp.Value = Double.Parse( s.Remove(s.LastIndexOf('\''), 2),
styles, provider);
}
else if( s.TrimEnd(null).EndsWith("'C") ) {
temp.Celsius = Double.Parse( s.Remove(s.LastIndexOf('\''), 2),
styles, provider);
}
else {
temp.Value = Double.Parse(s, styles, provider);
}

return temp;
}

// The value holder


protected double m_value;
public double Value {
get {
return m_value;
}
set {
m_value = value;
}
}

public double Celsius {


get {
return (m_value-32.0)/1.8;
}
set {
m_value = 1.8*value+32.0;
}
}
}

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Métodos System.Double.CompareTo
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Método CompareTo(Double)
Os valores devem ser idênticos para serem considerados iguais. Particularmente quando
os valores de ponto flutuante dependem de múltiplas operações matemáticas, é comum
que eles percam precisão e que seus valores sejam quase idênticos, exceto por seus
dígitos menos significativos. Devido a isso, o valor de retorno do CompareTo método às
vezes pode parecer surpreendente. Por exemplo, a multiplicação por um determinado
valor seguida de divisão pelo mesmo valor deve produzir o valor original. No exemplo a
seguir, no entanto, o valor calculado acaba sendo maior do que o valor original. Mostrar
todos os dígitos significativos dos dois valores usando a cadeia de caracteres de
formato numérico padrão "R" indica que o valor calculado difere do valor original em
seus dígitos menos significativos. Para obter informações sobre como lidar com essas
comparações, consulte a seção Comentários do Equals(Double) método.

C#

using System;

public class Example


{
public static void Main()
{
double value1 = 6.185;
double value2 = value1 * .1 / .1;
Console.WriteLine("Comparing {0} and {1}: {2}\n",
value1, value2, value1.CompareTo(value2));
Console.WriteLine("Comparing {0:R} and {1:R}: {2}",
value1, value2, value1.CompareTo(value2));
}
}
// The example displays the following output:
// Comparing 6.185 and 6.185: -1
//
// Comparing 6.185 and 6.1850000000000005: -1

Esse método implementa a System.IComparable<T> interface e executa um pouco


melhor do que o método porque ele não precisa converter o value Double.CompareTo
parâmetro em um objeto.
Observe que, embora um objeto cujo valor é não seja considerado igual a outro objeto
cujo valor é NaNNaN (mesmo ele mesmo), a interface requer que A.CompareTo(A)
retorne IComparable<T> zero.

Método CompareTo(Object)
O value parâmetro deve ser null ou uma instância de Double ; caso contrário, uma
exceção é lançada. Qualquer instância de , independentemente de Doubleseu valor, é
considerada maior que null .

Os valores devem ser idênticos para serem considerados iguais. Particularmente quando
os valores de ponto flutuante dependem de múltiplas operações matemáticas, é comum
que eles percam precisão e que seus valores sejam quase idênticos, exceto por seus
dígitos menos significativos. Devido a isso, o valor de retorno do CompareTo método às
vezes pode parecer surpreendente. Por exemplo, a multiplicação por um determinado
valor seguida de divisão pelo mesmo valor deve produzir o valor original. No exemplo a
seguir, no entanto, o valor calculado acaba sendo maior do que o valor original. Mostrar
todos os dígitos significativos dos dois valores usando a cadeia de caracteres de
formato numérico padrão "R" indica que o valor calculado difere do valor original em
seus dígitos menos significativos. Para obter informações sobre como lidar com essas
comparações, consulte a seção Comentários do Equals(Double) método.

C#

using System;

public class Example3


{
public static void Main()
{
double value1 = 6.185;
object value2 = value1 * .1 / .1;
Console.WriteLine("Comparing {0} and {1}: {2}\n",
value1, value2, value1.CompareTo(value2));
Console.WriteLine("Comparing {0:R} and {1:R}: {2}",
value1, value2, value1.CompareTo(value2));
}
}
// The example displays the following output:
// Comparing 6.185 and 6.185: -1
//
// Comparing 6.185 and 6.1850000000000005: -1

Este método é implementado para suportar a IComparable interface. Observe que,


embora um NaN não seja considerado igual a outro NaN (mesmo ele mesmo), a
interface requer que A.CompareTo(A) retorne IComparable zero.

Conversões de expansão
Dependendo da linguagem de programação, talvez seja possível codificar um
CompareTo método em que o tipo de parâmetro tem menos bits (é mais estreito) do
que o tipo de instância. Isso é possível porque algumas linguagens de programação
executam uma conversão de ampliação implícita que representa o parâmetro como um
tipo com tantos bit quanto a instância.

Por exemplo, suponha que o tipo de instância seja Double e o tipo de parâmetro seja
Int32. O compilador do Microsoft C# gera instruções para representar o valor do
parâmetro como um objeto e, em seguida, gera um DoubleDouble.CompareTo(Double)
método que compara os valores da instância e a representação ampliada do parâmetro.

Consulte a documentação da linguagem de programação para determinar se o


compilador executa conversões ampliadoras implícitas de tipos numéricos. Para obter
mais informações, consulte o tópico Tabelas de conversão de tipo.

Precisão nas comparações


A precisão dos números de ponto flutuante além da precisão documentada é específica
para a implementação e a versão do .NET. Consequentemente, uma comparação de
dois números específicos pode mudar entre as versões do .NET porque a precisão da
representação interna dos números pode mudar.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.Double.Equals
Artigo • 30/01/2024

O método Th Double.Equals(Double) implementa a System.IEquatable<T> interface e


executa um pouco melhor do que Double.Equals(Object) porque não precisa converter
o obj parâmetro em um objeto.

Conversões de expansão
Dependendo da linguagem de programação, talvez seja possível codificar um Equals
método em que o tipo de parâmetro tem menos bits (é mais estreito) do que o tipo de
instância. Isso é possível porque algumas linguagens de programação executam uma
conversão de ampliação implícita que representa o parâmetro como um tipo com
tantos bit quanto a instância.

Por exemplo, suponha que o tipo de instância seja Double e o tipo de parâmetro seja
Int32. O compilador do Microsoft C# gera instruções para representar o valor do
parâmetro como um objeto e, em seguida, gera um DoubleDouble.Equals(Double)
método que compara os valores da instância e a representação ampliada do parâmetro.

Consulte a documentação da linguagem de programação para determinar se o


compilador executa conversões ampliadoras implícitas de tipos numéricos. Para obter
mais informações, consulte o tópico Tabelas de conversão de tipo.

Precisão nas comparações


O Equals método deve ser usado com cautela, pois dois valores aparentemente
equivalentes podem ser desiguais devido à precisão diferente dos dois valores. O
exemplo a seguir relata que o valor .333333 e o DoubleDouble valor retornado
dividindo 1 por 3 são desiguais.

C#

// Initialize two doubles with apparently identical values


double double1 = .33333;
double double2 = (double) 1/3;
// Compare them for equality
Console.WriteLine(double1.Equals(double2)); // displays false

Em vez de comparar para igualdade, uma técnica envolve definir uma margem relativa
aceitável de diferença entre dois valores (como .001% de um dos valores). Se o valor
absoluto da diferença entre os dois valores for inferior ou igual a essa margem, é
provável que a diferença se deva a diferenças de precisão e, por conseguinte, é provável
que os valores sejam iguais. O exemplo a seguir usa essa técnica para comparar .33333
e 1/3, os dois Double valores que o exemplo de código anterior considerou desiguais.
Nesse caso, os valores são iguais.

C#

// Initialize two doubles with apparently identical values


double double1 = .333333;
double double2 = (double) 1/3;
// Define the tolerance for variation in their values
double difference = Math.Abs(double1 * .00001);

// Compare the values


// The output to the console indicates that the two values are equal
if (Math.Abs(double1 - double2) <= difference)
Console.WriteLine("double1 and double2 are equal.");
else
Console.WriteLine("double1 and double2 are unequal.");

7 Observação

Como Epsilon define a expressão mínima de um valor positivo cujo intervalo é


próximo de zero, a margem de diferença entre dois valores semelhantes deve ser
maior que Epsilon. Normalmente, é muitas vezes maior do que Epsilon. Por isso,
recomendamos que você não use Epsilon ao comparar Double valores para
igualdade.

Uma segunda técnica envolve comparar a diferença entre dois números de ponto
flutuante com algum valor absoluto. Se a diferença for menor ou igual a esse valor
absoluto, os números serão iguais. Se for maior, os números não são iguais. Uma
alternativa é selecionar arbitrariamente um valor absoluto. Isso é problemático, no
entanto, porque uma margem de diferença aceitável depende da magnitude dos
Double valores. Uma segunda alternativa aproveita um recurso de design do formato de
ponto flutuante: a diferença entre a representação inteira de dois valores de ponto
flutuante indica o número de possíveis valores de ponto flutuante que os separa. Por
exemplo, a diferença entre 0,0 e Epsilon é 1, porque Epsilon é o menor valor
representável quando se trabalha com um Double cujo valor é zero. O exemplo a seguir
usa essa técnica para comparar .33333 e 1/3, que são os dois Double valores que o
exemplo de código anterior com o Equals(Double) método encontrado para ser
desigual. Observe que o exemplo usa o BitConverter.DoubleToInt64Bits método para
converter um valor de ponto flutuante de precisão dupla em sua representação inteira.
C#

using System;

public class Example


{
public static void Main()
{
double value1 = .1 * 10;
double value2 = 0;
for (int ctr = 0; ctr < 10; ctr++)
value2 += .1;

Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,


HasMinimalDifference(value1, value2, 1));
}

public static bool HasMinimalDifference(double value1, double value2, int


units)
{
long lValue1 = BitConverter.DoubleToInt64Bits(value1);
long lValue2 = BitConverter.DoubleToInt64Bits(value2);

// If the signs are different, return false except for +0 and -0.
if ((lValue1 >> 63) != (lValue2 >> 63))
{
if (value1 == value2)
return true;

return false;
}

long diff = Math.Abs(lValue1 - lValue2);

if (diff <= (long) units)


return true;

return false;
}
}
// The example displays the following output:
// 1 = 0.99999999999999989: True

A precisão dos números de ponto flutuante além da precisão documentada é específica


da implementação e da versão do .NET Framework. Consequentemente, uma
comparação entre dois números específicos pode mudar entre versões do .NET
Framework porque a precisão da representação interna dos números pode mudar.

Se dois Double.NaN valores forem testados para igualdade chamando o método, o


Equals método retornará true . No entanto, se dois NaN valores forem testados para
igualdade usando o operador equality, o operador retornará false . Quando você
deseja determinar se o valor de a Double não é um número (NaN), uma alternativa é
chamar o IsNaN método.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Propriedade System.Double.Epsilon
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O valor da propriedade reflete o menor valor positivo Double que é significativo em


operações numéricas ou comparações quando o valor da EpsilonDouble instância é
zero. Por exemplo, o código a seguir mostra que zero e são considerados valores
desiguais, enquanto zero e Epsilon metade do valor de Epsilon são considerados iguais.

C#

using System;

public class Example


{
public static void Main()
{
double[] values = { 0, Double.Epsilon, Double.Epsilon * .5 };

for (int ctr = 0; ctr <= values.Length - 2; ctr++)


{
for (int ctr2 = ctr + 1; ctr2 <= values.Length - 1; ctr2++)
{
Console.WriteLine("{0:r} = {1:r}: {2}",
values[ctr], values[ctr2],
values[ctr].Equals(values[ctr2]));
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// 0 = 4.94065645841247E-324: False
// 0 = 0: True
//
// 4.94065645841247E-324 = 0: False

Mais precisamente, o formato de ponto flutuante consiste em um sinal, uma mantissa


ou significante de 52 bits e um expoente de 11 bits. Como mostra o exemplo a seguir,
zero tem um expoente de -1022 e uma mantissa de 0. Epsilon tem um expoente de
-1022 e uma mantissa de 1. Isso significa que é o menor valor positivo maior que
Epsilon zero e representa o menor valor possível e o menor incremento possível para
um Double cujo expoente é -1022.Double

C#
using System;

public class Example1


{
public static void Main()
{
double[] values = { 0.0, Double.Epsilon };
foreach (var value in values)
{
Console.WriteLine(GetComponentParts(value));
Console.WriteLine();
}
}

private static string GetComponentParts(double value)


{
string result = String.Format("{0:R}: ", value);
int indent = result.Length;

// Convert the double to an 8-byte array.


byte[] bytes = BitConverter.GetBytes(value);
// Get the sign bit (byte 7, bit 7).
result += String.Format("Sign: {0}\n",
(bytes[7] & 0x80) == 0x80 ? "1 (-)" : "0
(+)");

// Get the exponent (byte 6 bits 4-7 to byte 7, bits 0-6)


int exponent = (bytes[7] & 0x07F) << 4;
exponent = exponent | ((bytes[6] & 0xF0) >> 4);
int adjustment = exponent != 0 ? 1023 : 1022;
result += String.Format("{0}Exponent: 0x{1:X4} ({1})\n", new
String(' ', indent), exponent - adjustment);

// Get the significand (bits 0-51)


long significand = ((bytes[6] & 0x0F) << 48);
significand = significand | ((long)bytes[5] << 40);
significand = significand | ((long)bytes[4] << 32);
significand = significand | ((long)bytes[3] << 24);
significand = significand | ((long)bytes[2] << 16);
significand = significand | ((long)bytes[1] << 8);
significand = significand | bytes[0];
result += String.Format("{0}Mantissa: 0x{1:X13}\n", new String(' ',
indent), significand);

return result;
}
}
// // The example displays the following output:
// 0: Sign: 0 (+)
// Exponent: 0xFFFFFC02 (-1022)
// Mantissa: 0x0000000000000
//
//
// 4.94065645841247E-324: Sign: 0 (+)
// Exponent: 0xFFFFFC02 (-1022)
// Mantissa: 0x0000000000001

No entanto, a propriedade não é uma medida geral de precisão do Double tipo, aplica-
se apenas a EpsilonDouble instâncias que têm um valor de zero ou um expoente de
-1022.

7 Observação

O valor da propriedade não é equivalente ao épsilon da Epsilon máquina, que


representa o limite superior do erro relativo devido ao arredondamento na
aritmética de ponto flutuante.

O valor dessa constante é 4,94065645841247e-324.

Dois números de ponto flutuante aparentemente equivalentes podem não se comparar


iguais por causa de diferenças em seus dígitos menos significativos. Por exemplo, a
expressão C#, , não se compara igual porque a operação de divisão no lado esquerdo
tem precisão máxima, (double)1/3 == (double)0.33333 enquanto a constante no lado
direito é precisa apenas para os dígitos especificados. Se você criar um algoritmo
personalizado que determine se dois números de ponto flutuante podem ser
considerados iguais, não recomendamos que você baseie seu algoritmo no valor da
Epsilon constante para estabelecer a margem absoluta aceitável de diferença para que
os dois valores sejam considerados iguais. (Normalmente, essa margem de diferença é
muitas vezes maior do que Epsilon.) Para obter informações sobre como comparar dois
valores de ponto flutuante de precisão dupla, consulte Double e Equals(Double).

Notas da plataforma
Em sistemas ARM, o Epsilon valor da constante é muito pequeno para ser detectado,
por isso equivale a zero. Você pode definir um valor epsilon alternativo que é igual a
2,2250738585072014E-308.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações
de pull. Para obter mais  Abrir um problema de
informações, confira o nosso
documentação
guia para colaboradores.
 Fornecer comentários sobre o
produto
System.Int32 struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Int32 é um tipo de valor imutável que representa inteiros assinados com valores que
variam de 2.147.483.648 negativos (que é representado pela constante) até
2.147.483.647 positivos (que é representado pela Int32.MinValueInt32.MaxValue
constante). O .NET também inclui um tipo de valor inteiro de 32 bits não assinado,
UInt32, que representa valores que variam de 0 a 4.294.967.295.

Instanciar um valor Int32


Você pode instanciar um Int32 valor de várias maneiras:

Você pode declarar uma Int32 variável e atribuir-lhe um valor inteiro literal que
esteja dentro do intervalo do tipo de Int32 dados. O exemplo a seguir declara duas
Int32 variáveis e atribui valores a elas dessa maneira.

C#

int number1 = 64301;


int number2 = 25548612;

Você pode atribuir o valor de um tipo inteiro cujo intervalo é um subconjunto do


Int32 tipo. Esta é uma conversão de alargamento que não requer um operador de
conversão em C# ou um método de conversão no Visual Basic, mas requer um em
F#.

C#

sbyte value1 = 124;


short value2 = 1618;

int number1 = value1;


int number2 = value2;

Você pode atribuir o valor de um tipo numérico cujo intervalo exceda o Int32 do
tipo. Essa é uma conversão de estreitamento, portanto, requer um operador de
conversão em C# ou F# e um método de conversão no Visual Basic se Option
Strict estiver ativado. Se o valor numérico for um , ou Decimal valor que inclua
um Singlecomponente fracionário, Doubleo processamento de sua parte
fracionária dependerá do compilador que executa a conversão. O exemplo a seguir
executa conversões de estreitamento para atribuir vários valores numéricos a Int32
variáveis.

C#

long lNumber = 163245617;


try {
int number1 = (int) lNumber;
Console.WriteLine(number1);
}
catch (OverflowException) {
Console.WriteLine("{0} is out of range of an Int32.", lNumber);
}

double dbl2 = 35901.997;


try {
int number2 = (int) dbl2;
Console.WriteLine(number2);
}
catch (OverflowException) {
Console.WriteLine("{0} is out of range of an Int32.", dbl2);
}

BigInteger bigNumber = 132451;


try {
int number3 = (int) bigNumber;
Console.WriteLine(number3);
}
catch (OverflowException) {
Console.WriteLine("{0} is out of range of an Int32.", bigNumber);
}
// The example displays the following output:
// 163245617
// 35902
// 132451

Você pode chamar um método da Convert classe para converter qualquer tipo
com suporte em um Int32 valor. Isso é possível porque Int32 suporta a
IConvertible interface. O exemplo a seguir ilustra a conversão de uma matriz de
Decimal valores em Int32 valores.

C#

decimal[] values= { Decimal.MinValue, -1034.23m, -12m, 0m, 147m,


199.55m, 9214.16m, Decimal.MaxValue };
int result;

foreach (decimal value in values)


{
try {
result = Convert.ToInt32(value);
Console.WriteLine("Converted the {0} value '{1}' to the {2} value
{3}.",
value.GetType().Name, value,
result.GetType().Name, result);
}
catch (OverflowException) {
Console.WriteLine("{0} is outside the range of the Int32 type.",
value);
}
}
// The example displays the following output:
// -79228162514264337593543950335 is outside the range of the Int32
type.
// Converted the Decimal value '-1034.23' to the Int32 value -1034.
// Converted the Decimal value '-12' to the Int32 value -12.
// Converted the Decimal value '0' to the Int32 value 0.
// Converted the Decimal value '147' to the Int32 value 147.
// Converted the Decimal value '199.55' to the Int32 value 200.
// Converted the Decimal value '9214.16' to the Int32 value 9214.
// 79228162514264337593543950335 is outside the range of the Int32
type.

Você pode chamar o Parse método or TryParse para converter a representação de


cadeia de caracteres de um valor em um Int32Int32arquivo . A cadeia de caracteres
pode conter dígitos decimais ou hexadecimais. O exemplo a seguir ilustra a
operação de análise usando uma cadeia decimal e uma cadeia hexadecimal.

C#

string string1 = "244681";


try {
int number1 = Int32.Parse(string1);
Console.WriteLine(number1);
}
catch (OverflowException) {
Console.WriteLine("'{0}' is out of range of a 32-bit integer.",
string1);
}
catch (FormatException) {
Console.WriteLine("The format of '{0}' is invalid.", string1);
}

string string2 = "F9A3C";


try {
int number2 = Int32.Parse(string2,

System.Globalization.NumberStyles.HexNumber);
Console.WriteLine(number2);
}
catch (OverflowException) {
Console.WriteLine("'{0}' is out of range of a 32-bit integer.",
string2);
}
catch (FormatException) {
Console.WriteLine("The format of '{0}' is invalid.", string2);
}
// The example displays the following output:
// 244681
// 1022524

Executar operações em valores Int32


O Int32 tipo suporta operações matemáticas padrão, como adição, subtração, divisão,
multiplicação, negação e negação unária. Como os outros tipos integrais, o Int32 tipo
também suporta os operadores bitwise AND , , , OR XOR left shift e right shift.

Você pode usar os operadores numéricos padrão para comparar dois Int32 valores ou
pode chamar o CompareTo método ou Equals .

Também é possível chamar os membros da classe Math para realizar uma ampla
variedade de operações numéricas, inclusive obter o valor absoluto de um número,
calcular o quociente e o restante da divisão integral, determinando o valor máximo ou
mínimo de dois inteiros, obter o sinal de um número e arredondar um número.

Representar um Int32 como uma cadeia de


caracteres
O Int32 tipo fornece suporte completo para cadeias de caracteres de formato numérico
padrão e personalizado. (Para obter mais informações, consulte Tipos de formatação,
cadeias de caracteres de formato numérico padrão e cadeias de caracteres de formato
numérico personalizado.)

Para formatar um Int32 valor como uma cadeia de caracteres integral sem zeros à
esquerda, você pode chamar o método sem ToString() parâmetros. Usando o
especificador de formato "D", você também pode incluir um número especificado de
zeros à esquerda na representação de cadeia de caracteres. Usando o especificador de
formato "N", você pode incluir separadores de grupo e especificar o número de dígitos
decimais a serem exibidos na representação de cadeia de caracteres do número.
Usando o especificador de formato "X", você pode representar um Int32 valor como
uma cadeia de caracteres hexadecimal. O exemplo a seguir formata os elementos em
uma matriz de Int32 valores dessas quatro maneiras.
C#

int[] numbers = { -1403, 0, 169, 1483104 };


foreach (int number in numbers)
{
// Display value using default formatting.
Console.Write("{0,-8} --> ", number.ToString());
// Display value with 3 digits and leading zeros.
Console.Write("{0,11:D3}", number);
// Display value with 1 decimal digit.
Console.Write("{0,13:N1}", number);
// Display value as hexadecimal.
Console.Write("{0,12:X2}", number);
// Display value with eight hexadecimal digits.
Console.WriteLine("{0,14:X8}", number);
}
// The example displays the following output:
// -1403 --> -1403 -1,403.0 FFFFFA85 FFFFFA85
// 0 --> 000 0.0 00 00000000
// 169 --> 169 169.0 A9 000000A9
// 1483104 --> 1483104 1,483,104.0 16A160 0016A160

Você também pode formatar um Int32 valor como uma cadeia de caracteres binária,
octal, decimal ou hexadecimal chamando o método e fornecendo a base como o
ToString(Int32, Int32) segundo parâmetro do método. O exemplo a seguir chama esse
método para exibir as representações binárias, octais e hexadecimais de uma matriz de
valores inteiros.

C#

int[] numbers = { -146, 11043, 2781913 };


Console.WriteLine("{0,8} {1,32} {2,11} {3,10}",
"Value", "Binary", "Octal", "Hex");
foreach (int number in numbers)
{
Console.WriteLine("{0,8} {1,32} {2,11} {3,10}",
number, Convert.ToString(number, 2),
Convert.ToString(number, 8),
Convert.ToString(number, 16));
}
// The example displays the following output:
// Value Binary Octal Hex
// -146 11111111111111111111111101101110 37777777556 ffffff6e
// 11043 10101100100011 25443 2b23
// 2781913 1010100111001011011001 12471331 2a72d9

Trabalhar com valores inteiros de 32 bits não


decimais
Além de trabalhar com inteiros individuais como valores decimais, convém executar
operações bit a bit com valores inteiros ou trabalhar com as representações binárias ou
hexadecimais de valores inteiros. Int32 Os valores são representados em 31 bits, com o
bit de trinta segundos usado como um bit de sinal. Os valores positivos são
representados usando representação de sinal e magnitude. Os valores negativos estão
na representação do complemento de dois. Isso é importante ter em mente quando
você executa operações bit a bit em Int32 valores ou quando trabalha com bits
individuais. Para executar uma operação numérica, booleana ou de comparação em
quaisquer dois valores não decimais, ambos os valores devem usar a mesma
representação.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Int64 struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Int64 é um tipo de valor imutável que representa inteiros assinados com valores que
variam de 9.223.372.036.854.775.808 negativos (que é representado pela constante) até
9.223.372.036.854.775.807 positivos (que é representado pela
Int64.MinValueInt64.MaxValue constante). O .NET também inclui um tipo de valor inteiro
de 64 bits não assinado, , UInt64que representa valores que variam de 0 a
18.446.744.073.709.551.615.

Instanciar um valor Int64


Você pode instanciar um Int64 valor de várias maneiras:

Você pode declarar uma Int64 variável e atribuir-lhe um valor inteiro literal que
esteja dentro do intervalo do tipo de Int64 dados. O exemplo a seguir declara duas
Int64 variáveis e atribui valores a elas dessa maneira.

C#

long number1 = -64301728;


long number2 = 255486129307;

Você pode atribuir o valor de um tipo integral cujo intervalo é um subconjunto do


Int64 tipo. Esta é uma conversão de alargamento que não requer um operador de
conversão em C# ou um método de conversão no Visual Basic. Em F#, somente o
tipo pode ser ampliado Int32 automaticamente.

C#

sbyte value1 = 124;


short value2 = 1618;
int value3 = Int32.MaxValue;

long number1 = value1;


long number2 = value2;
long number3 = value3;

Você pode atribuir o valor de um tipo numérico cujo intervalo exceda o Int64 do
tipo. Essa é uma conversão de estreitamento, portanto, requer um operador de
conversão em C# ou F# e um método de conversão no Visual Basic se Option
Strict estiver ativado. Se o valor numérico for um , ou Decimal valor que inclua

um Singlecomponente fracionário, Doubleo processamento de sua parte


fracionária dependerá do compilador que executa a conversão. O exemplo a seguir
executa conversões de estreitamento para atribuir vários valores numéricos a Int64
variáveis.

C#

ulong ulNumber = 163245617943825;


try {
long number1 = (long) ulNumber;
Console.WriteLine(number1);
}
catch (OverflowException) {
Console.WriteLine("{0} is out of range of an Int64.", ulNumber);
}

double dbl2 = 35901.997;


try {
long number2 = (long) dbl2;
Console.WriteLine(number2);
}
catch (OverflowException) {
Console.WriteLine("{0} is out of range of an Int64.", dbl2);
}

BigInteger bigNumber = (BigInteger) 1.63201978555e30;


try {
long number3 = (long) bigNumber;
Console.WriteLine(number3);
}
catch (OverflowException) {
Console.WriteLine("{0} is out of range of an Int64.", bigNumber);
}
// The example displays the following output:
// 163245617943825
// 35902
// 1,632,019,785,549,999,969,612,091,883,520 is out of range of an
Int64.

Você pode chamar um método da Convert classe para converter qualquer tipo
com suporte em um Int64 valor. Isso é possível porque Int64 suporta a
IConvertible interface. O exemplo a seguir ilustra a conversão de uma matriz de
Decimal valores em Int64 valores.

C#
decimal[] values= { Decimal.MinValue, -1034.23m, -12m, 0m, 147m,
199.55m, 9214.16m, Decimal.MaxValue };
long result;

foreach (decimal value in values)


{
try {
result = Convert.ToInt64(value);
Console.WriteLine("Converted the {0} value '{1}' to the {2} value
{3}.",
value.GetType().Name, value,
result.GetType().Name, result);
}
catch (OverflowException) {
Console.WriteLine("{0} is outside the range of the Int64 type.",
value);
}
}
// The example displays the following output:
// -79228162514264337593543950335 is outside the range of the Int64
type.
// Converted the Decimal value '-1034.23' to the Int64 value -1034.
// Converted the Decimal value '-12' to the Int64 value -12.
// Converted the Decimal value '0' to the Int64 value 0.
// Converted the Decimal value '147' to the Int64 value 147.
// Converted the Decimal value '199.55' to the Int64 value 200.
// Converted the Decimal value '9214.16' to the Int64 value 9214.
// 79228162514264337593543950335 is outside the range of the Int64
type.

Você pode chamar o Parse método or TryParse para converter a representação de


cadeia de caracteres de um valor em um Int64Int64arquivo . A cadeia de caracteres
pode conter dígitos decimais ou hexadecimais. O exemplo a seguir ilustra a
operação de análise usando uma cadeia decimal e uma cadeia hexadecimal.

C#

string string1 = "244681903147";


try {
long number1 = Int64.Parse(string1);
Console.WriteLine(number1);
}
catch (OverflowException) {
Console.WriteLine("'{0}' is out of range of a 64-bit integer.",
string1);
}
catch (FormatException) {
Console.WriteLine("The format of '{0}' is invalid.", string1);
}

string string2 = "F9A3CFF0A";


try {
long number2 = Int64.Parse(string2,

System.Globalization.NumberStyles.HexNumber);
Console.WriteLine(number2);
}
catch (OverflowException) {
Console.WriteLine("'{0}' is out of range of a 64-bit integer.",
string2);
}
catch (FormatException) {
Console.WriteLine("The format of '{0}' is invalid.", string2);
}
// The example displays the following output:
// 244681903147
// 67012198154

Executar operações em valores Int64


O Int64 tipo suporta operações matemáticas padrão, como adição, subtração, divisão,
multiplicação, negação e negação unária. Como os outros tipos integrais, o Int64 tipo
também suporta os operadores bitwise AND , , , OR XOR left shift e right shift.

Você pode usar os operadores numéricos padrão para comparar dois Int64 valores ou
pode chamar o CompareTo método ou Equals .

Você também pode chamar os Math membros da classe para executar uma ampla gama
de operações numéricas, incluindo obter o valor absoluto de um número, calcular o
quociente e o restante da divisão integral, determinar o valor máximo ou mínimo de
dois inteiros longos, obter o sinal de um número e arredondar um número.

Representar um Int64 como uma cadeia de


caracteres
O Int64 tipo fornece suporte completo para cadeias de caracteres de formato numérico
padrão e personalizado. (Para obter mais informações, consulte Tipos de formatação,
cadeias de caracteres de formato numérico padrão e cadeias de caracteres de formato
numérico personalizado.)

Para formatar um Int64 valor como uma cadeia de caracteres integral sem zeros à
esquerda, você pode chamar o método sem ToString() parâmetros. Usando o
especificador de formato "D", você também pode incluir um número especificado de
zeros à esquerda na representação de cadeia de caracteres. Usando o especificador de
formato "N", você pode incluir separadores de grupo e especificar o número de dígitos
decimais a serem exibidos na representação de cadeia de caracteres do número.
Usando o especificador de formato "X", você pode representar um Int64 valor como
uma cadeia de caracteres hexadecimal. O exemplo a seguir formata os elementos em
uma matriz de Int64 valores dessas quatro maneiras.

C#

long[] numbers = { -1403, 0, 169, 1483104 };


foreach (var number in numbers)
{
// Display value using default formatting.
Console.Write("{0,-8} --> ", number.ToString());
// Display value with 3 digits and leading zeros.
Console.Write("{0,8:D3}", number);
// Display value with 1 decimal digit.
Console.Write("{0,13:N1}", number);
// Display value as hexadecimal.
Console.Write("{0,18:X2}", number);
// Display value with eight hexadecimal digits.
Console.WriteLine("{0,18:X8}", number);
}
// The example displays the following output:
// -1403 --> -1403 -1,403.0 FFFFFFFFFFFFFA85
FFFFFFFFFFFFFA85
// 0 --> 000 0.0 00
00000000
// 169 --> 169 169.0 A9
000000A9
// 1483104 --> 1483104 1,483,104.0 16A160
0016A160

Você também pode formatar um Int64 valor como uma cadeia de caracteres binária,
octal, decimal ou hexadecimal chamando o método e fornecendo a base como o
ToString(Int64, Int32) segundo parâmetro do método. O exemplo a seguir chama esse
método para exibir as representações binárias, octais e hexadecimais de uma matriz de
valores inteiros.

C#

long[] numbers = { -146, 11043, 2781913 };


foreach (var number in numbers)
{
Console.WriteLine("{0} (Base 10):", number);
Console.WriteLine(" Binary: {0}", Convert.ToString(number, 2));
Console.WriteLine(" Octal: {0}", Convert.ToString(number, 8));
Console.WriteLine(" Hex: {0}\n", Convert.ToString(number, 16));
}
// The example displays the following output:
// -146 (Base 10):
// Binary:
1111111111111111111111111111111111111111111111111111111101101110
// Octal: 1777777777777777777556
// Hex: ffffffffffffff6e
//
// 11043 (Base 10):
// Binary: 10101100100011
// Octal: 25443
// Hex: 2b23
//
// 2781913 (Base 10):
// Binary: 1010100111001011011001
// Octal: 12471331
// Hex: 2a72d9

Trabalhar com valores inteiros de 32 bits não


decimais
Além de trabalhar com inteiros longos individuais como valores decimais, convém
executar operações bit a bit com valores inteiros longos ou trabalhar com as
representações binárias ou hexadecimais de valores inteiros longos. Int64 Os valores são
representados em 63 bits, com o sexagésimo quarto bit usado como um bit de sinal. Os
valores positivos são representados usando representação de sinal e magnitude. Os
valores negativos estão na representação do complemento de dois. Isso é importante
ter em mente quando você executa operações bit a bit em Int64 valores ou quando
trabalha com bits individuais. Para executar uma operação numérica, booleana ou de
comparação em quaisquer dois valores não decimais, ambos os valores devem usar a
mesma representação.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Numerics.BigInteger struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O BigInteger tipo é um tipo imutável que representa um inteiro arbitrariamente grande


cujo valor em teoria não tem limites superiores ou inferiores. Os membros do BigInteger
tipo são muito paralelos aos de outros tipos integrais (os Bytetipos , , , , ,
UInt16Int64SByteUInt32, Int16Int32e ).UInt64 Esse tipo difere dos outros tipos integrais
no .NET, que têm um intervalo indicado por suas MinValue propriedades e MaxValue .

7 Observação

Como o BigInteger tipo é imutável (consulte Mutabilidade) e porque não tem


limites superiores ou inferiores, um pode ser lançado para qualquer operação que
faça com que um BigIntegerOutOfMemoryException valor cresça demais.

Instanciar um objeto BigInteger


Você pode instanciar um BigInteger objeto de várias maneiras:

Você pode usar a new palavra-chave e fornecer qualquer valor integral ou de


ponto flutuante como um parâmetro para o BigInteger construtor. (Os valores de
ponto flutuante são truncados antes de serem atribuídos ao BigInteger.) O
exemplo a seguir ilustra como usar a new palavra-chave para instanciar BigInteger
valores.

C#

BigInteger bigIntFromDouble = new BigInteger(179032.6541);


Console.WriteLine(bigIntFromDouble);
BigInteger bigIntFromInt64 = new BigInteger(934157136952);
Console.WriteLine(bigIntFromInt64);
// The example displays the following output:
// 179032
// 934157136952

Você pode declarar uma BigInteger variável e atribuir-lhe um valor como faria com
qualquer tipo numérico, desde que esse valor seja um tipo integral. O exemplo a
seguir usa atribuição para criar um valor a partir de um BigIntegerInt64arquivo .
C#

long longValue = 6315489358112;


BigInteger assignedFromLong = longValue;
Console.WriteLine(assignedFromLong);
// The example displays the following output:
// 6315489358112

Você pode atribuir um valor decimal ou de ponto flutuante a um BigInteger objeto


se converter o valor primeiro. O exemplo a seguir converte explicitamente (em C#)
ou converte (no Visual Basic) um e um valor em um DoubleDecimalBigInteger.

C#

BigInteger assignedFromDouble = (BigInteger) 179032.6541;


Console.WriteLine(assignedFromDouble);
BigInteger assignedFromDecimal = (BigInteger) 64312.65m;
Console.WriteLine(assignedFromDecimal);
// The example displays the following output:
// 179032
// 64312

Esses métodos permitem que você instancie um objeto cujo valor está no intervalo de
um BigInteger dos tipos numéricos existentes somente. Você pode instanciar um
BigInteger objeto cujo valor pode exceder o intervalo dos tipos numéricos existentes de
uma das três maneiras:

Você pode usar a new palavra-chave e fornecer uma matriz de bytes de qualquer
tamanho para o BigInteger.BigInteger construtor. Por exemplo:

C#

byte[] byteArray = { 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0};


BigInteger newBigInt = new BigInteger(byteArray);
Console.WriteLine("The value of newBigInt is {0} (or 0x{0:x}).",
newBigInt);
// The example displays the following output:
// The value of newBigInt is 4759477275222530853130 (or
0x102030405060708090a).

Você pode chamar os Parse métodos ou TryParse para converter a representação


de cadeia de caracteres de um número em um BigIntegerarquivo . Por exemplo:

C#

string positiveString = "91389681247993671255432112000000";


string negativeString = "-90315837410896312071002088037140000";
BigInteger posBigInt = 0;
BigInteger negBigInt = 0;

try {
posBigInt = BigInteger.Parse(positiveString);
Console.WriteLine(posBigInt);
}
catch (FormatException)
{
Console.WriteLine("Unable to convert the string '{0}' to a
BigInteger value.",
positiveString);
}

if (BigInteger.TryParse(negativeString, out negBigInt))


Console.WriteLine(negBigInt);
else
Console.WriteLine("Unable to convert the string '{0}' to a
BigInteger value.",
negativeString);

// The example displays the following output:


// 9.1389681247993671255432112E+31
// -9.0315837410896312071002088037E+34

Você pode chamar um método ( Shared no Visual Basic) BigInteger que executa
alguma operação em uma expressão numérica e retorna um static resultado
calculadoBigInteger. O exemplo a seguir faz isso cubando UInt64.MaxValue e
atribuindo o resultado a um BigIntegerarquivo .

C#

BigInteger number = BigInteger.Pow(UInt64.MaxValue, 3);


Console.WriteLine(number);
// The example displays the following output:
// 6277101735386680762814942322444851025767571854389858533375

O valor não inicializado de a BigInteger é Zero.

Executar operações em valores BigInteger


Você pode usar uma BigInteger instância como usaria qualquer outro tipo integral.
BigInteger sobrecarrega os operadores numéricos padrão para permitir que você
execute operações matemáticas básicas, como adição, subtração, divisão, multiplicação
e negação unária. Você também pode usar os operadores numéricos padrão para
comparar dois BigInteger valores entre si. Como os outros tipos integrais, também
suporta os operadores bitwise And , , , XOr BigInteger Or left shift e right shift. Para
linguagens que não oferecem suporte a operadores personalizados, a BigInteger
estrutura também fornece métodos equivalentes para executar operações matemáticas.
Estes incluem Add, , Divide, , , MultiplyNegateSubtracte vários outros.

Muitos membros da BigInteger estrutura correspondem diretamente aos membros dos


outros tipos integrais. Além disso, BigInteger adiciona membros como o seguinte:

Sign, que retorna um valor que indica o sinal de um BigInteger valor.

Abs, que retorna o valor absoluto de um BigInteger valor.

DivRem, que retorna o quociente e o restante de uma operação de divisão.

GreatestCommonDivisor, que retorna o maior divisor comum de dois BigInteger


valores.

Muitos desses membros adicionais correspondem aos membros da classe, que Math
fornece a funcionalidade para trabalhar com os tipos numéricos primitivos.

Mutabilidade
O exemplo a seguir instancia um objeto e, em seguida, incrementa seu valor por um
BigInteger .

C#

BigInteger number = BigInteger.Multiply(Int64.MaxValue, 3);


number++;
Console.WriteLine(number);

Embora este exemplo pareça modificar o valor do objeto existente, esse não é o caso.
BigInteger Os objetos são imutáveis, o que significa que, internamente, o Common
Language Runtime realmente cria um novo BigInteger objeto e atribui a ele um valor
maior do que seu valor anterior. Esse novo objeto é retornado ao chamador.

7 Observação

Os outros tipos numéricos no .NET também são imutáveis. No entanto, como o


BigInteger tipo não tem limites superiores ou inferiores, seus valores podem
crescer extremamente e ter um impacto mensurável no desempenho.

Embora esse processo seja transparente para o chamador, ele incorre em uma
penalidade de desempenho. Em alguns casos, especialmente quando operações
repetidas são executadas em um loop em valores muito grandes BigInteger , essa
penalidade de desempenho pode ser significativa. Por exemplo, no exemplo a seguir,
uma operação é executada repetidamente até um milhão de vezes, e um valor é
incrementado em um BigInteger cada vez que a operação é bem-sucedida.

C#

BigInteger number = Int64.MaxValue ^ 5;


int repetitions = 1000000;
// Perform some repetitive operation 1 million times.
for (int ctr = 0; ctr <= repetitions; ctr++)
{
// Perform some operation. If it fails, exit the loop.
if (!SomeOperationSucceeds()) break;
// The following code executes if the operation succeeds.
number++;
}

Nesse caso, você pode melhorar o desempenho executando todas as atribuições


intermediárias para uma Int32 variável. O valor final da variável pode então ser atribuído
ao BigInteger objeto quando o loop for encerrado. O exemplo a seguir ilustra esse
cenário.

C#

BigInteger number = Int64.MaxValue ^ 5;


int repetitions = 1000000;
int actualRepetitions = 0;
// Perform some repetitive operation 1 million times.
for (int ctr = 0; ctr <= repetitions; ctr++)
{
// Perform some operation. If it fails, exit the loop.
if (!SomeOperationSucceeds()) break;
// The following code executes if the operation succeeds.
actualRepetitions++;
}
number += actualRepetitions;

Matrizes de bytes e cadeias de caracteres


hexadecimais
Se você converter BigInteger valores em matrizes de bytes, ou se converter matrizes de
bytes em BigInteger valores, deverá considerar a ordem dos bytes. A BigInteger
estrutura espera que os bytes individuais em uma matriz de bytes apareçam em ordem
little-endian (ou seja, os bytes de ordem inferior do valor precedem os bytes de ordem
superior). Você pode fazer uma viagem de ida e volta de um BigInteger valor chamando
o método e, em seguida, passando a matriz de bytes resultante para o construtor, como
mostra o ToByteArrayBigInteger(Byte[]) exemplo a seguir.

C#

BigInteger number = BigInteger.Pow(Int64.MaxValue, 2);


Console.WriteLine(number);

// Write the BigInteger value to a byte array.


byte[] bytes = number.ToByteArray();

// Display the byte array.


foreach (byte byteValue in bytes)
Console.Write("0x{0:X2} ", byteValue);
Console.WriteLine();

// Restore the BigInteger value from a Byte array.


BigInteger newNumber = new BigInteger(bytes);
Console.WriteLine(newNumber);
// The example displays the following output:
// 8.5070591730234615847396907784E+37
// 0x01 0x00 0x00 0x00 0x00 0x00 0x00 0x00 0xFF 0xFF 0xFF 0xFF 0xFF 0xFF
0xFF 0x3F
//
// 8.5070591730234615847396907784E+37

Para instanciar um valor de uma matriz de bytes que representa um BigInteger valor de
algum outro tipo integral, você pode passar o valor integral para o método e, em
seguida, passar a matriz de bytes resultante para o
BitConverter.GetBytesBigInteger(Byte[]) construtor. O exemplo a seguir instancia um
valor de uma matriz de bytes que representa um BigIntegerInt16 valor.

C#

short originalValue = 30000;


Console.WriteLine(originalValue);

// Convert the Int16 value to a byte array.


byte[] bytes = BitConverter.GetBytes(originalValue);

// Display the byte array.


foreach (byte byteValue in bytes)
Console.Write("0x{0} ", byteValue.ToString("X2"));
Console.WriteLine();

// Pass byte array to the BigInteger constructor.


BigInteger number = new BigInteger(bytes);
Console.WriteLine(number);
// The example displays the following output:
// 30000
// 0x30 0x75
// 30000

A BigInteger estrutura assume que os valores negativos são armazenados usando a


representação de complemento de dois. Como a BigInteger estrutura representa um
valor numérico sem comprimento fixo, o construtor sempre interpreta o
BigInteger(Byte[]) bit mais significativo do último byte na matriz como um bit de sinal.
Para evitar que o BigInteger(Byte[]) construtor confunda a representação de
complemento dos dois de um valor negativo com a representação de sinal e magnitude
de um valor positivo, os valores positivos nos quais o bit mais significativo do último
byte na matriz de bytes normalmente seria definido devem incluir um byte adicional
cujo valor é 0. Por exemplo, 0xC0 0xBD 0xF0 0xFF é a representação hexadecimal little-
endian de -1.000.000 ou 4.293.967.296. Como o bit mais significativo do último byte
nessa matriz está ativado, o valor da matriz de bytes seria interpretado pelo
BigInteger(Byte[]) construtor como -1.000.000. Para instanciar um BigInteger cujo valor é
positivo, uma matriz de bytes cujos elementos são 0xC0 0xBD 0xF0 0xFF 0x00 deve ser
passada para o construtor. O exemplo a seguir ilustra essa situação.

C#

int negativeNumber = -1000000;


uint positiveNumber = 4293967296;

byte[] negativeBytes = BitConverter.GetBytes(negativeNumber);


BigInteger negativeBigInt = new BigInteger(negativeBytes);
Console.WriteLine(negativeBigInt.ToString("N0"));

byte[] tempPosBytes = BitConverter.GetBytes(positiveNumber);


byte[] positiveBytes = new byte[tempPosBytes.Length + 1];
Array.Copy(tempPosBytes, positiveBytes, tempPosBytes.Length);
BigInteger positiveBigInt = new BigInteger(positiveBytes);
Console.WriteLine(positiveBigInt.ToString("N0"));
// The example displays the following output:
// -1,000,000
// 4,293,967,296

As matrizes de bytes criadas pelo método a ToByteArray partir de valores positivos


incluem esse byte extra de valor zero. Portanto, a estrutura pode valores de ida e volta
com êxito atribuindo-os e, em seguida, restaurando-os a BigInteger partir de matrizes
de bytes, como mostra o exemplo a seguir.

C#

BigInteger positiveValue = 15777216;


BigInteger negativeValue = -1000000;
Console.WriteLine("Positive value: " + positiveValue.ToString("N0"));
byte[] bytes = positiveValue.ToByteArray();

foreach (byte byteValue in bytes)


Console.Write("{0:X2} ", byteValue);
Console.WriteLine();
positiveValue = new BigInteger(bytes);
Console.WriteLine("Restored positive value: " +
positiveValue.ToString("N0"));

Console.WriteLine();

Console.WriteLine("Negative value: " + negativeValue.ToString("N0"));


bytes = negativeValue.ToByteArray();
foreach (byte byteValue in bytes)
Console.Write("{0:X2} ", byteValue);
Console.WriteLine();
negativeValue = new BigInteger(bytes);
Console.WriteLine("Restored negative value: " +
negativeValue.ToString("N0"));
// The example displays the following output:
// Positive value: 15,777,216
// C0 BD F0 00
// Restored positive value: 15,777,216
//
// Negative value: -1,000,000
// C0 BD F0
// Restored negative value: -1,000,000

No entanto, talvez seja necessário adicionar esse byte adicional de valor zero a matrizes
de bytes criadas dinamicamente pelo desenvolvedor ou retornadas por métodos que
convertem inteiros não assinados em matrizes de bytes (como
BitConverter.GetBytes(UInt16), BitConverter.GetBytes(UInt32)e
BitConverter.GetBytes(UInt64)).

Ao analisar uma cadeia de caracteres hexadecimal, os métodos e BigInteger.Parse(String,


NumberStyles, IFormatProvider) pressupõem que, se o bit mais significativo do primeiro
byte na cadeia de caracteres for definido, ou se o primeiro dígito hexadecimal da cadeia
de caracteres representar os BigInteger.Parse(String, NumberStyles) quatro bits
inferiores de um valor de byte, o valor será representado usando a representação de
complemento de dois. Por exemplo, "FF01" e "F01" representam o valor decimal -255.
Para diferenciar valores positivos de negativos, os valores positivos devem incluir um
zero à esquerda. As sobrecargas relevantes do ToString método, quando são passadas a
cadeia de caracteres de formato "X", adicionam um zero à esquerda à cadeia
hexadecimal retornada para valores positivos. Isso torna possível valores de ida
BigInteger e volta usando os ToString métodos e Parse , como mostra o exemplo a
seguir.
C#

BigInteger negativeNumber = -1000000;


BigInteger positiveNumber = 15777216;

string negativeHex = negativeNumber.ToString("X");


string positiveHex = positiveNumber.ToString("X");

BigInteger negativeNumber2, positiveNumber2;


negativeNumber2 = BigInteger.Parse(negativeHex,
NumberStyles.HexNumber);
positiveNumber2 = BigInteger.Parse(positiveHex,
NumberStyles.HexNumber);

Console.WriteLine("Converted {0:N0} to {1} back to {2:N0}.",


negativeNumber, negativeHex, negativeNumber2);
Console.WriteLine("Converted {0:N0} to {1} back to {2:N0}.",
positiveNumber, positiveHex, positiveNumber2);
// The example displays the following output:
// Converted -1,000,000 to F0BDC0 back to -1,000,000.
// Converted 15,777,216 to 0F0BDC0 back to 15,777,216.

No entanto, as cadeias de caracteres hexadecimais criadas chamando os ToString


métodos dos outros tipos integrais ou as sobrecargas do método que incluem um
toBase parâmetro não indicam o sinal do valor ou o tipo de dados de origem do
ToString qual a cadeia hexadecimal foi derivada. Instanciar com êxito um BigInteger
valor de tal cadeia de caracteres requer alguma lógica adicional. O exemplo a seguir
fornece uma implementação possível.

C#

using System;
using System.Globalization;
using System.Numerics;

public struct HexValue


{
public int Sign;
public string Value;
}

public class ByteHexExample2


{
public static void Main()
{
uint positiveNumber = 4039543321;
int negativeNumber = -255423975;

// Convert the numbers to hex strings.


HexValue hexValue1, hexValue2;
hexValue1.Value = positiveNumber.ToString("X");
hexValue1.Sign = Math.Sign(positiveNumber);

hexValue2.Value = Convert.ToString(negativeNumber, 16);


hexValue2.Sign = Math.Sign(negativeNumber);

// Round-trip the hexadecimal values to BigInteger values.


string hexString;
BigInteger positiveBigInt, negativeBigInt;

hexString = (hexValue1.Sign == 1 ? "0" : "") + hexValue1.Value;


positiveBigInt = BigInteger.Parse(hexString,
NumberStyles.HexNumber);
Console.WriteLine("Converted {0} to {1} and back to {2}.",
positiveNumber, hexValue1.Value, positiveBigInt);

hexString = (hexValue2.Sign == 1 ? "0" : "") + hexValue2.Value;


negativeBigInt = BigInteger.Parse(hexString,
NumberStyles.HexNumber);
Console.WriteLine("Converted {0} to {1} and back to {2}.",
negativeNumber, hexValue2.Value, negativeBigInt);
}
}
// The example displays the following output:
// Converted 4039543321 to F0C68A19 and back to 4039543321.
// Converted -255423975 to f0c68a19 and back to -255423975.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Numerics.Complex struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Um número complexo é um número que compreende uma parte numérica real e uma
parte numérica imaginária. Um número complexo z é geralmente escrito na forma z = x
+ yi , onde x e y são números reais, e i é a unidade imaginária que tem a propriedade i 2

= -1. A parte real do número complexo é representada por x, e a parte imaginária do


número complexo é representada por y.

O Complex tipo usa o sistema de coordenadas cartesianas (real, imaginário) ao


instanciar e manipular números complexos. Um número complexo pode ser
representado como um ponto em um sistema de coordenadas bidimensionais, que é
conhecido como o plano complexo. A parte real do número complexo é posicionada no
eixo x (o eixo horizontal), e a parte imaginária é posicionada no eixo y (o eixo vertical).

Qualquer ponto no plano complexo também pode ser expresso com base em seu valor
absoluto, usando o sistema de coordenadas polares. Em coordenadas polares, um
ponto é caracterizado por dois números:

Sua magnitude, que é a distância do ponto da origem (isto é, 0,0, ou o ponto em


que o eixo x e o eixo y se cruzam).
Sua fase, que é o ângulo entre o eixo real e a linha traçada da origem ao ponto.

Instanciar um número complexo


Você pode atribuir um valor a um número complexo de uma das seguintes maneiras:

Passando dois Double valores para o seu construtor. O primeiro valor representa a
parte real do número complexo, e o segundo valor representa sua parte
imaginária. Esses valores representam a posição do número complexo no sistema
de coordenadas cartesianas bidimensionais.

Chamando o método estático ( Shared no Visual Basic)


Complex.FromPolarCoordinates para criar um número complexo a partir de suas
coordenadas polares.

Atribuindo um valor , , , , , , , , , Int32UInt16SingleUInt64Int64UInt32Int16ou Double


a um ByteComplex objeto. SByte O valor torna-se a parte real do número
complexo, e sua parte imaginária é igual a 0.
Convertendo (em C#) ou convertendo (no Visual Basic) um ou BigInteger valor em
um DecimalComplex objeto. O valor torna-se a parte real do número complexo, e
sua parte imaginária é igual a 0.

Atribuindo o número complexo que é retornado por um método ou operador a


um Complex objeto. Por exemplo, é um método estático que retorna um número
complexo que é a soma de dois números complexos, Complex.Add e o operador
adiciona dois números complexos e retorna o Complex.Addition resultado.

O exemplo a seguir demonstra cada uma dessas cinco maneiras de atribuir um valor a
um número complexo.

C#

using System;
using System.Numerics;

public class CreateEx


{
public static void Main()
{
// Create a complex number by calling its class constructor.
Complex c1 = new Complex(12, 6);
Console.WriteLine(c1);

// Assign a Double to a complex number.


Complex c2 = 3.14;
Console.WriteLine(c2);

// Cast a Decimal to a complex number.


Complex c3 = (Complex)12.3m;
Console.WriteLine(c3);

// Assign the return value of a method to a Complex variable.


Complex c4 = Complex.Pow(Complex.One, -1);
Console.WriteLine(c4);

// Assign the value returned by an operator to a Complex variable.


Complex c5 = Complex.One + Complex.One;
Console.WriteLine(c5);

// Instantiate a complex number from its polar coordinates.


Complex c6 = Complex.FromPolarCoordinates(10, .524);
Console.WriteLine(c6);
}
}
// The example displays the following output:
// (12, 6)
// (3.14, 0)
// (12.3, 0)
// (1, 0)
// (2, 0)
// (8.65824721882145, 5.00347430269914)

Operações com números complexos


A Complex estrutura no .NET inclui membros que fornecem a seguinte funcionalidade:

Métodos para comparar dois números complexos para determinar se eles são
iguais.
Operadores para realizar operações aritméticas em números complexos. Complex
Os operadores permitem que você execute adição, subtração, multiplicação,
divisão e negação unária com números complexos.
Métodos para realizar outras operações numéricas em números complexos. Além
das quatro operações aritméticas básicas, você pode elevar um número complexo
a uma potência especificada, localizar a raiz quadrada de um número complexo e
obter o valor absoluto de um número complexo.
Métodos para realizar operações trigonométricas em números complexos. Por
exemplo, você pode calcular a tangente de um ângulo representado por um
número complexo.

Observe que, como as Real propriedades e Imaginary são somente leitura, você não
pode modificar o valor de um objeto existente Complex . Todos os métodos que
executam uma operação em um número, se seu valor de retorno for do tipo Complex,
retornarão um Complex novo Complex número.

Precisão e números complexos


As partes real e imaginária de um número complexo são representadas por dois valores
de ponto flutuante de precisão dupla. Isso significa que Complex valores, como valores
de ponto flutuante de precisão dupla, podem perder precisão como resultado de
operações numéricas. Isso significa que comparações rigorosas para igualdade de dois
valores podem falhar, mesmo que a diferença entre os dois Complex valores seja devida
a uma perda de precisão. Para obter mais informações, consulte Double.

Por exemplo, executar exponenciação no logaritmo de um número deve retornar o


número original. No entanto, em alguns casos, a perda de precisão dos valores de
ponto flutuante pode causar pequenas diferenças entre os dois valores, como ilustra o
exemplo a seguir.

C#
Complex value = new Complex(Double.MinValue / 2, Double.MinValue / 2);
Complex value2 = Complex.Exp(Complex.Log(value));
Console.WriteLine("{0} \n{1} \nEqual: {2}", value, value2,
value == value2);
// The example displays the following output:
// (-8.98846567431158E+307, -8.98846567431158E+307)
// (-8.98846567431161E+307, -8.98846567431161E+307)
// Equal: False

Da mesma forma, o exemplo a seguir, que calcula a raiz quadrada de um Complex


número, produz resultados ligeiramente diferentes nas versões de 32 bits e IA64 do
.NET.

C#

Complex minusOne = new Complex(-1, 0);


Console.WriteLine(Complex.Sqrt(minusOne));
// The example displays the following output:
// (6.12303176911189E-17, 1) on 32-bit systems.
// (6.12323399573677E-17,1) on IA64 systems.

Infinito e NaN
As partes reais e imaginárias de um número complexo são representadas por Double
valores. Além de variar de a , a Double.MaxValueparte real ou imaginária de um número
complexo pode ter um valor de Double.PositiveInfinityDouble.MinValue ,
Double.NegativeInfinityou Double.NaN. Double.PositiveInfinity, Double.NegativeInfinitye
Double.NaN todos se propagam em qualquer operação aritmética ou trigonométrica.

No exemplo a seguir, a divisão por Zero produz um número complexo cujas partes real
e imaginária são ambas Double.NaN. Como resultado, realizar a multiplicação com esse
valor também produz um número complexo cujas partes reais e imaginárias são
Double.NaN. Da mesma forma, realizar uma multiplicação que transborda o intervalo do
Double tipo produz um número complexo cuja parte real é e cuja parte imaginária é
Double.NaNDouble.PositiveInfinity. Subsequentemente, realizar a divisão com esse
número complexo retorna um número complexo cuja parte real é e cuja parte
imaginária é Double.NaNDouble.PositiveInfinity.

C#

using System;
using System.Numerics;

public class NaNEx


{
public static void Main()
{
Complex c1 = new Complex(Double.MaxValue / 2, Double.MaxValue / 2);

Complex c2 = c1 / Complex.Zero;
Console.WriteLine(c2.ToString());
c2 = c2 * new Complex(1.5, 1.5);
Console.WriteLine(c2.ToString());
Console.WriteLine();

Complex c3 = c1 * new Complex(2.5, 3.5);


Console.WriteLine(c3.ToString());
c3 = c3 + new Complex(Double.MinValue / 2, Double.MaxValue / 2);
Console.WriteLine(c3);
}
}
// The example displays the following output:
// (NaN, NaN)
// (NaN, NaN)
// (NaN, Infinity)
// (NaN, Infinity)

Operações matemáticas com números complexos que são inválidos ou que excedem o
Double intervalo do tipo de dados não lançam uma exceção. Em vez disso, eles
retornam um Double.PositiveInfinity, Double.NegativeInfinityou Double.NaN sob as
seguintes condições:

A divisão de um número positivo por zero retorna Double.PositiveInfinity.


Qualquer operação que estoure Double o limite superior do tipo de dados retorna
Double.PositiveInfinity.
A divisão de um número negativo por zero retorna Double.NegativeInfinity.
Qualquer operação que estoure Double o limite inferior do tipo de dados retorna
Double.NegativeInfinity.
A divisão de um zero por zero retorna Double.NaN.
Qualquer operação executada em operandos cujos valores são
Double.PositiveInfinity, ou Double.NaN retorna Double.PositiveInfinity, ou ,
Double.NegativeInfinityDouble.NegativeInfinitydependendo Double.NaNda
operação específica.

Observe que isso se aplica a quaisquer cálculos intermediários realizados por um


método. Por exemplo, a multiplicação de new Complex(9e308, 9e308) and new
Complex(2.5, 3.5) usa a fórmula (ac - bd) + (ad + bc)i. O cálculo do componente real

resultante da multiplicação avalia a expressão 9e308 2,5 - 9e308 3,5. Cada multiplicação
intermediária nessa expressão retorna Double.PositiveInfinity, e a tentativa de subtrair
Double.PositiveInfinity retorna Double.NaNDouble.PositiveInfinity .
Formatar um número complexo
Por padrão, a representação de cadeia de caracteres de um número complexo assume a
forma ( imaginário real, onde real , e imaginário ) são as representações de cadeia de
caracteres dos Double valores que formam os componentes reais e imaginários do número
complexo. Algumas sobrecargas do método permitem a ToString personalização das
representações de cadeia de caracteres desses Double valores para refletir as
convenções de formatação de uma cultura específica ou para aparecer em um formato
específico definido por uma cadeia de caracteres de formato numérico padrão ou
personalizado. (Para obter mais informações, consulte Cadeias de caracteres de formato
numérico padrão e cadeias de caracteres de formato numérico personalizado.)

Uma das maneiras mais comuns de expressar a representação de cadeia de caracteres


de um número complexo assume a forma a + bi, onde a é o componente real do
número complexo, e b é o componente imaginário do número complexo. Em
engenharia elétrica, um número complexo é mais comumente expresso como a + bj.
Você pode retornar a representação de cadeia de caracteres de um número complexo
em qualquer uma dessas duas formas. Para fazer isso, defina um provedor de formato
personalizado implementando as ICustomFormatter interfaces e e IFormatProvider , em
seguida, chame o String.Format(IFormatProvider, String, Object[]) método.

O exemplo a seguir define uma classe que representa um número complexo como uma
ComplexFormatter cadeia de caracteres na forma de a + bi ou a + bj.

C#

using System;
using System.Numerics;

public class ComplexFormatter : IFormatProvider, ICustomFormatter


{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(string format, object arg,


IFormatProvider provider)
{
if (arg is Complex)
{
Complex c1 = (Complex)arg;
// Check if the format string has a precision specifier.
int precision;
string fmtString = String.Empty;
if (format.Length > 1)
{
try
{
precision = Int32.Parse(format.Substring(1));
}
catch (FormatException)
{
precision = 0;
}
fmtString = "N" + precision.ToString();
}
if (format.Substring(0, 1).Equals("I",
StringComparison.OrdinalIgnoreCase))
return c1.Real.ToString(fmtString) + " + " +
c1.Imaginary.ToString(fmtString) + "i";
else if (format.Substring(0, 1).Equals("J",
StringComparison.OrdinalIgnoreCase))
return c1.Real.ToString(fmtString) + " + " +
c1.Imaginary.ToString(fmtString) + "j";
else
return c1.ToString(format, provider);
}
else
{
if (arg is IFormattable)
return ((IFormattable)arg).ToString(format, provider);
else if (arg != null)
return arg.ToString();
else
return String.Empty;
}
}
}

O exemplo a seguir usa esse formatador personalizado para exibir a representação de


cadeia de caracteres de um número complexo.

C#

public class CustomFormatEx


{
public static void Main()
{
Complex c1 = new Complex(12.1, 15.4);
Console.WriteLine("Formatting with ToString(): " +
c1.ToString());
Console.WriteLine("Formatting with ToString(format): " +
c1.ToString("N2"));
Console.WriteLine("Custom formatting with I0: " +
String.Format(new ComplexFormatter(), "{0:I0}",
c1));
Console.WriteLine("Custom formatting with J3: " +
String.Format(new ComplexFormatter(), "{0:J3}",
c1));
}
}
// The example displays the following output:
// Formatting with ToString(): (12.1, 15.4)
// Formatting with ToString(format): (12.10, 15.40)
// Custom formatting with I0: 12 + 15i
// Custom formatting with J3: 12.100 + 15.400j

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Single struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Single tipo de valor representa um número de 32 bits de precisão única com valores
que variam de 3,402823e38 negativo a 3,402823e38 positivo, bem como zero positivo
ou negativo, PositiveInfinity, NegativeInfinitye não um número (NaN). Destina-se a
representar valores que são extremamente grandes (como distâncias entre planetas ou
galáxias) ou extremamente pequenos (como a massa molecular de uma substância em
quilogramas) e que muitas vezes são imprecisos (como a distância da Terra a outro
sistema solar). O Single tipo está em conformidade com o padrão IEC 60559:1989 (IEEE
754) para aritmética de ponto flutuante binário.

System.Single fornece métodos para comparar instâncias desse tipo, converter o valor
de uma instância em sua representação de cadeia de caracteres e converter a
representação de cadeia de caracteres de um número em uma instância desse tipo. Para
obter informações sobre como os códigos de especificação de formato controlam a
representação de cadeia de caracteres de tipos de valor, consulte Tipos de formatação,
Cadeias de caracteres de formato numérico padrão e Cadeias de caracteres de formato
numérico personalizado.

Representação de ponto flutuante e precisão


O Single tipo de dados armazena valores de ponto flutuante de precisão única em um
formato binário de 32 bits, conforme mostrado na tabela a seguir:

ノ Expandir a tabela

Parte Bits

Significand ou mantissa 0-22

Expoente 23-30

Sinal (0 = positivo, 1 = negativo) 31

Assim como as frações decimais são incapazes de representar com precisão alguns
valores fracionários (como 1/3 ou Math.PI), as frações binárias são incapazes de
representar alguns valores fracionários. Por exemplo, 2/10, que é representado
precisamente por .2 como uma fração decimal, é representado por .0011111001001100
como uma fração binária, com o padrão "1100" repetindo ao infinito. Nesse caso, o
valor de ponto flutuante fornece uma representação imprecisa do número que ele
representa. Realizar operações matemáticas adicionais no valor de ponto flutuante
original geralmente aumenta sua falta de precisão. Por exemplo, se você comparar os
resultados de multiplicar .3 por 10 e adicionar .3 a .3 nove vezes, verá que a adição
produz o resultado menos preciso, porque envolve oito operações a mais do que a
multiplicação. Observe que essa disparidade é aparente somente se você exibir os dois
Single valores usando a cadeia de caracteres de formato numérico padrão "R", que, se
necessário, exibe todos os 9 dígitos de precisão suportados Single pelo tipo.

C#

using System;

public class Example12


{
public static void Main()
{
Single value = .2f;
Single result1 = value * 10f;
Single result2 = 0f;
for (int ctr = 1; ctr <= 10; ctr++)
result2 += value;

Console.WriteLine(".2 * 10: {0:R}", result1);


Console.WriteLine(".2 Added 10 times: {0:R}", result2);
}
}
// The example displays the following output:
// .2 * 10: 2
// .2 Added 10 times: 2.00000024

Como alguns números não podem ser representados exatamente como valores binários
fracionários, os números de ponto flutuante só podem se aproximar de números reais.

Todos os números de ponto flutuante têm um número limitado de dígitos significativos,


o que também determina com que precisão um valor de ponto flutuante se aproxima
de um número real. Um Single valor tem até 7 dígitos decimais de precisão, embora um
máximo de 9 dígitos seja mantido internamente. Isso significa que algumas operações
de ponto flutuante podem não ter a precisão necessária para alterar um valor de ponto
flutuante. O exemplo a seguir define um grande valor de ponto flutuante de precisão
única e, em seguida, adiciona o produto de Single.Epsilon e um quatrilhão a ele. No
entanto, o produto é muito pequeno para modificar o valor original do ponto flutuante.
Seu dígito menos significativo é milésimos, enquanto o dígito mais significativo no
produto é 10-30.
C#

using System;

public class Example13


{
public static void Main()
{
Single value = 123.456f;
Single additional = Single.Epsilon * 1e15f;
Console.WriteLine($"{value} + {additional} = {value + additional}");
}
}
// The example displays the following output:
// 123.456 + 1.401298E-30 = 123.456

A precisão limitada de um número de ponto flutuante tem várias consequências:

Dois números de ponto flutuante que pareçam iguais para uma determinada
precisão podem não ser comparados como iguais porque seus dígitos menos
significantes são diferentes. No exemplo a seguir, uma série de números é somada
e seu total é comparado com o total esperado. Embora os dois valores pareçam
ser os mesmos, uma chamada para o Equals método indica que eles não são.

C#

using System;

public class Example9


{
public static void Main()
{
Single[] values = { 10.01f, 2.88f, 2.88f, 2.88f, 9.0f };
Single result = 27.65f;
Single total = 0f;
foreach (var value in values)
total += value;

if (total.Equals(result))
Console.WriteLine("The sum of the values equals the
total.");
else
Console.WriteLine("The sum of the values ({0}) does not
equal the total ({1}).",
total, result);
}
}
// The example displays the following output:
// The sum of the values (27.65) does not equal the total (27.65).
//
// If the index items in the Console.WriteLine statement are changed to
{0:R},
// the example displays the following output:
// The sum of the values (27.6500015) does not equal the total
(27.65).

Se você alterar os itens de formato na Console.WriteLine(String, Object, Object)


instrução de e {1} para e {1:R} para {0:R} exibir todos os dígitos significativos
dos dois valores, ficará claro que os dois Single valores são desiguais devido a uma
perda de precisão durante as operações de {0} adição. Nesse caso, o problema
pode ser resolvido chamando o Math.Round(Double, Int32) método para
arredondar os Single valores para a precisão desejada antes de realizar a
comparação.

Uma operação matemática ou de comparação que usa um número de ponto


flutuante pode não produzir o mesmo resultado se um número decimal for usado,
porque o número de ponto flutuante binário pode não ser igual ao número
decimal. Um exemplo anterior ilustrou isso exibindo o resultado de multiplicar .3
por 10 e adicionar .3 a .3 nove vezes.

Quando a precisão em operações numéricas com valores fracionários for


importante, use o Decimal tipo em vez do Single tipo. Quando a precisão em
operações numéricas com valores integrais além do intervalo dos Int64 tipos ou
UInt64 for importante, use o BigInteger tipo.

Um valor pode não ser de ida e volta se um número de ponto flutuante estiver
envolvido. Um valor é dito para ida e volta se uma operação converte um número
de ponto flutuante original em outra forma, uma operação inversa transforma a
forma convertida de volta em um número de ponto flutuante e o número de
ponto flutuante final é igual ao número de ponto flutuante original. A viagem de
ida e volta pode falhar porque um ou mais dígitos menos significativos são
perdidos ou alterados em uma conversão. No exemplo a seguir, três Single valores
são convertidos em cadeias de caracteres e salvos em um arquivo. Como mostra a
saída, embora os valores pareçam ser idênticos, os valores restaurados não são
iguais aos valores originais.

C#

using System;
using System.IO;

public class Example10


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\Singles.dat");
Single[] values = { 3.2f / 1.11f, 1.0f / 3f, (float)Math.PI };
for (int ctr = 0; ctr < values.Length; ctr++)
{
sw.Write(values[ctr].ToString());
if (ctr != values.Length - 1)
sw.Write("|");
}
sw.Close();

Single[] restoredValues = new Single[values.Length];


StreamReader sr = new StreamReader(@".\Singles.dat");
string temp = sr.ReadToEnd();
string[] tempStrings = temp.Split('|');
for (int ctr = 0; ctr < tempStrings.Length; ctr++)
restoredValues[ctr] = Single.Parse(tempStrings[ctr]);

for (int ctr = 0; ctr < values.Length; ctr++)


Console.WriteLine("{0} {2} {1}", values[ctr],
restoredValues[ctr],
values[ctr].Equals(restoredValues[ctr]) ?
"=" : "<>");
}
}
// The example displays the following output:
// 2.882883 <> 2.882883
// 0.3333333 <> 0.3333333
// 3.141593 <> 3.141593

Nesse caso, os valores podem ser arredondados com êxito usando a cadeia de
caracteres de formato numérico padrão "G9" para preservar a precisão total dos
Single valores, como mostra o exemplo a seguir.

C#

using System;
using System.IO;

public class Example11


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\Singles.dat");
Single[] values = { 3.2f / 1.11f, 1.0f / 3f, (float)Math.PI };
for (int ctr = 0; ctr < values.Length; ctr++)
sw.Write("{0:G9}{1}", values[ctr], ctr < values.Length - 1
? "|" : "");

sw.Close();

Single[] restoredValues = new Single[values.Length];


StreamReader sr = new StreamReader(@".\Singles.dat");
string temp = sr.ReadToEnd();
string[] tempStrings = temp.Split('|');
for (int ctr = 0; ctr < tempStrings.Length; ctr++)
restoredValues[ctr] = Single.Parse(tempStrings[ctr]);

for (int ctr = 0; ctr < values.Length; ctr++)


Console.WriteLine("{0} {2} {1}", values[ctr],
restoredValues[ctr],
values[ctr].Equals(restoredValues[ctr]) ?
"=" : "<>");
}
}
// The example displays the following output:
// 2.882883 = 2.882883
// 0.3333333 = 0.3333333
// 3.141593 = 3.141593

Single os valores têm menos precisão do que Double os valores. Um Single valor
que é convertido em um aparentemente equivalente Double muitas vezes não é
igual ao Double valor devido a diferenças na precisão. No exemplo a seguir, o
resultado de operações de divisão idênticas é atribuído a um valor e a um
DoubleSingle valor. Depois que o Single valor é convertido em um Double, uma
comparação dos dois valores mostra que eles são desiguais.

C#

using System;

public class Example9


{
public static void Main()
{
Double value1 = 1 / 3.0;
Single sValue2 = 1 / 3.0f;
Double value2 = (Double)sValue2;
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.33333333333333331 = 0.3333333432674408: False

Para evitar esse problema, use o tipo de dados no lugar do tipo de Single dados
ou use o DoubleRound método para que ambos os valores tenham a mesma
precisão.

Teste de igualdade
Para serem considerados iguais, dois Single valores devem representar valores idênticos.
No entanto, devido a diferenças de precisão entre valores, ou devido a uma perda de
precisão por um ou ambos os valores, os valores de ponto flutuante que se espera que
sejam idênticos muitas vezes acabam por ser desiguais devido a diferenças nos seus
dígitos menos significativos. Como resultado, chamadas para o método para determinar
se dois valores são iguais, ou chamadas para o EqualsCompareTo método para
determinar a relação entre dois Single valores, geralmente produzem resultados
inesperados. Isso fica evidente no exemplo a seguir, onde dois valores aparentemente
iguais Single acabam sendo desiguais, pois o primeiro valor tem 7 dígitos de precisão,
enquanto o segundo valor tem 9.

C#

using System;

public class Example


{
public static void Main()
{
float value1 = .3333333f;
float value2 = 1.0f/3;
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.3333333 = 0.333333343: False

Valores calculados que seguem caminhos de código diferentes e que são manipulados
de maneiras diferentes geralmente se mostram desiguais. No exemplo a seguir, um
Single valor é quadrado e, em seguida, a raiz quadrada é calculada para restaurar o
valor original. Um segundo Single é multiplicado por 3,51 e quadrado antes que a raiz
quadrada do resultado seja dividida por 3,51 para restaurar o valor original. Embora os
dois valores pareçam ser idênticos, uma chamada para o Equals(Single) método indica
que eles não são iguais. Usar a cadeia de caracteres de formato padrão "G9" para
retornar uma cadeia de caracteres de resultado que exibe todos os dígitos significativos
de cada Single valor mostra que o segundo valor é .0000000000001 menor que o
primeiro.

C#

using System;

public class Example1


{
public static void Main()
{
float value1 = 10.201438f;
value1 = (float)Math.Sqrt((float)Math.Pow(value1, 2));
float value2 = (float)Math.Pow((float)value1 * 3.51f, 2);
value2 = ((float)Math.Sqrt(value2)) / 3.51f;
Console.WriteLine("{0} = {1}: {2}\n",
value1, value2, value1.Equals(value2));
Console.WriteLine("{0:G9} = {1:G9}", value1, value2);
}
}
// The example displays the following output:
// 10.20144 = 10.20144: False
//
// 10.201438 = 10.2014389

Nos casos em que uma perda de precisão provavelmente afetará o resultado de uma
comparação, você pode usar as seguintes técnicas em vez de chamar o Equals método
ou CompareTo :

Chame o Math.Round método para garantir que ambos os valores tenham a


mesma precisão. O exemplo a seguir modifica um exemplo anterior para usar essa
abordagem para que dois valores fracionários sejam equivalentes.

C#

using System;

public class Example2


{
public static void Main()
{
float value1 = .3333333f;
float value2 = 1.0f / 3;
int precision = 7;
value1 = (float)Math.Round(value1, precision);
value2 = (float)Math.Round(value2, precision);
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.3333333 = 0.3333333: True

O problema da precisão ainda se aplica ao arredondamento dos valores médios.


Para obter mais informações, consulte o método Math.Round(Double, Int32,
MidpointRounding).

Teste de igualdade aproximada em vez de igualdade. Essa técnica requer que você
defina uma quantidade absoluta pela qual os dois valores podem diferir, mas
ainda assim serem iguais, ou que você defina uma quantidade relativa pela qual o
valor menor pode divergir do valor maior.

2 Aviso

Single.Epsilon às vezes é usado como uma medida absoluta da distância


entre dois Single valores ao testar a igualdade. No entanto, Single.Epsilon
mede o menor valor possível que pode ser adicionado ou subtraído de um
Single cujo valor é zero. Para a maioria dos valores positivos e negativos
Single , o valor de Single.Epsilon é muito pequeno para ser detectado.
Portanto, com exceção dos valores que são zero, não recomendamos seu uso
em testes de igualdade.

O exemplo a seguir usa a última abordagem para definir um IsApproximatelyEqual


método que testa a diferença relativa entre dois valores. Ele também contrasta o
resultado das chamadas com o método e o IsApproximatelyEqual Equals(Single)
método.

C#

using System;

public class Example3


{
public static void Main()
{
float one1 = .1f * 10;
float one2 = 0f;
for (int ctr = 1; ctr <= 10; ctr++)
one2 += .1f;

Console.WriteLine("{0:R} = {1:R}: {2}", one1, one2,


one1.Equals(one2));
Console.WriteLine("{0:R} is approximately equal to {1:R}: {2}",
one1, one2,
IsApproximatelyEqual(one1, one2, .000001f));
}

static bool IsApproximatelyEqual(float value1, float value2, float


epsilon)
{
// If they are equal anyway, just return True.
if (value1.Equals(value2))
return true;

// Handle NaN, Infinity.


if (Double.IsInfinity(value1) | Double.IsNaN(value1))
return value1.Equals(value2);
else if (Double.IsInfinity(value2) | Double.IsNaN(value2))
return value1.Equals(value2);

// Handle zero to avoid division by zero


double divisor = Math.Max(value1, value2);
if (divisor.Equals(0))
divisor = Math.Min(value1, value2);

return Math.Abs(value1 - value2) / divisor <= epsilon;


}
}
// The example displays the following output:
// 1 = 1.00000012: False
// 1 is approximately equal to 1.00000012: True

Valores de ponto flutuante e exceções


Operações com valores de ponto flutuante não lançam exceções, ao contrário das
operações com tipos integrais, que lançam exceções em casos de operações ilegais,
como divisão por zero ou transbordamento. Em vez disso, nessas situações, o resultado
de uma operação de ponto flutuante é zero, infinito positivo, infinito negativo ou não
um número (NaN):

Se o resultado de uma operação de ponto flutuante for muito pequeno para o


formato de destino, o resultado será zero. Isso pode ocorrer quando dois números
de ponto flutuante muito pequenos são multiplicados, como mostra o exemplo a
seguir.

C#

using System;

public class Example6


{
public static void Main()
{
float value1 = 1.163287e-36f;
float value2 = 9.164234e-25f;
float result = value1 * value2;
Console.WriteLine("{0} * {1} = {2}", value1, value2, result);
Console.WriteLine("{0} = 0: {1}", result, result.Equals(0.0f));
}
}
// The example displays the following output:
// 1.163287E-36 * 9.164234E-25 = 0
// 0 = 0: True
Se a magnitude do resultado de uma operação de ponto flutuante exceder o
intervalo do formato de destino, o resultado da operação será PositiveInfinity ou
NegativeInfinity, conforme apropriado para o sinal do resultado. O resultado de
uma operação que estoura é , e o resultado de uma operação que estoura
Single.MaxValueSingle.MinValue é PositiveInfinityNegativeInfinity, como mostra o
exemplo a seguir.

C#

using System;

public class Example7


{
public static void Main()
{
float value1 = 3.065e35f;
float value2 = 6.9375e32f;
float result = value1 * value2;
Console.WriteLine("PositiveInfinity: {0}",
Single.IsPositiveInfinity(result));
Console.WriteLine("NegativeInfinity: {0}\n",
Single.IsNegativeInfinity(result));

value1 = -value1;
result = value1 * value2;
Console.WriteLine("PositiveInfinity: {0}",
Single.IsPositiveInfinity(result));
Console.WriteLine("NegativeInfinity: {0}",
Single.IsNegativeInfinity(result));
}
}

// The example displays the following output:


// PositiveInfinity: True
// NegativeInfinity: False
//
// PositiveInfinity: False
// NegativeInfinity: True

PositiveInfinity também resulta de uma divisão por zero com um dividendo


positivo, e NegativeInfinity resulta de uma divisão por zero com um dividendo
negativo.

Se uma operação de ponto flutuante for inválida, o resultado da operação será


NaN. Por exemplo, NaN resultados das seguintes operações:
Divisão por zero com dividendo zero. Note que outros casos de divisão por zero
resultam em um PositiveInfinity ou NegativeInfinityoutro .
Qualquer operação de ponto flutuante com entrada inválida. Por exemplo,
tentar localizar a raiz quadrada de um valor negativo retorna NaN.
Qualquer operação com um argumento cujo valor seja Single.NaN.

Conversões de tipo
A Single estrutura não define nenhum operador de conversão explícito ou implícito, em
vez disso, as conversões são implementadas pelo compilador.

A tabela a seguir lista as possíveis conversões de um valor dos outros tipos numéricos
primitivos para um Single valor, Ele também indica se a conversão está aumentando ou
estreitando e se o resultado Single pode ter menos precisão do que o valor original.

ノ Expandir a tabela

Conversão Alargamento/estreitamento Possível perda de


de precisão

Byte Widening Não

Decimal Widening Sim. Decimal suporta 29


dígitos decimais de
Observe que o C# requer um operador de precisão; Single suporta 9.
transmissão.

Double Estreitamento; valores fora do intervalo são Sim. Double suporta 17


convertidos em Double.NegativeInfinity ou dígitos decimais de
Double.PositiveInfinity. precisão; Single suporta 9.

Int16 Widening Não

Int32 Widening Sim. Int32 suporta 10


dígitos decimais de
precisão; Single suporta 9.

Int64 Widening Sim. Int64 suporta 19


dígitos decimais de
precisão; Single suporta 9.

SByte Widening Não

UInt16 Widening Não

UInt32 Widening Sim. UInt32 suporta 10


dígitos decimais de
precisão; Single suporta 9.
Conversão Alargamento/estreitamento Possível perda de
de precisão

UInt64 Widening Sim. Int64 suporta 20


dígitos decimais de
precisão; Single suporta 9.

O exemplo a seguir converte o valor mínimo ou máximo de outros tipos numéricos


primitivos em um Single valor.

C#

using System;

public class Example4


{
public static void Main()
{
dynamic[] values = { Byte.MinValue, Byte.MaxValue, Decimal.MinValue,
Decimal.MaxValue, Double.MinValue,
Double.MaxValue,
Int16.MinValue, Int16.MaxValue, Int32.MinValue,
Int32.MaxValue, Int64.MinValue, Int64.MaxValue,
SByte.MinValue, SByte.MaxValue, UInt16.MinValue,
UInt16.MaxValue, UInt32.MinValue,
UInt32.MaxValue,
UInt64.MinValue, UInt64.MaxValue };
float sngValue;
foreach (var value in values)
{
if (value.GetType() == typeof(Decimal) ||
value.GetType() == typeof(Double))
sngValue = (float)value;
else
sngValue = value;
Console.WriteLine("{0} ({1}) --> {2:R} ({3})",
value, value.GetType().Name,
sngValue, sngValue.GetType().Name);
}
}
}
// The example displays the following output:
// 0 (Byte) --> 0 (Single)
// 255 (Byte) --> 255 (Single)
// -79228162514264337593543950335 (Decimal) --> -7.92281625E+28
(Single)
// 79228162514264337593543950335 (Decimal) --> 7.92281625E+28 (Single)
// -1.79769313486232E+308 (Double) --> -Infinity (Single)
// 1.79769313486232E+308 (Double) --> Infinity (Single)
// -32768 (Int16) --> -32768 (Single)
// 32767 (Int16) --> 32767 (Single)
// -2147483648 (Int32) --> -2.14748365E+09 (Single)
// 2147483647 (Int32) --> 2.14748365E+09 (Single)
// -9223372036854775808 (Int64) --> -9.223372E+18 (Single)
// 9223372036854775807 (Int64) --> 9.223372E+18 (Single)
// -128 (SByte) --> -128 (Single)
// 127 (SByte) --> 127 (Single)
// 0 (UInt16) --> 0 (Single)
// 65535 (UInt16) --> 65535 (Single)
// 0 (UInt32) --> 0 (Single)
// 4294967295 (UInt32) --> 4.2949673E+09 (Single)
// 0 (UInt64) --> 0 (Single)
// 18446744073709551615 (UInt64) --> 1.84467441E+19 (Single)

Além disso, os Double valores Double.NaN, e convertem em Single.NaN, e


Double.NegativeInfinitySingle.NegativeInfinity,
Double.PositiveInfinitySingle.PositiveInfinityrespectivamente.

Observe que a conversão do valor de alguns tipos numéricos em um Single valor pode
envolver uma perda de precisão. Como o exemplo ilustra, uma perda de precisão é
possível ao converter Decimal, Double, , , Int32UInt32Int64e UInt64 valores em Single
valores.

A conversão de um valor em um SingleDouble é uma conversão de ampliação. A


conversão pode resultar em uma perda de precisão se o tipo não tiver uma
representação precisa para o DoubleSingle valor.

A conversão de um valor em um valor de qualquer tipo de dados numérico primitivo


diferente de a Double é uma conversão de estreitamento e requer um operador cast
(em C#) ou um Single método de conversão (em Visual Basic). Os valores que estão fora
do intervalo do tipo de dados de destino, que são definidos pelas propriedades e
MaxValue do tipo de destino, comportam-se MinValue conforme mostrado na tabela a

seguir.

ノ Expandir a tabela

Tipo de Resultado
destino

Qualquer tipo Uma OverflowException exceção se a conversão ocorrer em um contexto


integral verificado.

Se a conversão ocorrer em um contexto não verificado (o padrão em C#), a


operação de conversão será bem-sucedida, mas o valor estourará.

Decimal Uma OverflowException exceção,


Além disso, , e Single.NegativeInfinity lançar um para conversões em inteiros em um
contexto verificado, Single.NaNSingle.PositiveInfinitymas esses valores transbordam
quando convertidos em inteiros em um OverflowException contexto não verificado. Para
conversões para Decimal, eles sempre lançam um OverflowExceptionarquivo . Para
conversões em , elas convertem em DoubleDouble.NaN, e Double.NegativeInfinity,
Double.PositiveInfinityrespectivamente.

Observe que uma perda de precisão pode resultar da conversão de um Single valor em
outro tipo numérico. No caso de converter valores não integrais Single , como mostra a
saída do exemplo, o componente fracionário é perdido quando o Single valor é
arredondado (como no Visual Basic) ou truncado (como em C# e F#). Para conversões
em Decimal valores, o Single valor pode não ter uma representação precisa no tipo de
dados de destino.

O exemplo a seguir converte vários Single valores em vários outros tipos numéricos. As
conversões ocorrem em um contexto verificado no Visual Basic (o padrão), em C#
(devido à palavra-chave verificada) e em F# (devido à open Checked instrução). A saída
do exemplo mostra o resultado para conversões em um contexto verificado e não
verificado. Você pode executar conversões em um contexto não verificado no Visual
Basic compilando com a opção do compilador, em C# comentando a instrução e em F#
comentando a /removeintchecks+ checked open Checked instrução.

C#

using System;

public class Example5


{
public static void Main()
{
float[] values = { Single.MinValue, -67890.1234f, -12345.6789f,
12345.6789f, 67890.1234f, Single.MaxValue,
Single.NaN, Single.PositiveInfinity,
Single.NegativeInfinity };
checked
{
foreach (var value in values)
{
try
{
Int64 lValue = (long)value;
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
lValue, lValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Int64.",
value);
}
try
{
UInt64 ulValue = (ulong)value;
Console.WriteLine("{0} ({1}) --> {2} (0x{2:X16}) ({3})",
value, value.GetType().Name,
ulValue, ulValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to UInt64.",
value);
}
try
{
Decimal dValue = (decimal)value;
Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dValue, dValue.GetType().Name);
}
catch (OverflowException)
{
Console.WriteLine("Unable to convert {0} to Decimal.",
value);
}

Double dblValue = value;


Console.WriteLine("{0} ({1}) --> {2} ({3})",
value, value.GetType().Name,
dblValue, dblValue.GetType().Name);
Console.WriteLine();
}
}
}
}
// The example displays the following output for conversions performed
// in a checked context:
// Unable to convert -3.402823E+38 to Int64.
// Unable to convert -3.402823E+38 to UInt64.
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// Unable to convert -67890.13 to UInt64.
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// Unable to convert -12345.68 to UInt64.
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// Unable to convert 3.402823E+38 to Int64.
// Unable to convert 3.402823E+38 to UInt64.
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// Unable to convert NaN to Int64.
// Unable to convert NaN to UInt64.
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Unable to convert Infinity to Int64.
// Unable to convert Infinity to UInt64.
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// Unable to convert -Infinity to Int64.
// Unable to convert -Infinity to UInt64.
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)
// The example displays the following output for conversions performed
// in an unchecked context:
// -3.402823E+38 (Single) --> -9223372036854775808
(0x8000000000000000) (Int64)
// -3.402823E+38 (Single) --> 9223372036854775808 (0x8000000000000000)
(UInt64)
// Unable to convert -3.402823E+38 to Decimal.
// -3.402823E+38 (Single) --> -3.40282346638529E+38 (Double)
//
// -67890.13 (Single) --> -67890 (0xFFFFFFFFFFFEF6CE) (Int64)
// -67890.13 (Single) --> 18446744073709483726 (0xFFFFFFFFFFFEF6CE)
(UInt64)
// -67890.13 (Single) --> -67890.12 (Decimal)
// -67890.13 (Single) --> -67890.125 (Double)
//
// -12345.68 (Single) --> -12345 (0xFFFFFFFFFFFFCFC7) (Int64)
// -12345.68 (Single) --> 18446744073709539271 (0xFFFFFFFFFFFFCFC7)
(UInt64)
// -12345.68 (Single) --> -12345.68 (Decimal)
// -12345.68 (Single) --> -12345.6787109375 (Double)
//
// 12345.68 (Single) --> 12345 (0x0000000000003039) (Int64)
// 12345.68 (Single) --> 12345 (0x0000000000003039) (UInt64)
// 12345.68 (Single) --> 12345.68 (Decimal)
// 12345.68 (Single) --> 12345.6787109375 (Double)
//
// 67890.13 (Single) --> 67890 (0x0000000000010932) (Int64)
// 67890.13 (Single) --> 67890 (0x0000000000010932) (UInt64)
// 67890.13 (Single) --> 67890.12 (Decimal)
// 67890.13 (Single) --> 67890.125 (Double)
//
// 3.402823E+38 (Single) --> -9223372036854775808 (0x8000000000000000)
(Int64)
// 3.402823E+38 (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert 3.402823E+38 to Decimal.
// 3.402823E+38 (Single) --> 3.40282346638529E+38 (Double)
//
// NaN (Single) --> -9223372036854775808 (0x8000000000000000) (Int64)
// NaN (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert NaN to Decimal.
// NaN (Single) --> NaN (Double)
//
// Infinity (Single) --> -9223372036854775808 (0x8000000000000000)
(Int64)
// Infinity (Single) --> 0 (0x0000000000000000) (UInt64)
// Unable to convert Infinity to Decimal.
// Infinity (Single) --> Infinity (Double)
//
// -Infinity (Single) --> -9223372036854775808 (0x8000000000000000)
(Int64)
// -Infinity (Single) --> 9223372036854775808 (0x8000000000000000)
(UInt64)
// Unable to convert -Infinity to Decimal.
// -Infinity (Single) --> -Infinity (Double)

Para obter mais informações sobre a conversão de tipos numéricos, consulte Conversão
de tipo no .NET e Tabelas de conversão de tipo.

Funcionalidade de ponto flutuante


A Single estrutura e os tipos relacionados fornecem métodos para executar as seguintes
categorias de operações:

Comparação de valores. Você pode chamar o método para determinar se dois


valores são iguais ou o EqualsCompareTo método para determinar a relação entre
dois Single valores.

A Single estrutura também suporta um conjunto completo de operadores de


comparação. Por exemplo, você pode testar a igualdade ou a desigualdade ou
determinar se um valor é maior ou igual a outro valor. Se um dos operandos for
um , o Single valor será convertido em um DoubleDouble antes de executar a
comparação. Se um dos operandos for um tipo integral, ele será convertido em um
Single antes de realizar a comparação. Embora sejam conversões crescentes, elas
podem envolver uma perda de precisão.
2 Aviso

Devido a diferenças na precisão, dois Single valores que você espera que
sejam iguais podem se tornar desiguais, o que afeta o resultado da
comparação. Consulte a seção Testar para igualdade para obter mais
informações sobre como comparar dois Single valores.

Você também pode chamar os IsNaNmétodos , IsInfinity, IsPositiveInfinitye


IsNegativeInfinity para testar esses valores especiais.

Operações matemáticas. Operações aritméticas comuns, como adição, subtração,


multiplicação e divisão, são implementadas por compiladores de linguagem e
instruções Common Intermediate Language (CIL) em vez de métodos Single . Se o
outro operando em uma operação matemática for um , o é convertido em um
antes de executar a operação, e o Single resultado da operação também é um
DoubleDoubleDouble valor. Se o outro operando for um tipo integral, ele será
convertido em um antes de executar a operação, e o resultado da operação
também será um SingleSingle valor.

Você pode executar outras operações matemáticas chamando static ( Shared no


Visual Basic) métodos na System.Math classe. Estes incluem métodos adicionais
comumente usados para aritmética (como , e ), geometria (como e ) e
Math.SinMath.Sqrtcálculo (como Math.AbsMath.CosMath.Log). Math.Sign Em
todos os casos, o Single valor é convertido em um Doublearquivo .

Você também pode manipular os bits individuais em um Single valor. O


BitConverter.GetBytes(Single) método retorna seu padrão de bits em uma matriz
de bytes. Ao passar essa matriz de bytes para o método, você também pode
preservar o BitConverter.ToInt32 padrão de bits do Single valor em um inteiro de
32 bits.

Arredondamento. O arredondamento é frequentemente usado como uma técnica


para reduzir o impacto das diferenças entre os valores causadas por problemas de
representação e precisão do ponto flutuante. Você pode arredondar um Single
valor chamando o Math.Round método. No entanto, observe que o valor é
convertido em um Double antes que o Single método seja chamado, e a conversão
pode envolver uma perda de precisão.

Formatação. Você pode converter um Single valor em sua representação de cadeia


de caracteres chamando o método ou usando o ToStringrecurso de formatação
composta. Para obter informações sobre como as cadeias de caracteres de
formato controlam a representação de cadeias de caracteres de valores de ponto
flutuante, consulte os tópicos Cadeias de caracteres de formato numérico padrão e
Cadeias de caracteres de formato numérico personalizado.

Analisando cadeias de caracteres. Você pode converter a representação de cadeia


de caracteres de um valor de ponto flutuante em um Single valor chamando o
Parse método or TryParse . Se a operação de análise falhar, o método lançará uma
exceção, enquanto o ParseTryParse método retornará false .

Conversão de tipos. A Single estrutura fornece uma implementação de interface


explícita para a IConvertible interface, que oferece suporte à conversão entre
quaisquer dois tipos de dados padrão do .NET Framework. Os compiladores de
linguagem também suportam a conversão implícita de valores para todos os
outros tipos numéricos padrão, exceto para a conversão de Double valores Single .
A conversão de um valor de qualquer tipo numérico padrão que não seja a para a
DoubleSingle é uma conversão de alargamento e não requer o uso de um
operador de fundição ou método de conversão.

No entanto, a conversão de valores inteiros de 32 bits e 64 bits pode envolver uma


perda de precisão. A tabela a seguir lista as diferenças de precisão para 32 bits, 64
bits e Double tipos:

ノ Expandir a tabela

Tipo Máxima precisão (em dígitos Precisão interna (em dígitos


decimais) decimais)

Double 15 17

Int32 e 10 10
UInt32

Int64 e 19 19
UInt64

Single 7 9

O problema da precisão afeta Single mais frequentemente os valores que são


convertidos em Double valores. No exemplo a seguir, dois valores produzidos por
operações de divisão idênticas são desiguais, porque um dos valores é um valor de
ponto flutuante de precisão única que é convertido em um Doublearquivo .

C#

using System;

public class Example8


{
public static void Main()
{
Double value1 = 1 / 3.0;
Single sValue2 = 1 / 3.0f;
Double value2 = (Double)sValue2;
Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,
value1.Equals(value2));
}
}
// The example displays the following output:
// 0.33333333333333331 = 0.3333333432674408: False

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Métodos System.Single.CompareTo
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Os valores devem ser idênticos para serem considerados iguais. Particularmente quando
os valores de ponto flutuante dependem de várias operações matemáticas, é comum
que eles percam precisão e que seus valores sejam quase idênticos, exceto por seus
dígitos menos significativos. Devido a isso, o valor de retorno do CompareTo método
pode parecer surpreendente às vezes. Por exemplo, a multiplicação por um
determinado valor seguida de divisão pelo mesmo valor deve produzir o valor original,
mas no exemplo a seguir, o valor calculado acaba sendo maior do que o valor original.
Mostrar todos os dígitos significativos dos dois valores usando a cadeia de caracteres
de formato numérico padrão "R" indica que o valor calculado difere do valor original em
seus dígitos menos significativos. Para obter informações sobre como lidar com essas
comparações, consulte a seção Comentários do Equals(Single) método.

Embora um objeto cujo valor é não seja considerado igual a outro objeto cujo valor é
NaNNaN (mesmo ele mesmo), a IComparable<T> interface requer que A.CompareTo(A)
retorne zero.

CompareTo(System.Object)
O value parâmetro deve ser null ou uma instância de Single; caso contrário, uma
exceção é lançada. Qualquer instância de , independentemente de Singleseu valor, é
considerada maior que null .

C#

using System;

public class Example


{
public static void Main()
{
float value1 = 16.5457f;
float operand = 3.8899982f;
object value2 = value1 * operand / operand;
Console.WriteLine("Comparing {0} and {1}: {2}\n",
value1, value2, value1.CompareTo(value2));
Console.WriteLine("Comparing {0:R} and {1:R}: {2}",
value1, value2, value1.CompareTo(value2));
}
}
// The example displays the following output:
// Comparing 16.5457 and 16.5457: -1
//
// Comparing 16.5457 and 16.545702: -1

Este método é implementado para suportar a IComparable interface.

CompareTo(System.Single)
Esse método implementa a interface e executa um pouco melhor do que a
System.IComparable<T>Single.CompareTo(Object) sobrecarga porque não precisa
converter o value parâmetro em um objeto.

C#

using System;

public class Example2


{
public static void Main()
{
float value1 = 16.5457f;
float operand = 3.8899982f;
float value2 = value1 * operand / operand;
Console.WriteLine("Comparing {0} and {1}: {2}\n",
value1, value2, value1.CompareTo(value2));
Console.WriteLine("Comparing {0:R} and {1:R}: {2}",
value1, value2, value1.CompareTo(value2));
}
}
// The example displays the following output:
// Comparing 16.5457 and 16.5457: -1
//
// Comparing 16.5457 and 16.545702: -1

Conversões de expansão
Dependendo da linguagem de programação, talvez seja possível codificar um
CompareTo método em que o tipo de parâmetro tem menos bits (é mais estreito) do
que o tipo de instância. Isso é possível porque algumas linguagens de programação
executam uma conversão de ampliação implícita que representa o parâmetro como um
tipo com tantos bit quanto a instância.

Por exemplo, suponha que o tipo de instância seja Single e o tipo de parâmetro seja
Int32. O compilador do Microsoft C# gera instruções para representar o valor do
parâmetro como um objeto e, em seguida, gera um SingleSingle.CompareTo(Single)
método que compara os valores da instância e a representação ampliada do parâmetro.

Consulte a documentação da linguagem de programação para determinar se o


compilador executa conversões ampliadoras implícitas de tipos numéricos. Para obter
mais informações, consulte o tópico Tabelas de conversão de tipo.

Precisão nas comparações


A precisão dos números de ponto flutuante além da precisão documentada é específica
para a implementação e a versão do .NET. Consequentemente, uma comparação de
dois números específicos pode mudar entre as versões do .NET porque a precisão da
representação interna dos números pode mudar.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Propriedade System.Single.Epsilon
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O valor da propriedade reflete o menor valor positivo Single que é significativo em


operações numéricas ou comparações quando o valor da EpsilonSingle instância é zero.
Por exemplo, o código a seguir mostra que zero e são considerados valores desiguais,
enquanto zero e Epsilon metade do valor de Epsilon são considerados iguais.

C#

using System;

public class Example1


{
public static void Main()
{
float[] values = { 0f, Single.Epsilon, Single.Epsilon * .5f };

for (int ctr = 0; ctr <= values.Length - 2; ctr++)


{
for (int ctr2 = ctr + 1; ctr2 <= values.Length - 1; ctr2++)
{
Console.WriteLine("{0:r} = {1:r}: {2}",
values[ctr], values[ctr2],
values[ctr].Equals(values[ctr2]));
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// 0 = 1.401298E-45: False
// 0 = 0: True
//
// 1.401298E-45 = 0: False

Mais precisamente, o formato de ponto flutuante de precisão única consiste em um


sinal, uma mantissa ou significante de 23 bits e um expoente de 8 bits. Como mostra o
exemplo a seguir, zero tem um expoente de -126 e uma mantissa de 0. Epsilon tem um
expoente de -126 e uma mantissa de 1. Isso significa que Single.Epsilon é o menor valor
positivo Single que é maior que zero e representa o menor valor possível e o menor
incremento possível para um Single cujo expoente é -126.

C#
using System;

public class Example2


{
public static void Main()
{
float[] values = { 0.0f, Single.Epsilon };
foreach (var value in values) {
Console.WriteLine(GetComponentParts(value));
Console.WriteLine();
}
}

private static string GetComponentParts(float value)


{
string result = String.Format("{0:R}: ", value);
int indent = result.Length;

// Convert the single to a 4-byte array.


byte[] bytes = BitConverter.GetBytes(value);
int formattedSingle = BitConverter.ToInt32(bytes, 0);

// Get the sign bit (byte 3, bit 7).


result += String.Format("Sign: {0}\n",
(formattedSingle >> 31) != 0 ? "1 (-)" : "0
(+)");

// Get the exponent (byte 2 bit 7 to byte 3, bits 6)


int exponent = (formattedSingle >> 23) & 0x000000FF;
int adjustment = (exponent != 0) ? 127 : 126;
result += String.Format("{0}Exponent: 0x{1:X4} ({1})\n", new String('
', indent), exponent - adjustment);

// Get the significand (bits 0-22)


long significand = exponent != 0 ?
((formattedSingle & 0x007FFFFF) | 0x800000) :
(formattedSingle & 0x007FFFFF);
result += String.Format("{0}Mantissa: 0x{1:X13}\n", new String(' ',
indent), significand);
return result;
}
}
// // The example displays the following output:
// 0: Sign: 0 (+)
// Exponent: 0xFFFFFF82 (-126)
// Mantissa: 0x0000000000000
//
//
// 1.401298E-45: Sign: 0 (+)
// Exponent: 0xFFFFFF82 (-126)
// Mantissa: 0x0000000000001
No entanto, a propriedade não é uma medida geral de precisão do Single tipo, aplica-se
apenas a EpsilonSingle instâncias que têm um valor de zero.

7 Observação

O valor da propriedade não é equivalente ao épsilon da Epsilon máquina, que


representa o limite superior do erro relativo devido ao arredondamento na
aritmética de ponto flutuante.

O valor dessa constante é 1,4e-45.

Dois números de ponto flutuante aparentemente equivalentes podem não se comparar


iguais por causa de diferenças em seus dígitos menos significativos. Por exemplo, a
expressão C#, , não se compara igual porque a operação de divisão no lado esquerdo
tem precisão máxima, (float)1/3 == (float)0.33333 enquanto a constante no lado
direito é precisa apenas para os dígitos especificados. Se você criar um algoritmo
personalizado que determine se dois números de ponto flutuante podem ser
considerados iguais, deverá usar um valor maior que a constante para estabelecer a
Epsilon margem absoluta de diferença aceitável para que os dois valores sejam
considerados iguais. (Normalmente, essa margem de diferença é muitas vezes maior do
que Epsilon.)

Notas da plataforma
Em sistemas ARM, o Epsilon valor da constante é muito pequeno para ser detectado,
por isso equivale a zero. Você pode definir um valor de épsilon alternativo que seja igual
a 1,175494351E-38.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.Single.Equals
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Single.Equals(Single) método implementa a System.IEquatable<T> interface e executa


um pouco melhor do que Single.Equals(Object) porque ele não precisa converter o obj
parâmetro em um objeto.

Conversões de expansão
Dependendo da linguagem de programação, talvez seja possível codificar um Equals
método em que o tipo de parâmetro tem menos bits (é mais estreito) do que o tipo de
instância. Isso é possível porque algumas linguagens de programação executam uma
conversão de ampliação implícita que representa o parâmetro como um tipo com
tantos bit quanto a instância.

Por exemplo, suponha que o tipo de instância seja Single e o tipo de parâmetro seja
Int32. O compilador do Microsoft C# gera instruções para representar o valor do
parâmetro como um objeto e, em seguida, gera um SingleSingle.Equals(Single) método
que compara os valores da instância e a representação ampliada do parâmetro.

Consulte a documentação da linguagem de programação para determinar se o


compilador executa conversões ampliadoras implícitas de tipos numéricos. Para obter
mais informações, consulte Tabelas de conversão de tipo.

Precisão nas comparações


O Equals método deve ser usado com cautela, pois dois valores aparentemente
equivalentes podem ser desiguais devido à precisão diferente dos dois valores. O
exemplo a seguir relata que o valor .3333 e o SingleSingle retornado dividindo 1 por 3
são desiguais.

C#

// Initialize two floats with apparently identical values


float float1 = .33333f;
float float2 = 1/3;
// Compare them for equality
Console.WriteLine(float1.Equals(float2)); // displays false
Uma técnica de comparação que evita os problemas associados à comparação para
igualdade envolve a definição de uma margem de diferença aceitável entre dois valores
(como 0,01% de um dos valores). Se o valor absoluto da diferença entre os dois valores
for inferior ou igual a essa margem, é provável que a diferença resulte de diferenças de
precisão e, por conseguinte, é provável que os valores sejam iguais. O exemplo a seguir
usa essa técnica para comparar .33333 e 1/3, que são os dois Single valores que o
exemplo de código anterior encontrou como desiguais.

C#

// Initialize two floats with apparently identical values


float float1 = .33333f;
float float2 = (float) 1/3;
// Define the tolerance for variation in their values
float difference = Math.Abs(float1 * .0001f);

// Compare the values


// The output to the console indicates that the two values are equal
if (Math.Abs(float1 - float2) <= difference)
Console.WriteLine("float1 and float2 are equal.");
else
Console.WriteLine("float1 and float2 are unequal.");

Nesse caso, os valores são iguais.

7 Observação

Como Epsilon define a expressão mínima de um valor positivo cujo intervalo é


próximo de zero, a margem de diferença deve ser maior que Epsilon.
Normalmente, é muitas vezes maior do que Epsilon. Por isso, recomendamos que
você não use Epsilon ao comparar Double valores para igualdade.

Uma segunda técnica que evita os problemas associados à comparação para igualdade
envolve a comparação da diferença entre dois números de ponto flutuante com algum
valor absoluto. Se a diferença for menor ou igual a esse valor absoluto, os números
serão iguais. Se for maior, os números não são iguais. Uma maneira de fazer isso é
selecionar arbitrariamente um valor absoluto. No entanto, isso é problemático, pois uma
margem de diferença aceitável depende da magnitude dos Single valores. Uma segunda
maneira aproveita um recurso de design do formato de ponto flutuante: A diferença
entre os componentes mantissa nas representações inteiras de dois valores de ponto
flutuante indica o número de possíveis valores de ponto flutuante que separa os dois
valores. Por exemplo, a diferença entre 0,0 e Epsilon é 1, porque Epsilon é o menor valor
representável quando se trabalha com um Single cujo valor é zero. O exemplo a seguir
usa essa técnica para comparar .33333 e 1/3, que são os dois Double valores que o
exemplo de código anterior com o Equals(Single) método encontrado para ser desigual.
Observe que o exemplo usa os BitConverter.GetBytes métodos e BitConverter.ToInt32
para converter um valor de ponto flutuante de precisão única em sua representação
inteira.

C#

using System;

public class Example


{
public static void Main()
{
float value1 = .1f * 10f;
float value2 = 0f;
for (int ctr = 0; ctr < 10; ctr++)
value2 += .1f;

Console.WriteLine("{0:R} = {1:R}: {2}", value1, value2,


HasMinimalDifference(value1, value2, 1));
}

public static bool HasMinimalDifference(float value1, float value2, int


units)
{
byte[] bytes = BitConverter.GetBytes(value1);
int iValue1 = BitConverter.ToInt32(bytes, 0);

bytes = BitConverter.GetBytes(value2);
int iValue2 = BitConverter.ToInt32(bytes, 0);

// If the signs are different, return false except for +0 and -0.
if ((iValue1 >> 31) != (iValue2 >> 31))
{
if (value1 == value2)
return true;

return false;
}

int diff = Math.Abs(iValue1 - iValue2);

if (diff <= units)


return true;

return false;
}
}
// The example displays the following output:
// 1 = 1.00000012: True
A precisão dos números de ponto flutuante além da precisão documentada é específica
para a implementação e a versão do .NET. Consequentemente, uma comparação de
dois números pode produzir resultados diferentes dependendo da versão do .NET,
porque a precisão da representação interna dos números pode mudar.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Datas, horas e fusos horários
Artigo • 27/01/2024

Além da estrutura DateTime básica, o .NET fornece as seguintes classes que dão suporte
para trabalhar com fusos horários:

TimeZone

Use esta classe para trabalhar com o fuso horário local do sistema e a zona UTC
(Tempo Universal Coordenado). A funcionalidade da classe TimeZone é
amplamente substituída pela classe TimeZoneInfo.

TimeZoneInfo

Use essa classe para trabalhar com qualquer fuso horário que esteja predefinido
em um sistema, para criar novos fusos horários e para converter facilmente datas e
horas de um fuso horário para outro. Para novos desenvolvimentos, use a classe
TimeZoneInfo em vez da classe TimeZone.

DateTimeOffset

Use essa estrutura para trabalhar com datas e horas cujo deslocamento (ou
diferença) em relação ao horário UTC é conhecido. A estrutura DateTimeOffset
combina um valor de data e hora com o deslocamento desse horário em relação
ao UTC. Devido à sua relação com o UTC, um valor individual de data e hora
identifica, sem ambiguidade um único ponto no tempo. Isso aumenta a
portabilidade de um computador para outro de um valor de DateTimeOffset em
relação a um valor de DateTime.

Do .NET 6 em diante, os seguintes tipos estão disponíveis:

DateOnly

Use essa estrutura ao trabalhar com um valor que representa apenas uma data. A
data representa o dia inteiro, desde o início do dia até o final. DateOnly tem um
intervalo de 0001-01-01 até 9999-12-31 . E esse tipo representa a combinação de
mês, dia e ano sem uma hora específica. Se você usava um tipo DateTime em seu
código para representar uma data que desconsiderava a hora, use agora esse tipo.

TimeOnly

Use essa estrutura para representar uma hora sem uma data. A hora representa as
horas, os minutos e os segundos de um dia não específico. TimeOnly tem um
intervalo de 00:00:00.0000000 a 23:59:59.9999999 . Esse tipo pode ser usado para
substituir tipos DateTime e TimeSpan no código quando você usou esses tipos para
representar uma hora.

A próxima seção fornece as informações de que você precisa para trabalhar com fusos
horários e para criar aplicativos com reconhecimento de fuso horário que podem
converter datas e horas de um fuso horário para outro.

Nesta seção
Visão geral do fuso horário
Aborda a terminologia, os conceitos e os problemas envolvidos na criação de aplicativos
com reconhecimento de fuso horário.

Escolhendo entre DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo


Discute quando usar os tipos DateTime, DateTimeOffset e TimeZoneInfo ao lidar com os
dados de data e hora.

Encontrando os fusos horários definidos em um sistema local


Descreve como enumerar os fusos horários encontrados em um sistema local.

Como: enumerar os fusos horários presentes em um computador


Fornece exemplos que enumeram os fusos horários definidos no Registro de um
computador e que permitem aos usuários selecionar um fuso horário predefinido em
uma lista.

Como: acessar os objetos de fuso horário predefinidos UTC e local


Descreve como acessar o fuso horário local e do Tempo Universal Coordenado.

Como: criar uma instância de um objeto TimeZoneInfo


Descreve como criar uma instância de um objeto TimeZoneInfo no Registro do sistema
local.

Criando uma instância de um objeto DateTimeOffset


Discute as maneiras de criar uma instância de um objeto DateTimeOffset e maneiras de
converter um valor DateTime em um valor DateTimeOffset.

Como: criar fusos horários sem regras de ajuste


Descreve como criar um fuso horário personalizado que não dê suporte à transição
bidirecional do horário de verão.

Como: criar fusos horários com regras de ajuste


Descreve como criar um fuso horário personalizado que dê suporte a uma ou mais
transições bidirecionais do horário de verão.

Salvar e restaurar fusos horários


Descreve o suporte de TimeZoneInfo para serialização e desserialização dos dados de
fuso horário e ilustra alguns dos cenários nos quais esses recursos podem ser usados.

Como: salvar fusos horários em um recurso inserido


Descreve como criar um fuso horário personalizado e salvar suas informações em um
arquivo de recurso.

Como: restaurar fusos horários de um recurso inserido


Descreve como criar uma instância de fusos horários personalizados que foram salvos
em um arquivo de recurso inserido.

Executando operações aritméticas com datas e horas


Discute os problemas envolvidos em adicionar, subtrair e comparar valores DateTime e
DateTimeOffset.

Como: usar fusos horários em aritmética de data e hora


Aborda como realizar a aritmética de data e hora que reflete as regras de ajuste de um
fuso horário.

Convertendo entre DateTime e DateTimeOffset


Descreve como converter entre valore DateTime e DateTimeOffset.

Convertendo horários entre fusos horários


Descreve como converter horários de um fuso horário para outro.

Como: resolver horários ambíguos


Descreve como resolver um horário ambíguo, mapeando-o para o horário padrão do
fuso horário.

Como: permitir que os usuários resolvam horários ambíguos


Descreve como permitir que um usuário determine o mapeamento entre um horário
local ambíguo e o Tempo Universal Coordenado.

Referência
System.TimeZoneInfo

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Escolher entre DateTime, DateOnly,
DateTimeOffset, TimeSpan, TimeOnly e
TimeZoneInfo
Artigo • 05/06/2023

Os aplicativos do .NET podem usar as informações de data e hora de várias maneiras.


Os usos mais comuns das informações de data e hora incluem:

Para refletir somente uma data, uma vez que a informação de tempo não é
importante.
Para refletir somente um tempo, uma vez que a informação de data não é
importante.
Para refletir uma data abstrata e um tempo que não está vinculado a uma hora e
local específicos (por exemplo, a maioria das lojas em um uma cadeia internacional
abre em dias da semana às 9h).
Para recuperar as informações de data e hora de fontes fora do .NET, normalmente
nas quais as informações de data e hora são armazenadas em um tipo de dados
simples.
Para identificar um único ponto no tempo de maneira única e não ambígua.
Alguns aplicativos exigem que a data e a hora sejam inequívocas somente no
sistema host. Outros exigem que sejam inequívocas em todos os sistemas (ou seja,
uma data serializada em um sistema pode ser significativamente desserializada e
usada em outro sistema em qualquer lugar do mundo).
Para preservar vários horários relacionados (como o horário local do solicitante e o
horário de recebimento de uma solicitação Web pelo servidor).
Para realizar a aritmética de data e hora, possivelmente com um resultado que
identifica de maneira única e não ambígua um único ponto no tempo.

O .NET inclui os tipos DateTime, DateOnly, DateTimeOffset, TimeSpan, TimeOnly e


TimeZoneInfo. Todos eles podem ser usados para compilar aplicativos que funcionam
com datas e horas.

7 Observação

Este artigo não discute o TimeZone, pois a funcionalidade foi quase totalmente
incorporada à classe TimeZoneInfo. Sempre que possível, use a classe
TimeZoneInfo, ao invés da classe TimeZone.
A estrutura DateTimeOffset
A estrutura DateTimeOffset representa um valor de data e hora, juntamente com um
deslocamento que indica quanto o valor difere do UTC. Portanto, o valor sempre
identifica sem ambiguidade um único ponto no tempo.

O tipo DateTimeOffset inclui toda a funcionalidade do tipo DateTime, juntamente com


reconhecimento de fuso horário. Isso o torna adequado para aplicativos que:

Identifique de maneira única e não ambígua um único ponto no tempo. O tipo


DateTimeOffset pode ser usado para definir de forma inequívoca o significado de
"agora", para registrar os tempos de transação, para registrar os tempos dos
eventos de sistema ou de aplicativo e para registrar a criação de arquivos e os
tempos de modificação.
Realizar aritmética geral de data e hora.
Preserve vários tempos relacionados contanto que esses tempos sejam
armazenados como dois valores separados ou como dois membros de uma
estrutura.

7 Observação

Esses usos para os valores DateTimeOffset são muito mais comuns do que aqueles
para os valores DateTime. Como resultado, considere o DateTimeOffset como o
tipo de data e hora padrão para desenvolvimento de aplicativos.

Um valor DateTimeOffset não está vinculado a um fuso horário específico, mas pode ser
proveniente de diversos fusos horários. O exemplo a seguir lista os fusos horários aos
quais inúmeros valores DateTimeOffset (incluindo a Hora Padrão do Pacífico local)
podem pertencer.

C#

using System;
using System.Collections.ObjectModel;

public class TimeOffsets


{
public static void Main()
{
DateTime thisDate = new DateTime(2007, 3, 10, 0, 0, 0);
DateTime dstDate = new DateTime(2007, 6, 10, 0, 0, 0);
DateTimeOffset thisTime;

thisTime = new DateTimeOffset(dstDate, new TimeSpan(-7, 0, 0));


ShowPossibleTimeZones(thisTime);
thisTime = new DateTimeOffset(thisDate, new TimeSpan(-6, 0, 0));
ShowPossibleTimeZones(thisTime);

thisTime = new DateTimeOffset(thisDate, new TimeSpan(+1, 0, 0));


ShowPossibleTimeZones(thisTime);
}

private static void ShowPossibleTimeZones(DateTimeOffset offsetTime)


{
TimeSpan offset = offsetTime.Offset;
ReadOnlyCollection<TimeZoneInfo> timeZones;

Console.WriteLine("{0} could belong to the following time zones:",


offsetTime.ToString());
// Get all time zones defined on local system
timeZones = TimeZoneInfo.GetSystemTimeZones();
// Iterate time zones
foreach (TimeZoneInfo timeZone in timeZones)
{
// Compare offset with offset for that date in that time zone
if (timeZone.GetUtcOffset(offsetTime.DateTime).Equals(offset))
Console.WriteLine(" {0}", timeZone.DisplayName);
}
Console.WriteLine();
}
}
// This example displays the following output to the console:
// 6/10/2007 12:00:00 AM -07:00 could belong to the following time
zones:
// (GMT-07:00) Arizona
// (GMT-08:00) Pacific Time (US & Canada)
// (GMT-08:00) Tijuana, Baja California
//
// 3/10/2007 12:00:00 AM -06:00 could belong to the following time
zones:
// (GMT-06:00) Central America
// (GMT-06:00) Central Time (US & Canada)
// (GMT-06:00) Guadalajara, Mexico City, Monterrey - New
// (GMT-06:00) Guadalajara, Mexico City, Monterrey - Old
// (GMT-06:00) Saskatchewan
//
// 3/10/2007 12:00:00 AM +01:00 could belong to the following time
zones:
// (GMT+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna
// (GMT+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
// (GMT+01:00) Brussels, Copenhagen, Madrid, Paris
// (GMT+01:00) Sarajevo, Skopje, Warsaw, Zagreb
// (GMT+01:00) West Central Africa

A saída mostra que cada valor de data e hora nesse exemplo pode pertencer a pelo
menos três fusos horários diferentes. O valor DateTimeOffset de 10/06/2007 mostra
que, se um valor de data e hora representa um horário de verão, a diferença UTC não
corresponde necessariamente à diferença UTC de base do fuso horário de origem ou à
diferença UTC encontrada no nome de exibição. Como um único valor DateTimeOffset
não está rigorosamente acoplado ao fuso horário, ele não pode refletir a transição de
um fuso horário para o horário de verão. Isso pode ser problemático quando a
aritmética de data e hora é usada para manipular um valor DateTimeOffset. Para ver
uma discussão sobre como realizar a aritmética de data e hora de uma forma que
considere as regras de ajuste de um fuso horário, consulte Executando operações
aritméticas com datas e horas.

A estrutura DateTime
Um valor DateTime define a data e a hora específicas. Isso inclui uma propriedade Kind
que fornece informações limitadas sobre o fuso horário ao qual a data e a hora
pertencem. O valor DateTimeKind retornado pela propriedade Kind indica se o valor
DateTime representa a hora local (DateTimeKind.Local), UTC (Tempo Universal
Coordenado) (DateTimeKind.Utc) ou uma hora não especificada
(DateTimeKind.Unspecified).

A estrutura DateTime é adequada para aplicativos com uma ou mais das seguintes
características:

Trabalhar com datas e horas abstratas.


Trabalhar com datas e horas para as quais as informações de fuso horário estão
ausentes.
Trabalhar apenas com datas e horas UTC.
Realizar aritmética de data e hora, mas que há preocupação com resultados gerais.
Por exemplo, em uma operação de adição que soma seis meses a uma data e hora
determinada, geralmente não é importante se o resultado é ajustado para horário
de verão.

A menos que um valor DateTime específico represente o UTC, geralmente a


portabilidade do valor de data e hora é ambígua ou limitada. Por exemplo, se um valor
DateTime representa a hora local, ele é portátil nesse fuso horário local (ou seja, se o
valor for desserializado em outro sistema no mesmo fuso horário, esse valor ainda
identificará de maneira inequívoca um único ponto no tempo). Fora do fuso horário
local, esse valor DateTime pode ter várias interpretações. Se a propriedade Kind do valor
for DateTimeKind.Unspecified, o valor será ainda menos portátil: agora ele é ambíguo
no mesmo fuso horário e possivelmente até no mesmo sistema onde foi serializado pela
primeira vez. Somente se um valor DateTime representar o UTC, esse valor identifica de
forma inequívoca um único ponto no tempo, independentemente do sistema ou do
fuso horário em que o valor é usado.
) Importante

Ao salvar ou compartilhar dados DateTime, use UTC e defina a propriedade Kind


do valor DateTime como DateTimeKind.Utc.

A estrutura DateOnly
A estrutura DateOnly representa uma data específica, sem a hora. Como não tem
nenhum componente de hora, ela representa uma data do início do dia até o final do
dia. Essa estrutura é ideal para armazenar datas específicas, como uma data de
nascimento, uma data de aniversário, um feriado ou uma data relacionada a negócios.

Embora você possa usar DateTime enquanto ignora o componente de hora, há alguns
benefícios em usar DateOnly em vez de DateTime :

A estrutura DateTime poderá ser revertida para o dia anterior ou seguinte se for
deslocada por um fuso horário. DateOnly não pode ser deslocado por um fuso
horário e sempre representa a data que foi definida.
Serializar uma estrutura DateTime inclui o componente de hora, que pode
obscurecer a intenção dos dados. Além disso, DateOnly serializa menos dados.
Quando o código interage com um banco de dados, como o SQL Server, datas
inteiras geralmente são armazenadas como o tipo de dados date , que não inclui
uma hora. DateOnly corresponde melhor ao tipo de banco de dados.

Para obter mais informações sobre DateOnly , consulte Como usar as estruturas
DateOnly e TimeOnly.

) Importante

DateOnly não está disponível no .NET Framework.

A estrutura TimeSpan
A estrutura TimeSpan representa um intervalo de tempo. Seus dois usos típicos são:

Refletir o intervalo de tempo entre dois valores de data e hora. Por exemplo,
subtrair um valor DateTime de outro retorna um valor TimeSpan.
Calcular o tempo decorrido. Por exemplo, a propriedade Stopwatch.Elapsedretorna
um valor TimeSpan, que reflete o intervalo de tempo decorrido desde a chamada
para um dos métodos Stopwatch que começa a medir o tempo decorrido.

Um valor TimeSpan também pode ser usado como uma substituição para um valor
DateTime, quando esse valor reflete um tempo sem referência a determinado dia. Esse
uso é semelhante às propriedades DateTime.TimeOfDaye DateTimeOffset.TimeOfDay,
que retornam um valor TimeSpan que representa o tempo sem referência a uma data.
Por exemplo, a estrutura TimeSpan pode ser usada para refletir a hora de abertura ou
de fechamento diário de um repositório ou pode ser usada para representar a hora em
que qualquer evento regular ocorre.

O exemplo a seguir define uma estrutura StoreInfo que inclui objetos TimeSpan para a
hora de abertura e de fechamento do repositório, bem como um objeto TimeZoneInfo
que representa o fuso horário do repositório. A estrutura também inclui dois métodos,
IsOpenNow e IsOpenAt , que indica se o repositório está aberto em um momento

especificado pelo usuário, que se considera estar no fuso horário local.

C#

using System;

public struct StoreInfo


{
public String store;
public TimeZoneInfo tz;
public TimeSpan open;
public TimeSpan close;

public bool IsOpenNow()


{
return IsOpenAt(DateTime.Now.TimeOfDay);
}

public bool IsOpenAt(TimeSpan time)


{
TimeZoneInfo local = TimeZoneInfo.Local;
TimeSpan offset = TimeZoneInfo.Local.BaseUtcOffset;

// Is the store in the same time zone?


if (tz.Equals(local)) {
return time >= open & time <= close;
}
else {
TimeSpan delta = TimeSpan.Zero;
TimeSpan storeDelta = TimeSpan.Zero;

// Is it daylight saving time in either time zone?


if (local.IsDaylightSavingTime(DateTime.Now.Date + time))
delta = local.GetAdjustmentRules()
[local.GetAdjustmentRules().Length - 1].DaylightDelta;
if
(tz.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(DateTime.Now.Date + time,
local, tz)))
storeDelta = tz.GetAdjustmentRules()
[tz.GetAdjustmentRules().Length - 1].DaylightDelta;

TimeSpan comparisonTime = time + (offset -


tz.BaseUtcOffset).Negate() + (delta - storeDelta).Negate();
return comparisonTime >= open & comparisonTime <= close;
}
}
}

A estrutura StoreInfo pode ser usada pelo código do cliente como o exposto a seguir.

C#

public class Example


{
public static void Main()
{
// Instantiate a StoreInfo object.
var store103 = new StoreInfo();
store103.store = "Store #103";
store103.tz = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard
Time");
// Store opens at 8:00.
store103.open = new TimeSpan(8, 0, 0);
// Store closes at 9:30.
store103.close = new TimeSpan(21, 30, 0);

Console.WriteLine("Store is open now at {0}: {1}",


DateTime.Now.TimeOfDay, store103.IsOpenNow());
TimeSpan[] times = { new TimeSpan(8, 0, 0), new TimeSpan(21, 0, 0),
new TimeSpan(4, 59, 0), new TimeSpan(18, 31, 0)
};
foreach (var time in times)
Console.WriteLine("Store is open at {0}: {1}",
time, store103.IsOpenAt(time));
}
}
// The example displays the following output:
// Store is open now at 15:29:01.6129911: True
// Store is open at 08:00:00: True
// Store is open at 21:00:00: False
// Store is open at 04:59:00: False
// Store is open at 18:31:00: False

A estrutura TimeOnly
A estrutura TimeOnly representa um valor de hora do dia, como um despertador diário
ou a hora em que você almoça todos os dias. TimeOnly é limitado ao intervalo de
00:00:00.0000000 - 23:59:59.9999999, uma hora específica do dia.

Antes do tipo TimeOnly ser introduzido, os programadores normalmente usavam o tipo


DateTime ou o tipo TimeSpan para representar uma hora específica. No entanto, usar
essas estruturas para simular uma hora sem uma data pode introduzir alguns
problemas, o que TimeOnly resolve:

TimeSpan representa o tempo decorrido, como o tempo medido com um

cronômetro. O intervalo superior é de mais de 29.000 anos, e seu valor pode ser
negativo para indicar a movimentação para trás no tempo. Um TimeSpan negativo
não indica uma hora específica do dia.
Se TimeSpan for usado como uma hora do dia, há o risco de que possa ser
manipulado para um valor fora do dia de 24 horas. TimeOnly não tem esse risco.
Por exemplo, se o turno de trabalho de um funcionário começar às 18:00 e durar 8
horas, adicionar 8 horas à estrutura TimeOnly será revertido para 2:00.
Usar DateTime para uma hora do dia requer que uma data arbitrária seja associada
à hora e, em seguida, seja desconsiderada. É uma prática comum escolher
DateTime.MinValue (0001-01-01) como a data; no entanto, se horas forem

subtraídas do valor DateTime , uma exceção OutOfRange poderá ocorrer. TimeOnly


não tem esse problema, pois o tempo decorre para frente e para trás em torno do
período de 24 horas.
Serializar uma estrutura DateTime inclui o componente de data, que pode
obscurecer a intenção dos dados. Além disso, TimeOnly serializa menos dados.

Para obter mais informações sobre TimeOnly , consulte Como usar as estruturas
DateOnly e TimeOnly.

) Importante

TimeOnly não está disponível no .NET Framework.

A classe TimeZoneInfo
A classe TimeZoneInfo representa qualquer um dos fusos horários da Terra e permite a
conversão de qualquer data e hora em um fuso horário para o equivalente em outro
fuso horário. A classe TimeZoneInfo possibilita trabalhar com datas e horas de modo
que qualquer valor de data e hora identifique de forma inequívoca um único ponto no
tempo. A classe TimeZoneInfo também é extensível. Embora dependa das informações
de fuso horário fornecidas para sistemas Windows e definidas no registro, ela permite a
criação de fusos horários personalizados. Ela também permite a serialização e
desserialização das informações de fuso horário.

Em alguns casos, aproveitar ao máximo a classe TimeZoneInfo pode exigir um trabalho


adicional de desenvolvimento. Se os valores de data e hora não forem rigorosamente
acoplados aos fusos horários aos quais pertencem, um trabalho adicional será
necessário. A menos que o aplicativo ofereça um mecanismo para vincular a data e hora
ao fuso horário associado, é fácil para determinado valor de data e hora se desassociar
do fuso horário. Um método de vinculação dessas informações é definir uma classe ou
estrutura que contenha o valor de data e hora e seu objeto de fuso horário associado.

Para aproveitar o suporte a fuso horário no .NET, você deve saber o fuso horário a que
um valor de data e hora pertence, quando esse objeto de data e hora for instanciado.
Muitas vezes, o fuso horário não é conhecido, especialmente em aplicativos Web ou de
rede.

Confira também
Datas, horas e fusos horários

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
be found on GitHub, where you source. Provide feedback here.
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Trabalhar com calendários
Artigo • 10/05/2023

Embora um valor de data e hora representem um momento, sua representação de


cadeia de caracteres depende da cultura e também das convenções usadas para exibir
valores de data e hora por uma cultura específica e do calendário usado por essa
cultura. Este tópico explora o suporte a calendários no .NET e discute o uso de classes
de calendário ao trabalhar com valores de data.

Calendários no .NET
Todos os calendários no .NET derivam da classe System.Globalization.Calendar, a qual
fornece a implementação do calendário base. Uma das classes que herda da classe
Calendar é a classe EastAsianLunisolarCalendar, a qual é a classe base para todos os
calendários lunissolares. O .NET inclui as seguintes implementações de calendários:

ChineseLunisolarCalendar, que representa o calendário lunissolar chinês.

GregorianCalendar, que representa o calendário gregoriano. Esse calendário é


particionado em subtipos adicionais (como árabe e francês do Oriente Médio) que
são definidos pela enumeração System.Globalization.GregorianCalendarTypes. A
propriedade GregorianCalendar.CalendarType especifica o subtipo do calendário
gregoriano.

HebrewCalendar, que representa o calendário hebraico.

HijriCalendar, que representa o calendário islâmico.

JapaneseCalendar, que representa o calendário japonês.

JapaneseLunisolarCalendar, que representa o calendário lunissolar japonês.

JulianCalendar, que representa o calendário juliano.

KoreanCalendar, que representa o calendário coreano.

KoreanLunisolarCalendar, que representa o calendário lunissolar coreano.

PersianCalendar, que representa o calendário persa.

TaiwanCalendar, que representa o calendário de Taiwan.

TaiwanLunisolarCalendar, que representa o calendário lunissolar de Taiwan.


ThaiBuddhistCalendar, que representa o calendário tailandês budista.

UmAlQuraCalendar, que representa o calendário Um Al Qura.

Um calendário pode ser usado em uma de duas formas:

Como o calendário usado por uma cultura específica. Cada objeto CultureInfo
contém um calendário atual, que é o calendário que o objeto está usando no
momento. As representações de cadeia de caracteres de todos os valores de data
e hora refletem automaticamente a cultura e seu calendário atual. Normalmente, o
calendário atual é o calendário padrão da cultura. Os objetos CultureInfo também
possuem calendários opcionais, que incluem os calendários adicionais que a
cultura pode usar.

Como calendário autônomo independente de uma cultura específica. Nesse caso,


os métodos Calendar são usados para expressar datas como os valores que
refletem o calendário.

Observe que seis classes de calendário – ChineseLunisolarCalendar,


JapaneseLunisolarCalendar, JulianCalendar, KoreanLunisolarCalendar, PersianCalendar e
TaiwanLunisolarCalendar – podem ser usadas apenas como calendários autônomos. Elas
não são usadas por nenhuma cultura, seja como o calendário padrão ou como um
calendário opcional.

Calendários e culturas
Cada cultura tem um calendário padrão, o qual é definido pela propriedade
CultureInfo.Calendar. A propriedade CultureInfo.OptionalCalendars retorna uma matriz
de objetos Calendar que especifica todos os calendários suportados por uma cultura
específica, inclusive o calendário padrão da cultura.

O exemplo a seguir ilustra as propriedades CultureInfo.Calendar e


CultureInfo.OptionalCalendars. Ele cria objetos CultureInfo para as culturas Tailandês
(Tailândia) e Japonês (Japão) e exibe seus calendários padrão e opcionais. Observe que,
em ambos os casos, o calendário padrão da cultura também é incluído na coleção
CultureInfo.OptionalCalendars.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
// Create a CultureInfo for Thai in Thailand.
CultureInfo th = CultureInfo.CreateSpecificCulture("th-TH");
DisplayCalendars(th);

// Create a CultureInfo for Japanese in Japan.


CultureInfo ja = CultureInfo.CreateSpecificCulture("ja-JP");
DisplayCalendars(ja);
}

static void DisplayCalendars(CultureInfo ci)


{
Console.WriteLine("Calendars for the {0} culture:", ci.Name);

// Get the culture's default calendar.


Calendar defaultCalendar = ci.Calendar;
Console.Write(" Default Calendar: {0}",
GetCalendarName(defaultCalendar));

if (defaultCalendar is GregorianCalendar)
Console.WriteLine(" ({0})",
((GregorianCalendar)
defaultCalendar).CalendarType);
else
Console.WriteLine();

// Get the culture's optional calendars.


Console.WriteLine(" Optional Calendars:");
foreach (var optionalCalendar in ci.OptionalCalendars) {
Console.Write("{0,6}{1}", "", GetCalendarName(optionalCalendar));
if (optionalCalendar is GregorianCalendar)
Console.Write(" ({0})",
((GregorianCalendar)
optionalCalendar).CalendarType);

Console.WriteLine();
}
Console.WriteLine();
}

static string GetCalendarName(Calendar cal)


{
return cal.ToString().Replace("System.Globalization.", "");
}
}
// The example displays the following output:
// Calendars for the th-TH culture:
// Default Calendar: ThaiBuddhistCalendar
// Optional Calendars:
// ThaiBuddhistCalendar
// GregorianCalendar (Localized)
//
// Calendars for the ja-JP culture:
// Default Calendar: GregorianCalendar (Localized)
// Optional Calendars:
// GregorianCalendar (Localized)
// JapaneseCalendar
// GregorianCalendar (USEnglish)

O calendário em uso no momento por um objeto CultureInfo específico é definido pela


propriedade DateTimeFormatInfo.Calendar da cultura. O objeto DateTimeFormatInfo de
uma cultura é retornado pela propriedade CultureInfo.DateTimeFormat. Quando uma
cultura é criada, o valor padrão é o mesmo valor da propriedade CultureInfo.Calendar.
No entanto, você pode mudar o calendário atual da cultura para qualquer calendário
contido na matriz retornada pela propriedade CultureInfo.OptionalCalendars. Se você
tentar definir o calendário atual como um calendário que não está incluído no valor da
propriedade CultureInfo.OptionalCalendars, uma ArgumentException será gerada.

O exemplo a seguir altera o calendário usado pela cultura Árabe (Arábia Saudita). Ele
primeiro cria uma instância de um valor DateTime e o exibe usando a cultura atual –
que, nesse caso, é Inglês (Estados Unidos) – e o calendário atual da cultura (que, nesse
caso, é o calendário gregoriano). Em seguida, ele muda a cultura atual para Árabe
(Arábia Saudita) e exibe a data usando seu calendário padrão Um Al Qura. Finalmente,
ele chama o método CalendarExists para determinar se o calendário islâmico é
suportado pela cultura Árabe (Arábia Saudita). Como o calendário é suportado, ele
muda o calendário atual para Hijri e, novamente, exibe a data. Observe que, em cada
caso, a data é exibida usando o calendário atual da cultura atual.

C#

using System;
using System.Globalization;
using System.Threading;

public class Example


{
public static void Main()
{
DateTime date1 = new DateTime(2011, 6, 20);

DisplayCurrentInfo();
// Display the date using the current culture and calendar.
Console.WriteLine(date1.ToString("d"));
Console.WriteLine();

CultureInfo arSA = CultureInfo.CreateSpecificCulture("ar-SA");

// Change the current culture to Arabic (Saudi Arabia).


Thread.CurrentThread.CurrentCulture = arSA;
// Display date and information about the current culture.
DisplayCurrentInfo();
Console.WriteLine(date1.ToString("d"));
Console.WriteLine();

// Change the calendar to Hijri.


Calendar hijri = new HijriCalendar();
if (CalendarExists(arSA, hijri)) {
arSA.DateTimeFormat.Calendar = hijri;
// Display date and information about the current culture.
DisplayCurrentInfo();
Console.WriteLine(date1.ToString("d"));
}
}

private static void DisplayCurrentInfo()


{
Console.WriteLine("Current Culture: {0}",
CultureInfo.CurrentCulture.Name);
Console.WriteLine("Current Calendar: {0}",
DateTimeFormatInfo.CurrentInfo.Calendar);
}

private static bool CalendarExists(CultureInfo culture, Calendar cal)


{
foreach (Calendar optionalCalendar in culture.OptionalCalendars)
if (cal.ToString().Equals(optionalCalendar.ToString()))
return true;

return false;
}
}
// The example displays the following output:
// Current Culture: en-US
// Current Calendar: System.Globalization.GregorianCalendar
// 6/20/2011
//
// Current Culture: ar-SA
// Current Calendar: System.Globalization.UmAlQuraCalendar
// 18/07/32
//
// Current Culture: ar-SA
// Current Calendar: System.Globalization.HijriCalendar
// 19/07/32

Datas e calendários
Com exceção dos construtores que incluem um parâmetro de tipo Calendar e permitem
que os elementos de uma data (ou seja, mês, dia e ano), reflitam valores em um
calendário designado, os valores de DateTime e DateTimeOffset sempre são baseados
no calendário gregoriano. Isso significa, por exemplo, que a propriedade DateTime.Year
retorna o ano no calendário gregoriano e que a propriedade DateTime.Day retorna o
dia do mês no calendário gregoriano.
) Importante

É importante lembrar que há uma diferença entre um valor de data e sua


representação de cadeia de caracteres. O primeiro baseia-se no calendário
gregoriano; o último baseia-se no calendário atual de uma determinada cultura.

O exemplo a seguir ilustra a diferença entre as propriedades DateTime e seus métodos


Calendar correspondentes. No exemplo, a cultura atual é Árabe (Egito), e o calendário
atual é Um Al Qura. Um valor de DateTime é definido como o décimo quinto dia do
sétimo mês de 2011. Está claro que esse é interpretado como uma data gregoriana, pois
esses mesmos valores são retornados pelo método DateTime.ToString(String,
IFormatProvider) quando as convenções da cultura invariável são usadas. A
representação de cadeia de caracteres da data que é formatada usando as convenções
de cultura atual é 14/08/32, que é a data equivalente no calendário Um Al Qura. Em
seguida, membros de DateTime e Calendar são usados para retornar o dia, mês e o ano
do valor de DateTime. Em cada caso, os valores retornados pelos membros de DateTime
refletem os valores do calendário gregoriano, enquanto que os valores retornados pelos
membros de UmAlQuraCalendar refletem os valores do calendário Um Al Qura.

C#

using System;
using System.Globalization;
using System.Threading;

public class Example


{
public static void Main()
{
// Make Arabic (Egypt) the current culture
// and Umm al-Qura calendar the current calendar.
CultureInfo arEG = CultureInfo.CreateSpecificCulture("ar-EG");
Calendar cal = new UmAlQuraCalendar();
arEG.DateTimeFormat.Calendar = cal;
Thread.CurrentThread.CurrentCulture = arEG;

// Display information on current culture and calendar.


DisplayCurrentInfo();

// Instantiate a date object.


DateTime date1 = new DateTime(2011, 7, 15);

// Display the string representation of the date.


Console.WriteLine("Date: {0:d}", date1);
Console.WriteLine("Date in the Invariant Culture: {0}",
date1.ToString("d", CultureInfo.InvariantCulture));
Console.WriteLine();
// Compare DateTime properties and Calendar methods.
Console.WriteLine("DateTime.Month property: {0}", date1.Month);
Console.WriteLine("UmAlQura.GetMonth: {0}",
cal.GetMonth(date1));
Console.WriteLine();

Console.WriteLine("DateTime.Day property: {0}", date1.Day);


Console.WriteLine("UmAlQura.GetDayOfMonth: {0}",
cal.GetDayOfMonth(date1));
Console.WriteLine();

Console.WriteLine("DateTime.Year property: {0:D4}", date1.Year);


Console.WriteLine("UmAlQura.GetYear: {0}",
cal.GetYear(date1));
Console.WriteLine();
}

private static void DisplayCurrentInfo()


{
Console.WriteLine("Current Culture: {0}",
CultureInfo.CurrentCulture.Name);
Console.WriteLine("Current Calendar: {0}",
DateTimeFormatInfo.CurrentInfo.Calendar);
}
}
// The example displays the following output:
// Current Culture: ar-EG
// Current Calendar: System.Globalization.UmAlQuraCalendar
// Date: 14/08/32
// Date in the Invariant Culture: 07/15/2011
//
// DateTime.Month property: 7
// UmAlQura.GetMonth: 8
//
// DateTime.Day property: 15
// UmAlQura.GetDayOfMonth: 14
//
// DateTime.Year property: 2011
// UmAlQura.GetYear: 1432

Criar uma instância de datas com base em um calendário


Como os valores DateTime e DateTimeOffset baseiam-se no calendário gregoriano,
você deve chamar um construtor sobrecarregado que inclua um parâmetro do tipo
Calendar para criar uma instância de um valor de data se você quiser usar os valores de
dia, mês ou ano em um calendário diferente. Você também pode chamar uma das
sobrecargas do método Calendar.ToDateTime de um calendário específico para criar
uma instância de um objeto DateTime com base nos valores de um calendário
específico.
O exemplo a seguir cria uma instância de um valor de DateTime ao passar um objeto
HebrewCalendar para um construtor de DateTime e cria uma instância de um segundo
valor DateTime ao chamar o método HebrewCalendar.ToDateTime(Int32, Int32, Int32,
Int32, Int32, Int32, Int32, Int32). Como os dois valores são criados com valores idênticos
do calendário hebraico, a chamada ao método DateTime.Equals mostra que os dois
valores de DateTime são iguais.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
HebrewCalendar hc = new HebrewCalendar();

DateTime date1 = new DateTime(5771, 6, 1, hc);


DateTime date2 = hc.ToDateTime(5771, 6, 1, 0, 0, 0, 0);

Console.WriteLine("{0:d} (Gregorian) = {1:d2}/{2:d2}/{3:d4} ({4}):


{5}",
date1,
hc.GetMonth(date2),
hc.GetDayOfMonth(date2),
hc.GetYear(date2),
GetCalendarName(hc),
date1.Equals(date2));
}

private static string GetCalendarName(Calendar cal)


{
return cal.ToString().Replace("System.Globalization.", "").
Replace("Calendar", "");
}
}
// The example displays the following output:
// 2/5/2011 (Gregorian) = 06/01/5771 (Hebrew): True

Representar datas no calendário atual


Os métodos de formatação de data e hora sempre usam o calendário atual ao converter
datas em cadeias de caracteres. Isso significa que a representação de cadeia de
caracteres do ano, mês e dia do mês reflete o calendário atual, e não necessariamente o
calendário gregoriano.
O exemplo a seguir mostra como o calendário atual afeta a representação de cadeia de
caracteres de uma data. Ele muda a cultura atual de Chinês (Tradicional, Taiwan) e cria
uma instância de um valor de data. Ele então exibe o calendário e a data atuais, altera o
calendário atual para TaiwanCalendar e exibe o calendário e a data atuais mais uma vez.
Na primeira vez que a data é exibida, ela é representada como uma data no calendário
gregoriano. Na segunda vez que a data é exibida, ela é representada como uma data no
calendário de Taiwan.

C#

using System;
using System.Globalization;
using System.Threading;

public class Example


{
public static void Main()
{
// Change the current culture to zh-TW.
CultureInfo zhTW = CultureInfo.CreateSpecificCulture("zh-TW");
Thread.CurrentThread.CurrentCulture = zhTW;
// Define a date.
DateTime date1 = new DateTime(2011, 1, 16);

// Display the date using the default (Gregorian) calendar.


Console.WriteLine("Current calendar: {0}",
zhTW.DateTimeFormat.Calendar);
Console.WriteLine(date1.ToString("d"));

// Change the current calendar and display the date.


zhTW.DateTimeFormat.Calendar = new TaiwanCalendar();
Console.WriteLine("Current calendar: {0}",
zhTW.DateTimeFormat.Calendar);
Console.WriteLine(date1.ToString("d"));
}
}
// The example displays the following output:
// Current calendar: System.Globalization.GregorianCalendar
// 2011/1/16
// Current calendar: System.Globalization.TaiwanCalendar
// 100/1/16

Representar datas em um calendário diferente do atual


Para representar uma data usando um calendário que não é o calendário atual de uma
cultura específica, você deve chamar métodos do objeto Calendar. Por exemplo, os
métodos Calendar.GetYear, Calendar.GetMonth e Calendar.GetDayOfMonth convertem
o ano, o mês e o dia para valores que refletem um calendário específico.
2 Aviso

Como alguns calendários são calendários não opcionais de qualquer cultura,


representar datas nesses calendários sempre exige que você chame métodos de
calendário. Isso é verdadeiro para todos os calendários que derivam das classes
EastAsianLunisolarCalendar, de JulianCalendar e PersianCalendar.

O exemplo a seguir usa um objeto JulianCalendar para criar uma instância de uma data,
9 de janeiro de 1905, no calendário juliano. Quando essa data é exibida no calendário
gregoriano (padrão), ela é representada como 22 de janeiro de 1905. Chamadas para
métodos JulianCalendar individuais permitem que a data seja representada no
calendário juliano.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
JulianCalendar julian = new JulianCalendar();
DateTime date1 = new DateTime(1905, 1, 9, julian);

Console.WriteLine("Date ({0}): {1:d}",


CultureInfo.CurrentCulture.Calendar,
date1);
Console.WriteLine("Date in Julian calendar: {0:d2}/{1:d2}/{2:d4}",
julian.GetMonth(date1),
julian.GetDayOfMonth(date1),
julian.GetYear(date1));
}
}
// The example displays the following output:
// Date (System.Globalization.GregorianCalendar): 1/22/1905
// Date in Julian calendar: 01/09/1905

Calendários e intervalos de datas


A data mais antiga suportada por um calendário é indicada pela propriedade
Calendar.MinSupportedDateTime desse calendário. Para a classe GregorianCalendar,
essa data é 1º de janeiro de 0001. C.E. A maioria dos outros calendários no .NET dá
suporte a uma data posterior. Tentar trabalhar com um valor de data e hora que
antecedem a data com suporte mais antiga de um calendário gerará uma exceção
ArgumentOutOfRangeException.

Porém, há uma exceção importante. O valor padrão (não inicializado) de um objeto


DateTime e um objeto DateTimeOffset é igual ao valor de
GregorianCalendar.MinSupportedDateTime. Se você tentar formatar essa data em um
calendário que não ofereça suporte a 1º de janeiro de 0001. C.E e não fornecer um
especificador de formato, o método de formatação usará o especificador de formato “s”
(padrão de data/hora classificável) em vez do especificador de formato “G” (padrão
geral de data/hora). Como resultado, a operação de formatação não gerará uma
exceção ArgumentOutOfRangeException. Em vez disso, retornará uma data sem
suporte. Isso é ilustrado no exemplo a seguir, que exibe o valor de DateTime.MinValue
quando a cultura atual é configurada para Japonês (Japão) com o calendário japonês, e
para Árabe (Egito) com o calendário Um Al Qura. Ela também define a cultura atual
como Inglês (Estados Unidos) e chama o método DateTime.ToString(IFormatProvider)
com cada um desses objetos CultureInfo. Em cada caso, a data é exibida usando o
padrão classificável de data/hora.

C#

using System;
using System.Globalization;
using System.Threading;

public class Example


{
public static void Main()
{
DateTime dat = DateTime.MinValue;

// Change the current culture to ja-JP with the Japanese Calendar.


CultureInfo jaJP = CultureInfo.CreateSpecificCulture("ja-JP");
jaJP.DateTimeFormat.Calendar = new JapaneseCalendar();
Thread.CurrentThread.CurrentCulture = jaJP;
Console.WriteLine("Earliest supported date by {1} calendar: {0:d}",
jaJP.DateTimeFormat.Calendar.MinSupportedDateTime,
GetCalendarName(jaJP));
// Attempt to display the date.
Console.WriteLine(dat.ToString());
Console.WriteLine();

// Change the current culture to ar-EG with the Um Al Qura calendar.


CultureInfo arEG = CultureInfo.CreateSpecificCulture("ar-EG");
arEG.DateTimeFormat.Calendar = new UmAlQuraCalendar();
Thread.CurrentThread.CurrentCulture = arEG;
Console.WriteLine("Earliest supported date by {1} calendar: {0:d}",
arEG.DateTimeFormat.Calendar.MinSupportedDateTime,
GetCalendarName(arEG));
// Attempt to display the date.
Console.WriteLine(dat.ToString());
Console.WriteLine();

// Change the current culture to en-US.


Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-US");
Console.WriteLine(dat.ToString(jaJP));
Console.WriteLine(dat.ToString(arEG));
Console.WriteLine(dat.ToString("d"));
}

private static string GetCalendarName(CultureInfo culture)


{
Calendar cal = culture.DateTimeFormat.Calendar;
return cal.GetType().Name.Replace("System.Globalization.",
"").Replace("Calendar", "");
}
}
// The example displays the following output:
// Earliest supported date by Japanese calendar: 明治 1/9/8
// 0001-01-01T00:00:00
//
// Earliest supported date by UmAlQura calendar: 01/01/18
// 0001-01-01T00:00:00
//
// 0001-01-01T00:00:00
// 0001-01-01T00:00:00
// 1/1/0001

Trabalhar com eras


Os calendários normalmente dividem as datas em eras. No entanto, as classes Calendar
no .NET não oferecem suporte a cada era definida por um calendário, e a maioria das
classes Calendar oferecem suporte a uma única era. Somente as classes
JapaneseCalendar e JapaneseLunisolarCalendar oferecem suporte a várias eras.

) Importante

A era Reiwa, uma nove era no JapaneseCalendar e no JapaneseLunisolarCalendar


começa em 1º de maio de 2019. Essa alteração afeta todos os aplicativos que usam
esses calendários. Confira os artigos a seguir para saber mais:

Tratamento de uma nova era no calendário japonês no .NET , que


documenta recursos adicionados ao .NET para dar suporte a calendários com
várias eras e discute as práticas recomendadas a serem usadas ao tratar
calendários de várias eras.
Preparar seu aplicativo para a mudança de era no calendário japonês, que
fornece informações sobre como testar seus aplicativos no Windows para
assegurar que eles estejam preparados para a alteração de era.
Resumo das novas atualizações da era japonesa no .NET Framework , que
lista as atualizações no .NET Framework para versões individuais do Windows
relacionadas à nova era do calendário japonês, observa novos recursos do
.NET Framework para suporte de várias eras e inclui itens a serem buscados
no teste de seus aplicativos.

Uma era na maioria dos calendários indica um período de tempo extremamente longo.
No calendário gregoriano, por exemplo, a era atual abrange mais de dois milênios. Isso
não ocorre em JapaneseCalendar e JapaneseLunisolarCalendar, os dois calendários que
dão suporte a várias eras. Uma era corresponde ao período do reinado de um
imperador. O suporte a várias eras, especialmente quando o limite superior da era atual
é desconhecido, apresenta desafios especiais.

Eras e nomes de eras


No .NET, inteiros que representam as eras com suporte em uma implementação
específica do calendário são armazenadas em ordem inversa na matriz Calendar.Eras. A
era atual (que é a era com o intervalo de tempo mais recente) está no índice zero. Para
classes Calendar que oferecem suporte a várias eras, cada índice sucessivo reflete a era
anterior. A propriedade estática Calendar.CurrentEra define o índice de era atual na
matriz Calendar.Eras ; ela é uma constante cujo valor é sempre zero. As classes Calendar
individuais também incluem os campos estáticos que retornam o valor da era atual. Elas
são listadas na tabela a seguir.

Classe do calendário Campo de era atual

ChineseLunisolarCalendar ChineseEra

GregorianCalendar ADEra

HebrewCalendar HebrewEra

HijriCalendar HijriEra

JapaneseLunisolarCalendar JapaneseEra

JulianCalendar JulianEra

KoreanCalendar KoreanEra
Classe do calendário Campo de era atual

KoreanLunisolarCalendar GregorianEra

PersianCalendar PersianEra

ThaiBuddhistCalendar ThaiBuddhistEra

UmAlQuraCalendar UmAlQuraEra

O nome que corresponde a um número de era específico não pode ser recuperado com
a passagem do número da era para o método DateTimeFormatInfo.GetEraName ou
DateTimeFormatInfo.GetAbbreviatedEraName. O exemplo a seguir chama esses
métodos para recuperar informações sobre o suporte a eras na classe
GregorianCalendar. Exibe a data do calendário gregoriano que corresponde a 1º de
janeiro do segundo ano da era atual, bem como a data do calendário gregoriano que
corresponde a 1º de janeiro do segundo ano de cada era do calendário japonês com
suporte.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
int year = 2;
int month = 1;
int day = 1;
Calendar cal = new JapaneseCalendar();

Console.WriteLine("\nDate instantiated without an era:");


DateTime date1 = new DateTime(year, month, day, 0, 0, 0, 0, cal);
Console.WriteLine("{0}/{1}/{2} in Japanese Calendar -> {3:d} in
Gregorian",
cal.GetMonth(date1), cal.GetDayOfMonth(date1),
cal.GetYear(date1), date1);

Console.WriteLine("\nDates instantiated with eras:");


foreach (int era in cal.Eras) {
DateTime date2 = cal.ToDateTime(year, month, day, 0, 0, 0, 0, era);
Console.WriteLine("{0}/{1}/{2} era {3} in Japanese Calendar ->
{4:d} in Gregorian",
cal.GetMonth(date2), cal.GetDayOfMonth(date2),
cal.GetYear(date2), cal.GetEra(date2), date2);
}
}
}
Além disso, a cadeia de caracteres de formato de data e hora personalizado "g" inclui o
nome da era na representação de cadeia de caracteres de uma data e hora. Para obter
mais informações, consulte Cadeias de caracteres personalizadas de formato data e
hora.

Criar uma instância de uma data com uma era


Para as duas classes Calendar que oferecem suporte a várias eras, uma data que
consiste em um determinado valor de ano, mês, dia e dia do mês pode ser ambígua. Por
exemplo, todas as quatro eras do JapaneseCalendar têm os anos numerados como 1.
Normalmente, se uma era não é especificada, os métodos de data e hora e calendário
assumem que os valores pertencem à era atual. Isso se aplica aos construtores DateTime
e DateTimeOffset que incluem parâmetros do tipo Calendar, bem como aos métodos
JapaneseCalendar.ToDateTime e JapaneseLunisolarCalendar.ToDateTime. O exemplo a
seguir cria uma data que representa 1º de janeiro do segundo ano de uma era não
especificada. Se você executar o exemplo quando a era Reiwa for a era atual, a data será
interpretada como o segundo ano da era Reiwa. A era, 令和, precede o ano na cadeia de
caracteres retornada pelo método DateTime.ToString(String, IFormatProvider) e
corresponde a 1º de janeiro de 2020, no calendário gregoriano. (A era Reiwa começa no
ano de 2019 do calendário gregoriano.)

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
var japaneseCal = new JapaneseCalendar();
var jaJp = new CultureInfo("ja-JP");
jaJp.DateTimeFormat.Calendar = japaneseCal;

var date = new DateTime(2, 1, 1, japaneseCal);


Console.WriteLine($"Gregorian calendar date: {date:d}");
Console.WriteLine($"Japanese calendar date: {date.ToString("d",
jaJp)}");
}
}

No entanto, se a era for alterada, a intenção desse código se tornará ambígua. A data
destina-se a representar o segundo ano da era atual ou o segundo ano da era Heisei?
Há duas formas de evitar essa ambiguidade:
Crie uma instância do valor de data e hora usando a classe padrão
GregorianCalendar. Em seguida, você pode usar o calendário japonês ou o
calendário lunissolar japonês para representação da cadeia de caracteres de datas,
como mostra o exemplo a seguir.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
var japaneseCal = new JapaneseCalendar();
var jaJp = new CultureInfo("ja-JP");
jaJp.DateTimeFormat.Calendar = japaneseCal;

var date = new DateTime(1905, 2, 12);


Console.WriteLine($"Gregorian calendar date: {date:d}");

// Call the ToString(IFormatProvider) method.


Console.WriteLine($"Japanese calendar date: {date.ToString("d",
jaJp)}");

// Use a FormattableString object.


FormattableString fmt = $"{date:d}";
Console.WriteLine($"Japanese calendar date:
{fmt.ToString(jaJp)}");

// Use the JapaneseCalendar object.


Console.WriteLine($"Japanese calendar date:
{jaJp.DateTimeFormat.GetEraName(japaneseCal.GetEra(date))}" +
$"
{japaneseCal.GetYear(date)}/{japaneseCal.GetMonth(date)}/{japaneseCal.G
etDayOfMonth(date)}");

// Use the current culture.


CultureInfo.CurrentCulture = jaJp;
Console.WriteLine($"Japanese calendar date: {date:d}");
}
}
// The example displays the following output:
// Gregorian calendar date: 2/12/1905
// Japanese calendar date: 明治38/2/12
// Japanese calendar date: 明治38/2/12
// Japanese calendar date: 明治38/2/12
// Japanese calendar date: 明治38/2/12

Chame um método de data e hora que especifique explicitamente uma era. Entre
elas estão os seguintes métodos:
O método ToDateTime(Int32, Int32, Int32, Int32, Int32, Int32, Int32, Int32) da
classe JapaneseCalendar ou JapaneseLunisolarCalendar.

Um método de análise DateTime ou DateTimeOffset, como Parse, TryParse,


ParseExact ou TryParseExact, que inclui a cadeia de caracteres a ser analisada e,
opcionalmente, um argumento DateTimeStyles se a cultura atual for Japonesa -
Japão ("ja-JP") e o calendário dessa cultura é JapaneseCalendar. A cadeia de
caracteres a ser analisada deve incluir a era.

Um método de análise DateTime ou DateTimeOffset que inclui um parâmetro


provider do tipo IFormatProvider. provider deve ser um objeto CultureInfo

que representa a cultura Japonesa - Japão ("ja-JP") cujo calendário atual é


JapaneseCalendar ou um objeto DateTimeFormatInfo cuja propriedade
Calendar é JapaneseCalendar. A cadeia de caracteres a ser analisada deve incluir
a era.

O exemplo a seguir usa três desses métodos para criar uma instância de data e
hora na era Meiji, que começou em 8 de setembro de 1868 e terminou em 29 de
julho de 1912.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
var japaneseCal = new JapaneseCalendar();
var jaJp = new CultureInfo("ja-JP");
jaJp.DateTimeFormat.Calendar = japaneseCal;

// We can get the era index by calling


DateTimeFormatInfo.GetEraName.
int eraIndex = 0;

for (int ctr = 0; ctr <


jaJp.DateTimeFormat.Calendar.Eras.Length; ctr++)
if (jaJp.DateTimeFormat.GetEraName(ctr) == "明治")
eraIndex = ctr;
var date1 = japaneseCal.ToDateTime(23, 9, 8, 0, 0, 0, 0,
eraIndex);
Console.WriteLine($"{date1.ToString("d", jaJp)} (Gregorian
{date1:d})");

try {
var date2 = DateTime.Parse("明治23/9/8", jaJp);
Console.WriteLine($"{date2.ToString("d", jaJp)} (Gregorian
{date2:d})");
}
catch (FormatException)
{
Console.WriteLine("The parsing operation failed.");
}

try {
var date3 = DateTime.ParseExact("明治23/9/8", "gyy/M/d",
jaJp);
Console.WriteLine($"{date3.ToString("d", jaJp)} (Gregorian
{date3:d})");
}
catch (FormatException)
{
Console.WriteLine("The parsing operation failed.");
}
}
}
// The example displays the following output:
// 明治23/9/8 (Gregorian 9/8/1890)
// 明治23/9/8 (Gregorian 9/8/1890)
// 明治23/9/8 (Gregorian 9/8/1890)

 Dica

Ao trabalhar com calendários que dão suporte a várias eras, sempre use a data
gregoriana para criar uma instância de data ou especifique a era ao criar uma
instância de data e hora com base nesse calendário.

Ao especificar uma era para o método ToDateTime(Int32, Int32, Int32, Int32, Int32, Int32,
Int32, Int32), você fornece o índice da era na propriedade Eras do calendário. Para
calendários cujas eras estão sujeitas a alterações, no entanto, esses índices não são
valores constantes; a era atual está no índice 0 e a era mais antiga está no índice
Eras.Length - 1 . Quando uma nova era é adicionada a um calendário, os índices das

eras anteriores aumentam em um. É possível fornecer o índice de era apropriado da


seguinte maneira:

Para datas na era atual, sempre use a propriedade CurrentEra do calendário.

Para datas em uma era especificada, use o método


DateTimeFormatInfo.GetEraName para recuperar o índice que corresponde a um
nome de era especificado. Isso requer que JapaneseCalendar seja o calendário
atual do objeto CultureInfo que representa a cultura ja-JP. (Essa técnica também
funciona para JapaneseLunisolarCalendar, pois dá suporte às mesmas eras que
JapaneseCalendar.) O exemplo anterior ilustra essa abordagem.
Calendários, eras e intervalos de datas: verificações de
intervalo reduzidas
Assim como os calendários individuais têm suporte para intervalos de datas, as eras nas
classes JapaneseCalendar e JapaneseLunisolarCalendar também têm intervalos com
suporte. Anteriormente, o .NET usava verificações estritas de intervalo de era para
garantir que uma data específica da era estivesse no intervalo daquela época. Ou seja,
se uma data estiver fora do intervalo da era especificada, o método gerará um
ArgumentOutOfRangeException. Atualmente, o .NET usa a verificação de intervalo
reduzida por padrão. As atualizações a todas as versões do .NET introduziram
verificações reduzidas de intervalo de era; a tentativa de criar uma instância de data
específica da era que está fora do intervalo da era especificada "estoura" para a era
seguinte e nenhuma exceção é lançada.

O exemplo a seguir tenta instanciar uma data no 65º ano da era Showa, que começou
em 25 de dezembro de 1926 e terminou em 7 de janeiro de 1989. Essa data
corresponde a 9 de janeiro de 1990, que está fora do intervalo da era Showa no
JapaneseCalendar. Como ilustrado pela saída do exemplo, a data exibida pelo exemplo
é 9 de janeiro de 1990, no segundo ano da era Heisei.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
var jaJp = new CultureInfo("ja-JP");
var cal = new JapaneseCalendar();
jaJp.DateTimeFormat.Calendar = cal;
string showaEra = "昭和";

var dt = cal.ToDateTime(65, 1, 9, 15, 0, 0, 0, GetEraIndex(showaEra));


FormattableString fmt = $"{dt:d}";

Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)}");


Console.WriteLine($"Gregorian calendar date: {fmt}");

int GetEraIndex(string eraName)


{
foreach (var ctr in cal.Eras)
if (jaJp.DateTimeFormat.GetEraName(ctr) == eraName)
return ctr;

return 0;
}
}
}
// The example displays the following output:
// Japanese calendar date: 平成2/1/9
// Gregorian calendar date: 1/9/1990

Se as verificações de intervalo reduzidas forem indesejáveis, você poderá restaurar as


verificações de intervalo estritas de várias maneiras, dependendo da versão do .NET na
qual seu aplicativo está em execução:

.NET Core: adicione o seguinte ao arquivo de configuração .netcore.runtime.json:

JSON

"runtimeOptions": {
"configProperties": {
"Switch.System.Globalization.EnforceJapaneseEraYearRanges": true
}
}

.NET Framework 4.6 ou posterior: defina a seguinte opção AppContext no arquivo


app.config:

XML

<?xml version="1.0" encoding="utf-8"?>


<configuration>
<runtime>
<AppContextSwitchOverrides
value="Switch.System.Globalization.EnforceJapaneseEraYearRanges=true"
/>
</runtime>
</configuration>

.NET Framework 4.5.2 ou anterior: defina o seguinte valor de registro:

Valor

Chave HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AppContext

Entry Switch.System.Globalization.EnforceJapaneseEraYearRanges

Tipo REG_SZ

Valor true

Com as verificações de intervalo estritas habilitadas, o exemplo anterior gera um


ArgumentOutOfRangeException e exibe a seguinte saída:
Console

Unhandled Exception: System.ArgumentOutOfRangeException: Valid values are


between 1 and 64, inclusive.
Parameter name: year
at System.Globalization.GregorianCalendarHelper.GetYearOffset(Int32 year,
Int32 era, Boolean throwOnError)
at System.Globalization.GregorianCalendarHelper.ToDateTime(Int32 year,
Int32 month, Int32 day, Int32 hour, Int32 minute, Int32 second, Int32
millisecond, Int32 era)
at Example.Main()

Representar datas em calendários com várias eras


Se um objeto Calendar oferece suporte a eras e é o calendário atual de um objeto
CultureInfo, a era está incluída na representação de cadeia de caracteres de um valor de
data e hora para os padrões de data e hora completa, data completa e data abreviada.
O exemplo a seguir exibe esses padrões de data quando a cultura atual é Japão
(japanese) e o calendário atual é o calendário japonês.

C#

using System;
using System.Globalization;
using System.IO;
using System.Threading;

public class Example


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\eras.txt");
DateTime dt = new DateTime(2012, 5, 1);

CultureInfo culture = CultureInfo.CreateSpecificCulture("ja-JP");


DateTimeFormatInfo dtfi = culture.DateTimeFormat;
dtfi.Calendar = new JapaneseCalendar();
Thread.CurrentThread.CurrentCulture = culture;

sw.WriteLine("\n{0,-43} {1}", "Full Date and Time Pattern:",


dtfi.FullDateTimePattern);
sw.WriteLine(dt.ToString("F"));
sw.WriteLine();

sw.WriteLine("\n{0,-43} {1}", "Long Date Pattern:",


dtfi.LongDatePattern);
sw.WriteLine(dt.ToString("D"));

sw.WriteLine("\n{0,-43} {1}", "Short Date Pattern:",


dtfi.ShortDatePattern);
sw.WriteLine(dt.ToString("d"));
sw.Close();
}
}
// The example writes the following output to a file:
// Full Date and Time Pattern: gg y'年'M'月'd'日' H:mm:ss
// 平成 24年5月1日 0:00:00
//
// Long Date Pattern: gg y'年'M'月'd'日'
// 平成 24年5月1日
//
// Short Date Pattern: gg y/M/d
// 平成 24/5/1

2 Aviso

A classe JapaneseCalendar é a única classe de calendário no .NET que oferece


suporte a datas em mais de uma era e que pode ser o calendário atual de um
objeto CultureInfo – especificamente, um objeto CultureInfo que representa a
cultura Japonês (Japão).

Para todos os calendários, o especificador de formato personalizado “g” inclui a era na


cadeia de caracteres de resultado. O exemplo a seguir usa a cadeia de caracteres de
formato personalizado "MM-dd-aaaa g" para incluir a era na cadeia de caracteres de
resultado quando o calendário atual é o calendário gregoriano.

C#

DateTime dat = new DateTime(2012, 5, 1);


Console.WriteLine("{0:MM-dd-yyyy g}", dat);
// The example displays the following output:
// 05-01-2012 A.D.

Em casos em que a representação de cadeia de caracteres de uma data é expressa em


um calendário que não é o calendário atual, a classe Calendar inclui um método
Calendar.GetEra que pode ser usado junto com Calendar.GetYear, Calendar.GetMonth e
os métodos Calendar.GetDayOfMonth para indicar inequivocamente uma data e a era à
qual ela pertence. O exemplo a seguir usa a classe JapaneseLunisolarCalendar para
fornecer uma ilustração. No entanto, observe que incluir um nome ou abreviação
significativa em vez de um inteiro para a era na cadeia de caracteres de resultado exige
que você crie uma instância de um objeto DateTimeFormatInfo e faça de
JapaneseCalendar o calendário atual. (O calendário JapaneseLunisolarCalendar não
pode ser o calendário atual de qualquer cultura, mas, nesse caso, os dois calendários
compartilham as mesmas eras.)
C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
DateTime date1 = new DateTime(2011, 8, 28);
Calendar cal = new JapaneseLunisolarCalendar();

Console.WriteLine("{0} {1:d4}/{2:d2}/{3:d2}",
cal.GetEra(date1),
cal.GetYear(date1),
cal.GetMonth(date1),
cal.GetDayOfMonth(date1));

// Display eras
CultureInfo culture = CultureInfo.CreateSpecificCulture("ja-JP");
DateTimeFormatInfo dtfi = culture.DateTimeFormat;
dtfi.Calendar = new JapaneseCalendar();

Console.WriteLine("{0} {1:d4}/{2:d2}/{3:d2}",
dtfi.GetAbbreviatedEraName(cal.GetEra(date1)),
cal.GetYear(date1),
cal.GetMonth(date1),
cal.GetDayOfMonth(date1));
}
}
// The example displays the following output:
// 4 0023/07/29
// 平 0023/07/29

Nos calendários japoneses, o primeiro ano de uma era é chamado Gannen (元年). Por
exemplo, em vez de Heisei 1, o primeiro ano da era Heisei pode ser descrito como
Heisei Gannen. O .NET adota essa convenção em operações de formatação para datas e
horas formatadas com as seguintes cadeias de caracteres de formato de data e hora
padrão ou personalizadas quando são usadas com um objeto CultureInfo que
representa a cultura Japonesa-Japão ("ja-JP") com a classe JapaneseCalendar:

O padrão de data longa, indicado pela cadeia de caracteres de formato de data e


hora padrão "D".
O padrão de data e hora completo, indicado pela cadeia de caracteres de formato
de data e hora padrão "F".
O padrão de data e hora curto, indicado pela cadeia de caracteres de formato de
data e hora padrão "f".
O padrão ano/mês, indicado pela cadeia de caracteres de formato de data e hora
padrão "Y" ou "y".
A cadeia de caracteres de formato de data e hora personalizado "ggy'年'" ou "ggy
年".

Por exemplo, o exemplo a eguir exibe uma data no primeiro ano da era Heisei no
JapaneseCalendar.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
var enUs = new CultureInfo("en-US");
var japaneseCal = new JapaneseCalendar();
var jaJp = new CultureInfo("ja-JP");
jaJp.DateTimeFormat.Calendar = japaneseCal;
string heiseiEra = "平成";

var date = japaneseCal.ToDateTime(1, 8, 18, 0, 0, 0, 0,


GetEraIndex(heiseiEra));
FormattableString fmt = $"{date:D}";
Console.WriteLine($"Japanese calendar date: {fmt.ToString(jaJp)}
(Gregorian: {fmt.ToString(enUs)})");

int GetEraIndex(string eraName)


{
foreach (var ctr in japaneseCal.Eras)
if (jaJp.DateTimeFormat.GetEraName(ctr) == eraName)
return ctr;

return 0;
}
}
}
// The example displays the following output:
// Japanese calendar date: 平成元年8月18日 (Gregorian: Friday, August 18,
1989)

Se esse comportamento for indesejável em operações de formatação, você poderá


restaurar o comportamento anterior, que sempre representa o primeiro ano de uma era
como "1" em vez de "Gannen", fazendo o seguinte, dependendo da versão do .NET:

.NET Core: adicione o seguinte ao arquivo de configuração .netcore.runtime.json:

JSON

"runtimeOptions": {
"configProperties": {
"Switch.System.Globalization.FormatJapaneseFirstYearAsANumber":
true
}
}

.NET Framework 4.6 ou posterior: defina a seguinte opção AppContext no arquivo


app.config:

XML

<?xml version="1.0" encoding="utf-8"?>


<configuration>
<runtime>
<AppContextSwitchOverrides
value="Switch.System.Globalization.FormatJapaneseFirstYearAsANumber=tru
e" />
</runtime>
</configuration>

.NET Framework 4.5.2 ou anterior: defina o seguinte valor de registro:

Valor

Chave HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AppContext

Entry Switch.System.Globalization.FormatJapaneseFirstYearAsANumber

Tipo REG_SZ

Valor true

Com o suporte a Gannen em operações de formatação desabilitadas, o exemplo


anterior exibe a seguinte saída:

Console

Japanese calendar date: 平成1年8月18日 (Gregorian: Friday, August 18, 1989)

O .NET também foi atualizado para que as operações de análise de data e hora
ofereçam suporte a cadeias de caracteres que contêm o ano representado como "1" ou
Gannen. Embora você não precise fazer isso, você pode restaurar o comportamento
anterior para reconhecer apenas "1" como o primeiro ano de uma era. Você pode fazer
isso da seguinte maneira, dependendo da versão do .NET:

.NET Core: adicione o seguinte ao arquivo de configuração .netcore.runtime.json:

JSON
"runtimeOptions": {
"configProperties": {
"Switch.System.Globalization.EnforceLegacyJapaneseDateParsing":
true
}
}

.NET Framework 4.6 ou posterior: defina a seguinte opção AppContext no arquivo


app.config:

XML

<?xml version="1.0" encoding="utf-8"?>


<configuration>
<runtime>
<AppContextSwitchOverrides
value="Switch.System.Globalization.EnforceLegacyJapaneseDateParsing=tru
e" />
</runtime>
</configuration>

.NET Framework 4.5.2 ou anterior: defina o seguinte valor de registro:

Valor

Chave HKEY_LOCAL_MACHINE\Software\Microsoft\.NETFramework\AppContext

Entry Switch.System.Globalization.EnforceLegacyJapaneseDateParsing

Tipo REG_SZ

Valor true

Confira também
Como exibir datas em calendários não gregorianos
Classe do calendário

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our  Provide product feedback
contributor guide.
Como usar as estruturas DateOnly e
TimeOnly
Artigo • 05/06/2023

As estruturas DateOnly e TimeOnly foram introduzidas com o .NET 6 e representam uma


data ou hora específica do dia, respectivamente. Antes do .NET 6 e sempre no .NET
Framework, os desenvolvedores usavam o tipo DateTime (ou alguma outra alternativa)
para representar um dos seguintes:

Uma data e hora inteiras.


Uma data, desconsiderando a hora.
Uma hora, desconsiderando a data.

DateOnly e TimeOnly são tipos que representam essas partes específicas de um tipo
DateTime .

) Importante

Os tipos DateOnly e TimeOnly não estão disponíveis no .NET Framework.

A estrutura DateOnly
A estrutura DateOnly representa uma data específica, sem a hora. Como não tem
nenhum componente de hora, ela representa uma data do início do dia até o final do
dia. Essa estrutura é ideal para armazenar datas específicas, como uma data de
nascimento, uma data de aniversário ou datas relacionadas a negócios.

Embora você possa usar DateTime enquanto ignora o componente de hora, há alguns
benefícios em usar DateOnly em vez de DateTime :

A estrutura DateTime poderá ser revertida para o dia anterior ou seguinte se for
deslocada por um fuso horário. DateOnly não pode ser deslocado por um fuso
horário e sempre representa a data que foi definida.

Serializar uma estrutura DateTime inclui o componente de hora, que pode


obscurecer a intenção dos dados. Além disso, DateOnly serializa menos dados.

Quando o código interage com um banco de dados, como o SQL Server, datas
inteiras geralmente são armazenadas como o tipo de dados date , que não inclui
uma hora. DateOnly corresponde melhor ao tipo de banco de dados.

DateOnly tem um intervalo de 0001-01-01 a 9999-12-31, assim como DateTime . Você

pode especificar um calendário específico no construtor DateOnly . No entanto, um


objeto DateOnly sempre representa uma data no calendário gregoriano proléptico,
independentemente de qual calendário foi usado para construí-lo. Por exemplo, você
pode criar a data de um calendário hebraico, mas a data é convertida em gregoriano:

C#

var hebrewCalendar = new System.Globalization.HebrewCalendar();


var theDate = new DateOnly(5776, 2, 8, hebrewCalendar); // 8 Cheshvan 5776

Console.WriteLine(theDate);

/* This example produces the following output:


*
* 10/21/2015
*/

Exemplos de DateOnly
Use os seguintes exemplos para saber mais sobre DateOnly :

Converter DateTime em DateOnly


Adicionar ou subtrair dias, meses, anos
Analisar e formatar DateOnly
Comparar DateOnly

Converter DateTime em DateOnly


Use o método estático DateOnly.FromDateTime para criar um tipo DateOnly de um
DateTime tipo, conforme demonstrado no seguinte código:

C#

var today = DateOnly.FromDateTime(DateTime.Now);


Console.WriteLine($"Today is {today}");

/* This example produces output similar to the following:


*
* Today is 12/28/2022
*/
Adicionar ou subtrair dias, meses, anos
Três métodos são usados para ajustar uma DateOnly estrutura: AddDays, AddMonths e
AddYears. Cada método usa um parâmetro inteiro e aumenta a data por essa medida.
Se um número negativo for fornecido, a data será reduzida por essa medida. Os
métodos retornam uma nova instância de DateOnly , pois a estrutura é imutável.

C#

var theDate = new DateOnly(2015, 10, 21);

var nextDay = theDate.AddDays(1);


var previousDay = theDate.AddDays(-1);
var decadeLater = theDate.AddYears(10);
var lastMonth = theDate.AddMonths(-1);

Console.WriteLine($"Date: {theDate}");
Console.WriteLine($" Next day: {nextDay}");
Console.WriteLine($" Previous day: {previousDay}");
Console.WriteLine($" Decade later: {decadeLater}");
Console.WriteLine($" Last month: {lastMonth}");

/* This example produces the following output:


*
* Date: 10/21/2015
* Next day: 10/22/2015
* Previous day: 10/20/2015
* Decade later: 10/21/2025
* Last month: 9/21/2015
*/

Analisar e formatar DateOnly


DateOnly pode ser analisado de uma cadeia de caracteres, assim como a estrutura
DateTime. Todos os tokens de análise baseados em data do .NET padrão funcionam
com DateOnly . Ao converter um tipo DateOnly em uma cadeia de caracteres, você
também pode usar padrões de formatação baseados em data do .NET. Para obter mais
informações sobre cadeias de caracteres de formatação, confira Cadeias de caracteres
de formato de data e hora padrão.

C#

var theDate = DateOnly.ParseExact("21 Oct 2015", "dd MMM yyyy",


CultureInfo.InvariantCulture); // Custom format
var theDate2 = DateOnly.Parse("October 21, 2015",
CultureInfo.InvariantCulture);

Console.WriteLine(theDate.ToString("m", CultureInfo.InvariantCulture));
// Month day pattern
Console.WriteLine(theDate2.ToString("o", CultureInfo.InvariantCulture));
// ISO 8601 format
Console.WriteLine(theDate2.ToLongDateString());

/* This example produces the following output:


*
* October 21
* 2015-10-21
* Wednesday, October 21, 2015
*/

Comparar DateOnly
DateOnly pode ser comparado com outras instâncias. Por exemplo, você pode marcar se
uma data é antes ou depois de outra ou se uma data hoje corresponde a uma data
específica.

C#

var theDate = DateOnly.ParseExact("21 Oct 2015", "dd MMM yyyy",


CultureInfo.InvariantCulture); // Custom format
var theDate2 = DateOnly.Parse("October 21, 2015",
CultureInfo.InvariantCulture);
var dateLater = theDate.AddMonths(6);
var dateBefore = theDate.AddDays(-10);

Console.WriteLine($"Consider {theDate}...");
Console.WriteLine($" Is '{nameof(theDate2)}' equal? {theDate == theDate2}");
Console.WriteLine($" Is {dateLater} after? {dateLater > theDate} ");
Console.WriteLine($" Is {dateLater} before? {dateLater < theDate} ");
Console.WriteLine($" Is {dateBefore} after? {dateBefore > theDate} ");
Console.WriteLine($" Is {dateBefore} before? {dateBefore < theDate} ");

/* This example produces the following output:


*
* Consider 10/21/2015
* Is 'theDate2' equal? True
* Is 4/21/2016 after? True
* Is 4/21/2016 before? False
* Is 10/11/2015 after? False
* Is 10/11/2015 before? True
*/

A estrutura TimeOnly
A estrutura TimeOnly representa um valor de hora do dia, como um despertador diário
ou a hora em que você almoça todos os dias. TimeOnly é limitado ao intervalo de
00:00:00.0000000 - 23:59:59.9999999, uma hora específica do dia.

Antes do tipo TimeOnly ser introduzido, os programadores normalmente usavam o tipo


DateTime ou o tipo TimeSpan para representar uma hora específica. No entanto, usar
essas estruturas para simular uma hora sem uma data pode introduzir alguns
problemas, o que TimeOnly resolve:

TimeSpan representa o tempo decorrido, como o tempo medido com um

cronômetro. O intervalo superior é de mais de 29.000 anos, e seu valor pode ser
negativo para indicar a movimentação para trás no tempo. Um TimeSpan negativo
não indica uma hora específica do dia.

Se TimeSpan for usado como uma hora do dia, há o risco de que possa ser
manipulado para um valor fora do dia de 24 horas. TimeOnly não tem esse risco.
Por exemplo, se o turno de trabalho de um funcionário começar às 18:00 e durar 8
horas, adicionar 8 horas à estrutura TimeOnly será revertido para 2:00

Usar DateTime para uma hora do dia requer que uma data arbitrária seja associada
à hora e, em seguida, seja desconsiderada. É uma prática comum escolher
DateTime.MinValue (0001-01-01) como a data; no entanto, se horas forem

subtraídas do valor DateTime , uma exceção OutOfRange poderá ocorrer. TimeOnly


não tem esse problema, pois o tempo decorre para frente e para trás em torno do
período de 24 horas.

Serializar uma estrutura DateTime inclui o componente de data, que pode


obscurecer a intenção dos dados. Além disso, TimeOnly serializa menos dados.

Exemplos de TimeOnly
Use os seguintes exemplos para saber mais sobre TimeOnly :

Converter DateTime em TimeOnly


Adicionar ou subtrair a hora
Analisar e formatar TimeOnly
Trabalhar com TimeSpan e DateTime
Operadores aritméticos e comparação de TimeOnly

Converter DateTime em TimeOnly


Use o método estático TimeOnly.FromDateTime para criar um tipo TimeOnly de um
DateTime tipo, conforme demonstrado no seguinte código:
C#

var now = TimeOnly.FromDateTime(DateTime.Now);


Console.WriteLine($"It is {now} right now");

/* This example produces output similar to the following:


*
* It is 2:01 PM right now
*/

Adicionar ou subtrair a hora


Três métodos são usados para ajustar uma TimeOnly estrutura: AddHours, AddMinutes
e Add. AddHours e AddMinutes pegam um parâmetro inteiro e ajustam o valor
adequadamente. Você pode usar um valor negativo para subtrair e um valor positivo
para adicionar. Os métodos retornam uma nova instância de TimeOnly , pois a estrutura
é imutável. O método Add usa um parâmetro TimeSpan e adiciona ou subtrai o valor do
valor TimeOnly .

Como TimeOnly representa apenas um período de 24 horas, ele rola reverte frente ou
para trás adequadamente ao adicionar valores fornecidos a esses três métodos. Por
exemplo, se você usar um valor de 01:30:00 para representar 1h30, adicione -4 horas a
partir desse período e ele será revertido para 21:30:00 , que é 21h30. Há sobrecargas de
método para AddHours , AddMinutes e Add que capturam o número de dias revertidos.

C#

var theTime = new TimeOnly(7, 23, 11);

var hourLater = theTime.AddHours(1);


var minutesBefore = theTime.AddMinutes(-12);
var secondsAfter = theTime.Add(TimeSpan.FromSeconds(10));
var daysLater = theTime.Add(new TimeSpan(hours: 21, minutes: 200, seconds:
83), out int wrappedDays);
var daysBehind = theTime.AddHours(-222, out int wrappedDaysFromHours);

Console.WriteLine($"Time: {theTime}");
Console.WriteLine($" Hours later: {hourLater}");
Console.WriteLine($" Minutes before: {minutesBefore}");
Console.WriteLine($" Seconds after: {secondsAfter}");
Console.WriteLine($" {daysLater} is the time, which is {wrappedDays} days
later");
Console.WriteLine($" {daysBehind} is the time, which is
{wrappedDaysFromHours} days prior");

/* This example produces the following output:


*
* Time: 7:23 AM
* Hours later: 8:23 AM
* Minutes before: 7:11 AM
* Seconds after: 7:23 AM
* 7:44 AM is the time, which is 1 days later
* 1:23 AM is the time, which is -9 days prior
*/

Analisar e formatar TimeOnly


TimeOnly pode ser analisado de uma cadeia de caracteres, assim como a estrutura
DateTime. Todos os tokens de análise baseados em hora do .NET padrão funcionam
com TimeOnly . Ao converter um tipo TimeOnly em uma cadeia de caracteres, você
também pode usar padrões de formatação baseados em data do .NET. Para obter mais
informações sobre cadeias de caracteres de formatação, confira Cadeias de caracteres
de formato de data e hora padrão.

C#

var theTime = TimeOnly.ParseExact("5:00 pm", "h:mm tt",


CultureInfo.InvariantCulture); // Custom format
var theTime2 = TimeOnly.Parse("17:30:25", CultureInfo.InvariantCulture);

Console.WriteLine(theTime.ToString("o", CultureInfo.InvariantCulture));
// Round-trip pattern.
Console.WriteLine(theTime2.ToString("t", CultureInfo.InvariantCulture));
// Long time format
Console.WriteLine(theTime2.ToLongTimeString());

/* This example produces the following output:


*
* 17:00:00.0000000
* 17:30
* 5:30:25 PM
*/

Serializar os tipos DateOnly e TimeOnly


Com o .NET 7+, o System.Text.Json dá suporte à serialização e desserialização dos tipos
DateOnly e TimeOnly. Considere o seguinte objeto:

C#

sealed file record Appointment(


Guid Id,
string Description,
DateOnly Date,
TimeOnly StartTime,
TimeOnly EndTime);

O exemplo a seguir serializa um objeto Appointment , exibe o JSON resultante e, em


seguida, desserializa-o novamente em uma nova instância do tipo Appointment . Por fim,
as instâncias originais e recém-desserializadas são comparadas quanto à igualdade e os
resultados são gravados no console:

C#

Appointment originalAppointment = new(


Id: Guid.NewGuid(),
Description: "Take dog to veterinarian.",
Date: new DateOnly(2002, 1, 13),
StartTime: new TimeOnly(5,15),
EndTime: new TimeOnly(5, 45));
string serialized = JsonSerializer.Serialize(originalAppointment);

Console.WriteLine($"Resulting JSON: {serialized}");

Appointment deserializedAppointment =
JsonSerializer.Deserialize<Appointment>(serialized)!;

bool valuesAreTheSame = originalAppointment == deserializedAppointment;


Console.WriteLine($"""
Original record has the same values as the deserialized record:
{valuesAreTheSame}
""");

No código anterior:

Um objeto Appointment é instanciado e atribuído à variável appointment .


A instância appointment é serializada para JSON usando JsonSerializer.Serialize.
O JSON resultante é gravado no console.
O JSON é desserializado novamente em uma nova instância do tipo Appointment
usando JsonSerializer.Deserialize.
As instâncias originais e recém-desserializadas são comparadas quanto à
igualdade.
O resultado da comparação é gravado no console.

Para obter mais informações, consulte Como serializar e desserializar o JSON no .NET.

Trabalhar com TimeSpan e DateTime


TimeOnly pode ser criado de e convertido em um TimeSpan. Além disso, TimeOnly pode
ser usado com um DateTime, seja para criar a instância TimeOnly ou para criar uma
instância DateTime , desde que uma data seja fornecida.

O seguinte exemplo cria um objeto TimeOnly de um TimeSpan e o converte novamente:

C#

// TimeSpan must in the range of 00:00:00.0000000 to 23:59:59.9999999


var theTime = TimeOnly.FromTimeSpan(new TimeSpan(23, 59, 59));
var theTimeSpan = theTime.ToTimeSpan();

Console.WriteLine($"Variable '{nameof(theTime)}' is {theTime}");


Console.WriteLine($"Variable '{nameof(theTimeSpan)}' is {theTimeSpan}");

/* This example produces the following output:


*
* Variable 'theTime' is 11:59 PM
* Variable 'theTimeSpan' is 23:59:59
*/

O seguinte exemplo cria um DateTime de um objeto TimeOnly , com uma data arbitrária
escolhida:

C#

var theTime = new TimeOnly(11, 25, 46); // 11:25 PM and 46 seconds


var theDate = new DateOnly(2015, 10, 21); // October 21, 2015
var theDateTime = theDate.ToDateTime(theTime);
var reverseTime = TimeOnly.FromDateTime(theDateTime);

Console.WriteLine($"Date only is {theDate}");


Console.WriteLine($"Time only is {theTime}");
Console.WriteLine();
Console.WriteLine($"Combined to a DateTime type, the value is
{theDateTime}");
Console.WriteLine($"Converted back from DateTime, the time is
{reverseTime}");

/* This example produces the following output:


*
* Date only is 10/21/2015
* Time only is 11:25 AM
*
* Combined to a DateTime type, the value is 10/21/2015 11:25:46 AM
* Converted back from DateTime, the time is 11:25 AM
*/

Operadores aritméticos e comparação de TimeOnly


Duas instâncias TimeOnly podem ser comparadas umas com as outras e você pode usar
o método IsBetween para marcar se uma hora está entre duas outras horas. Quando um
operador de adição ou subtração é usado em um TimeOnly , um TimeSpan é retornado,
representando uma duração de tempo.

C#

var start = new TimeOnly(10, 12, 01); // 10:12:01 AM


var end = new TimeOnly(14, 00, 53); // 02:00:53 PM

var outside = start.AddMinutes(-3);


var inside = start.AddMinutes(120);

Console.WriteLine($"Time starts at {start} and ends at {end}");


Console.WriteLine($" Is {outside} between the start and end?
{outside.IsBetween(start, end)}");
Console.WriteLine($" Is {inside} between the start and end?
{inside.IsBetween(start, end)}");
Console.WriteLine($" Is {start} less than {end}? {start < end}");
Console.WriteLine($" Is {start} greater than {end}? {start > end}");
Console.WriteLine($" Does {start} equal {end}? {start == end}");
Console.WriteLine($" The time between {start} and {end} is {end - start}");

/* This example produces the following output:


*
* Time starts at 10:12 AM and ends at 2:00 PM
* Is 10:09 AM between the start and end? False
* Is 12:12 PM between the start and end? True
* Is 10:12 AM less than 2:00 PM? True
* Is 10:12 AM greater than 2:00 PM? False
* Does 10:12 AM equal 2:00 PM? False
* The time between 10:12 AM and 2:00 PM is 03:48:52
*/

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Executando operações aritméticas com
datas e horários
Artigo • 07/04/2023

Embora as estruturas DateTime e DateTimeOffset forneçam membros que realizam


operações aritméticas em seus valores, os resultados das operações aritméticas são
muito diferentes. Este artigo examina tais diferenças, as relaciona a graus de
reconhecimento de fuso horário em dados de data e hora e discute como executar
totalmente operações de reconhecimento de fuso horário usando dados de data e hora.

Comparações e operações aritméticas com


valores DateTime
A propriedade DateTime.Kind permite que um valor DateTimeKind seja atribuído à data
e à hora para indicar se elas representam a hora local, a UTC (Hora Universal
Coordenada) ou a hora em um fuso horário não especificado. No entanto, essas
informações limitadas de fuso horário são ignoradas ao comparar ou executar a
aritmética de data e hora em valores DateTimeKind. O exemplo a seguir, que compara a
hora local atual com a hora UTC atual, ilustra como as informações de fuso horário são
ignoradas.

C#

using System;

public enum TimeComparison


{
EarlierThan = -1,
TheSameAs = 0,
LaterThan = 1
}

public class DateManipulation


{
public static void Main()
{
DateTime localTime = DateTime.Now;
DateTime utcTime = DateTime.UtcNow;

Console.WriteLine("Difference between {0} and {1} time: {2}:{3}


hours",
localTime.Kind,
utcTime.Kind,
(localTime - utcTime).Hours,
(localTime - utcTime).Minutes);
Console.WriteLine("The {0} time is {1} the {2} time.",
localTime.Kind,
Enum.GetName(typeof(TimeComparison),
localTime.CompareTo(utcTime)),
utcTime.Kind);
}
}
// If run in the U.S. Pacific Standard Time zone, the example displays
// the following output to the console:
// Difference between Local and Utc time: -7:0 hours
// The Local time is EarlierThan the Utc time.

O método CompareTo(DateTime) relata que a hora local é anterior (ou menor) que a
hora UTC e a operação de subtração indica que a diferença entre a hora UTC e a hora
local para um sistema no fuso horário padrão do Pacífico dos EUA é de sete horas.
Porém, como esses dois valores oferecem representações diferentes de um único
momento, fica claro que, neste caso, o intervalo de tempo é completamente atribuível
ao deslocamento do fuso horário local em relação à hora UTC.

Normalmente, a propriedade DateTime.Kind não afeta os resultados retornado pelos


métodos aritméticos e de comparação Kind (como indica a comparação de dois
momentos idênticos), embora possa afetar a interpretação dos resultados. Por exemplo:

O resultado de qualquer operação aritmética executada em dois valores de data e


hora com as propriedades DateTime.Kind equivalentes a DateTimeKind reflete o
intervalo de tempo real entre os dois valores. Da mesma forma, a comparação dos
dois valores de data e hora reflete com precisão a relação entre horários.

O resultado de qualquer operação aritmética ou de comparação realizada em dois


valores de data e hora com as propriedades DateTime.Kind iguais a DateTimeKind
ou em dois valores de data e hora com valores de propriedade DateTime.Kind
diferentes reflete a diferença na hora do relógio entre os dois valores.

As operações aritméticas ou de comparação em valores de data e hora local não


consideram se um valor específico é ambíguo ou inválido nem levam em conta o
efeito de regras de ajuste que resultam da transição do fuso horário local de ou
para o horário de verão.

Qualquer operação que compara ou calcula a diferença entre o UTC e o horário


local inclui um intervalo de tempo igual ao deslocamento do fuso horário local em
relação ao UTC no resultado.

Qualquer operação que compara ou calcula a diferença entre um horário não


especificado e o UTC ou o horário local reflete a hora do relógio simples. As
diferenças de fuso horário não são consideradas; o resultado não reflete a
aplicação das regras de ajuste de fuso horário.

Qualquer operação que compara ou calcula a diferença entre dois horários não
especificados poderá incluir um intervalo desconhecido que reflete a diferença
entre o horário em dois fusos horários diferentes.

Há muitos cenários em que as diferenças de fuso horário não afetam os cálculos de data
e hora (para uma discussão de alguns desses cenários, veja Escolher entre DateTime,
DateTimeOffset, TimeSpan e TimeZoneInfo) ou em que o contexto dos dados de data e
hora define o significado de comparação ou operações aritméticas.

Comparações e operações aritméticas com


valores DateTimeOffset
Um valor DateTimeOffset inclui não somente uma data e hora, mas também um
deslocamento que define inequivocamente essa data e hora em relação à hora UTC.
Este deslocamento torna possível definir a igualdade de maneira diferente dos valores
DateTime. Enquanto os valores DateTime são iguais quando têm o mesmo valor de data
e hora, os valores DateTimeOffset são iguais quando ambos se referem ao mesmo
momento. Quando usado em comparações e na maioria das operações aritméticas que
determinam o intervalo entre duas datas e horas, um valor DateTimeOffset é mais
preciso e objetivo. O exemplo a seguir, que é o DateTimeOffset equivalente ao exemplo
anterior que comparou valores DateTimeOffset locais e UTC, ilustra essa diferença de
comportamento.

C#

using System;

public enum TimeComparison


{
EarlierThan = -1,
TheSameAs = 0,
LaterThan = 1
}

public class DateTimeOffsetManipulation


{
public static void Main()
{
DateTimeOffset localTime = DateTimeOffset.Now;
DateTimeOffset utcTime = DateTimeOffset.UtcNow;

Console.WriteLine("Difference between local time and UTC: {0}:{1:D2}


hours",
(localTime - utcTime).Hours,
(localTime - utcTime).Minutes);
Console.WriteLine("The local time is {0} UTC.",
Enum.GetName(typeof(TimeComparison),
localTime.CompareTo(utcTime)));
}
}
// Regardless of the local time zone, the example displays
// the following output to the console:
// Difference between local time and UTC: 0:00 hours.
// The local time is TheSameAs UTC.

Neste exemplo, o método CompareTo indica que a hora local atual e a hora UTC atual
são iguais, e a subtração de valores CompareTo(DateTimeOffset) indica que a diferença
entre as duas horas é TimeSpan.Zero.

A principal limitação do uso de valores DateTimeOffset na aritmética de data e hora é


que, embora os valores DateTimeOffset tenham algum reconhecimento de fuso horário,
eles não são totalmente conscientes do fuso horário. Embora o deslocamento do valor
DateTimeOffset reflita o deslocamento de um fuso horário de UTC quando uma variável
DateTimeOffset recebe um valor pela primeira vez, depois disso, ele fica desassociado
do fuso horário. Como não estão mais diretamente associadas a um horário
identificável, a adição e a subtração dos intervalos de data e hora não consideram as
regras de ajuste de um fuso horário.

Para ilustrar, a transição para o horário de verão no fuso horário padrão central dos EUA
ocorre às 2h em 9 de março de 2008. Com isso em mente, adicionar um intervalo de
duas horas e meia a um horário padrão central de 1h30 em 9 de março de 2008 deve
resultar em uma data/hora às 5h em 9 de março de 2008. No entanto, como mostra o
exemplo a seguir, o resultado da adição é 4h em 9 de março de 2008. O resultado dessa
operação representa o momento correto, embora não seja o horário no fuso horário em
que estamos interessados (ou seja, não tem o deslocamento de fuso horário esperado).

C#

using System;

public class IntervalArithmetic


{
public static void Main()
{
DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);
const string tzName = "Central Standard Time";
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

// Instantiate DateTimeOffset value to have correct CST offset


try
{
DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,

TimeZoneInfo.FindSystemTimeZoneById(tzName).GetUtcOffset(generalTime));

// Add two and a half hours


DateTimeOffset centralTime2 = centralTime1.Add(twoAndAHalfHours);
// Display result
Console.WriteLine("{0} + {1} hours = {2}", centralTime1,

twoAndAHalfHours.ToString(),
centralTime2);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to retrieve Central Standard Time zone
information.");
}
}
}
// The example displays the following output to the console:
// 3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 4:00:00 AM
-06:00

Operações aritméticas com horas em fusos


horários
A classe TimeZoneInfo inclui métodos de conversão que aplicam ajustes
automaticamente ao converter horários de um fuso horário para outro. Esses métodos
de conversão incluem:

Os métodos ConvertTime e ConvertTimeBySystemTimeZoneId, que convertem os


horários entre quaisquer dois fusos horários.

Os métodos ConvertTimeFromUtc e ConvertTimeToUtc, que convertem a hora em


um fuso horário específico para UTC ou vice-versa.

Para saber mais, veja Convertendo horários entre fusos horários.

A classe TimeZoneInfo não fornece nenhum método que aplique regras de ajuste
automaticamente ao executar a aritmética de data e hora. No entanto, é possível aplicar
regras de ajuste convertendo a hora em um fuso horário para UTC, executando a
operação aritmética e, em seguida, convertendo de UTC novamente para a hora no fuso
horário. Para saber mais, veja Como usar fusos horários na aritmética de data e hora.

Por exemplo, o código a seguir é semelhante ao código anterior que adicionou duas
horas e meia às 2h em 9 de março de 2008. No entanto, como converte um horário
padrão da região central em UTC antes de realizar a aritmética de data e hora e, depois,
converte o resultado do UTC novamente no horário padrão da região central, o horário
resultante reflete a transição do fuso horário padrão da região central para o horário de
verão.

C#

using System;

public class TimeZoneAwareArithmetic


{
public static void Main()
{
const string tzName = "Central Standard Time";

DateTime generalTime = new DateTime(2008, 3, 9, 1, 30, 0);


TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

// Instantiate DateTimeOffset value to have correct CST offset


try
{
DateTimeOffset centralTime1 = new DateTimeOffset(generalTime,
cst.GetUtcOffset(generalTime));

// Add two and a half hours


DateTimeOffset utcTime = centralTime1.ToUniversalTime();
utcTime += twoAndAHalfHours;

DateTimeOffset centralTime2 = TimeZoneInfo.ConvertTime(utcTime,


cst);
// Display result
Console.WriteLine("{0} + {1} hours = {2}", centralTime1,

twoAndAHalfHours.ToString(),
centralTime2);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to retrieve Central Standard Time zone
information.");
}
}
}
// The example displays the following output to the console:
// 3/9/2008 1:30:00 AM -06:00 + 02:30:00 hours = 3/9/2008 5:00:00 AM
-05:00

Confira também
Datas, horas e fusos horários
Como: usar fusos horários em aritmética de data e hora

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Suporte a DateTime e DateTimeOffset
em System.Text.Json
Artigo • 28/03/2023

A biblioteca System.Text.Json analisa e grava os valores DateTime e DateTimeOffset de


acordo com o perfil estendido ISO 8601-1:2019. Os conversores fornecem suporte
personalizado para serialização e desserialização com JsonSerializer. Também é possível
usar Utf8JsonReader e Utf8JsonWriter para implementar o suporte personalizado.

Suporte para o formato ISO 8601-1:2019


Os tipos JsonSerializer, Utf8JsonReader, Utf8JsonWriter e JsonElement analisam e
gravam representações de texto DateTime e DateTimeOffset de acordo com o perfil
estendido do formato ISO 8601-1:2019. Por exemplo, 2019-07-26T16:59:57-05:00 .

Os dados DateTime e DateTimeOffset podem ser serializados com JsonSerializer:

C#

using System.Text.Json;

public class Example


{
private class Product
{
public string? Name { get; set; }
public DateTime ExpiryDate { get; set; }
}

public static void Main(string[] args)


{
Product p = new Product();
p.Name = "Banana";
p.ExpiryDate = new DateTime(2019, 7, 26);

string json = JsonSerializer.Serialize(p);


Console.WriteLine(json);
}
}

// The example displays the following output:


// {"Name":"Banana","ExpiryDate":"2019-07-26T00:00:00"}

DateTime e DateTimeOffset também podem ser desserializados com JsonSerializer:


C#

using System.Text.Json;

public class Example


{
private class Product
{
public string? Name { get; set; }
public DateTime ExpiryDate { get; set; }
}

public static void Main(string[] args)


{
string json = @"{""Name"":""Banana"",""ExpiryDate"":""2019-07-
26T00:00:00""}";
Product p = JsonSerializer.Deserialize<Product>(json)!;
Console.WriteLine(p.Name);
Console.WriteLine(p.ExpiryDate);
}
}

// The example displays output similar to the following:


// Banana
// 7/26/2019 12:00:00 AM

Com as opções padrão, as representações de texto de entrada DateTime e


DateTimeOffset devem estar em conformidade com o perfil estendido ISO 8601-1:2019.
Tentar desserializar representações que não estão em conformidade com o perfil fará
com que JsonSerializer gere um JsonException:

C#

using System.Text.Json;

public class Example


{
private class Product
{
public string? Name { get; set; }
public DateTime ExpiryDate { get; set; }
}

public static void Main(string[] args)


{
string json = @"
{""Name"":""Banana"",""ExpiryDate"":""26/07/2019""}";
try
{
Product _ = JsonSerializer.Deserialize<Product>(json)!;
}
catch (JsonException e)
{
Console.WriteLine(e.Message);
}
}
}

// The example displays the following output:


// The JSON value could not be converted to System.DateTime. Path:
$.ExpiryDate | LineNumber: 0 | BytePositionInLine: 42.

O JsonDocument fornece acesso estruturado ao conteúdo de uma carga JSON,


incluindo representações DateTime e DateTimeOffset. O exemplo a seguir mostra como
calcular a temperatura média às segundas-feiras a partir de uma coleção de
temperaturas:

C#

using System.Text.Json;

public class Example


{
private static double ComputeAverageTemperatures(string json)
{
JsonDocumentOptions options = new JsonDocumentOptions
{
AllowTrailingCommas = true
};

using (JsonDocument document = JsonDocument.Parse(json, options))


{
int sumOfAllTemperatures = 0;
int count = 0;

foreach (JsonElement element in


document.RootElement.EnumerateArray())
{
DateTimeOffset date =
element.GetProperty("date").GetDateTimeOffset();

if (date.DayOfWeek == DayOfWeek.Monday)
{
int temp = element.GetProperty("temp").GetInt32();
sumOfAllTemperatures += temp;
count++;
}
}

double averageTemp = (double)sumOfAllTemperatures / count;


return averageTemp;
}
}
public static void Main(string[] args)
{
string json =
@"[" +
@"{" +
@"""date"": ""2013-01-07T00:00:00Z""," +
@"""temp"": 23," +
@"}," +
@"{" +
@"""date"": ""2013-01-08T00:00:00Z""," +
@"""temp"": 28," +
@"}," +
@"{" +
@"""date"": ""2013-01-14T00:00:00Z""," +
@"""temp"": 8," +
@"}," +
@"]";

Console.WriteLine(ComputeAverageTemperatures(json));
}
}

// The example displays the following output:


// 15.5

Tentar calcular a temperatura média dada uma carga com representações não
compatíveis de DateTime fará com que JsonDocument gere um FormatException:

C#

using System.Text.Json;

public class Example


{
private static double ComputeAverageTemperatures(string json)
{
JsonDocumentOptions options = new JsonDocumentOptions
{
AllowTrailingCommas = true
};

using (JsonDocument document = JsonDocument.Parse(json, options))


{
int sumOfAllTemperatures = 0;
int count = 0;

foreach (JsonElement element in


document.RootElement.EnumerateArray())
{
DateTimeOffset date =
element.GetProperty("date").GetDateTimeOffset();

if (date.DayOfWeek == DayOfWeek.Monday)
{
int temp = element.GetProperty("temp").GetInt32();
sumOfAllTemperatures += temp;
count++;
}
}

double averageTemp = (double)sumOfAllTemperatures / count;


return averageTemp;
}
}

public static void Main(string[] args)


{
// Computing the average temperatures will fail because the
DateTimeOffset
// values in the payload do not conform to the extended ISO 8601-
1:2019 profile.
string json =
@"[" +
@"{" +
@"""date"": ""2013/01/07 00:00:00Z""," +
@"""temp"": 23," +
@"}," +
@"{" +
@"""date"": ""2013/01/08 00:00:00Z""," +
@"""temp"": 28," +
@"}," +
@"{" +
@"""date"": ""2013/01/14 00:00:00Z""," +
@"""temp"": 8," +
@"}," +
@"]";

Console.WriteLine(ComputeAverageTemperatures(json));
}
}

// The example displays the following output:


// Unhandled exception.System.FormatException: One of the identified items
was in an invalid format.
// at System.Text.Json.JsonElement.GetDateTimeOffset()

O nível inferior Utf8JsonWriter grava dados DateTime e DateTimeOffset:

C#

using System.Text;
using System.Text.Json;

public class Example


{
public static void Main(string[] args)
{
JsonWriterOptions options = new JsonWriterOptions
{
Indented = true
};

using (MemoryStream stream = new MemoryStream())


{
using (Utf8JsonWriter writer = new Utf8JsonWriter(stream,
options))
{
writer.WriteStartObject();
writer.WriteString("date", DateTimeOffset.UtcNow);
writer.WriteNumber("temp", 42);
writer.WriteEndObject();
}

string json = Encoding.UTF8.GetString(stream.ToArray());


Console.WriteLine(json);
}
}
}

// The example output similar to the following:


// {
// "date": "2019-07-26T00:00:00+00:00",
// "temp": 42
// }

Utf8JsonReader analisa dados DateTime e DateTimeOffset:

C#

using System.Text;
using System.Text.Json;

public class Example


{
public static void Main(string[] args)
{
byte[] utf8Data = Encoding.UTF8.GetBytes(@"""2019-07-
26T00:00:00""");

Utf8JsonReader json = new Utf8JsonReader(utf8Data);


while (json.Read())
{
if (json.TokenType == JsonTokenType.String)
{
Console.WriteLine(json.TryGetDateTime(out DateTime
datetime));
Console.WriteLine(datetime);
Console.WriteLine(json.GetDateTime());
}
}
}
}

// The example displays output similar to the following:


// True
// 7/26/2019 12:00:00 AM
// 7/26/2019 12:00:00 AM

A tentativa de ler formatos não compatíveis fará com que Utf8JsonReader gere um
FormatException:

C#

using System.Text;
using System.Text.Json;

public class Example


{
public static void Main(string[] args)
{
byte[] utf8Data = Encoding.UTF8.GetBytes(@"""2019/07/26
00:00:00""");

Utf8JsonReader json = new Utf8JsonReader(utf8Data);


while (json.Read())
{
if (json.TokenType == JsonTokenType.String)
{
Console.WriteLine(json.TryGetDateTime(out DateTime
datetime));
Console.WriteLine(datetime);

DateTime _ = json.GetDateTime();
}
}
}
}

// The example displays the following output:


// False
// 1/1/0001 12:00:00 AM
// Unhandled exception. System.FormatException: The JSON value is not in a
supported DateTime format.
// at System.Text.Json.Utf8JsonReader.GetDateTime()

Serializar as propriedades DateOnly e TimeOnly


Com o .NET 7+, o System.Text.Json dá suporte à serialização e desserialização dos tipos
DateOnly e TimeOnly. Considere o seguinte objeto:
C#

sealed file record Appointment(


Guid Id,
string Description,
DateOnly Date,
TimeOnly StartTime,
TimeOnly EndTime);

O exemplo a seguir serializa um objeto Appointment , exibe o JSON resultante e, em


seguida, desserializa-o novamente em uma nova instância do tipo Appointment . Por fim,
as instâncias originais e recém-desserializadas são comparadas quanto à igualdade e os
resultados são gravados no console:

C#

Appointment originalAppointment = new(


Id: Guid.NewGuid(),
Description: "Take dog to veterinarian.",
Date: new DateOnly(2002, 1, 13),
StartTime: new TimeOnly(5,15),
EndTime: new TimeOnly(5, 45));
string serialized = JsonSerializer.Serialize(originalAppointment);

Console.WriteLine($"Resulting JSON: {serialized}");

Appointment deserializedAppointment =
JsonSerializer.Deserialize<Appointment>(serialized)!;

bool valuesAreTheSame = originalAppointment == deserializedAppointment;


Console.WriteLine($"""
Original record has the same values as the deserialized record:
{valuesAreTheSame}
""");

No código anterior:

Um objeto Appointment é instanciado e atribuído à variável appointment .


A instância appointment é serializada para JSON usando JsonSerializer.Serialize.
O JSON resultante é gravado no console.
O JSON é desserializado novamente em uma nova instância do tipo Appointment
usando JsonSerializer.Deserialize.
As instâncias originais e recém-desserializadas são comparadas quanto à
igualdade.
O resultado da comparação é gravado no console.
Suporte personalizado para DateTime e
DateTimeOffset

Ao usar JsonSerializer
Para que o serializador execute a análise ou a formatação personalizadas, você poderá
implementar conversores personalizados. Veja alguns exemplos:

Ao usar DateTime(Offset).Parse e DateTime(Offset).ToString

Se você não conseguir determinar os formatos de suas representações de entrada de


texto DateTime ou DateTimeOffset, poderá usar o método DateTime(Offset).Parse na
lógica de leitura do conversor. Esse método permite que você use o suporte amplo do
.NET para analisar vários formatos de texto DateTime e DateTimeOffset, incluindo
cadeias de caracteres fora dos formatos ISO 8601 e ISO 8601 que não estão em
conformidade com o perfil ISO 8601-1:2019 estendido. Essa abordagem tem um
desempenho menor do que o uso da implementação nativa do serializador.

Para serializar, você pode usar o método DateTime(Offset).ToString na lógica de


gravação do conversor. Esse método permite gravar os valores DateTime e
DateTimeOffset usando quaisquer formatos de data e hora padrão e os formatos de
data e hora personalizados. Essa abordagem também tem um desempenho
significativamente menor do que o uso da implementação nativa do serializador.

C#

using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace DateTimeConverterExamples;

public class DateTimeConverterUsingDateTimeParse : JsonConverter<DateTime>


{
public override DateTime Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));
return DateTime.Parse(reader.GetString() ?? string.Empty);
}

public override void Write(Utf8JsonWriter writer, DateTime value,


JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}
}

class Program
{
private static void ParseDateTimeWithDefaultOptions()
{
DateTime _ = JsonSerializer.Deserialize<DateTime>(@"""04-10-2008
6:30 AM""");
}

private static void FormatDateTimeWithDefaultOptions()


{
Console.WriteLine(JsonSerializer.Serialize(DateTime.Parse("04-10-
2008 6:30 AM -4")));
}

private static void ProcessDateTimeWithCustomConverter()


{
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new DateTimeConverterUsingDateTimeParse());

string testDateTimeStr = "04-10-2008 6:30 AM";


string testDateTimeJson = @"""" + testDateTimeStr + @"""";

DateTime resultDateTime = JsonSerializer.Deserialize<DateTime>


(testDateTimeJson, options);
Console.WriteLine(resultDateTime);

string resultDateTimeJson =
JsonSerializer.Serialize(DateTime.Parse(testDateTimeStr), options);
Console.WriteLine(Regex.Unescape(resultDateTimeJson));
}

static void Main(string[] args)


{
// Parsing non-compliant format as DateTime fails by default.
try
{
ParseDateTimeWithDefaultOptions();
}
catch (JsonException e)
{
Console.WriteLine(e.Message);
}

// Formatting with default options prints according to extended ISO


8601 profile.
FormatDateTimeWithDefaultOptions();

// Using converters gives you control over the serializers parsing


and formatting.
ProcessDateTimeWithCustomConverter();
}
}

// The example displays output similar to the following:


// The JSON value could not be converted to System.DateTime. Path: $ |
LineNumber: 0 | BytePositionInLine: 20.
// "2008-04-10T06:30:00-04:00"
// 4/10/2008 6:30:00 AM
// "4/10/2008 6:30:00 AM"

7 Observação

Ao implementar JsonConverter<T>, e T é DateTime, o parâmetro typeToConvert


sempre será typeof(DateTime) . O parâmetro é útil para lidar com casos
polimórficos e ao usar genéricos para obter typeof(T) com alto desempenho.

Usando Utf8Parser e Utf8Formatter

Você pode usar métodos rápidos de análise e formatação baseados em UTF 8 em sua
lógica de conversor se suas representações de entrada de texto DateTime ou
DateTimeOffset estiverem em conformidade com uma das cadeias de caracteres padrão
de formato de data e hora "R", "l", "O" ou "G" ou se desejar gravar de acordo com um
desses formatos. Essa abordagem é muito mais rápida do que ao usar
s DateTime(Offset).Parse e DateTime(Offset).ToString .

O exemplo a seguir mostra um conversor personalizado que serializa e desserializa os


valores DateTime de acordo com o formato padrão "R":

C#

using System.Buffers;
using System.Buffers.Text;
using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace DateTimeConverterExamples;

// This converter reads and writes DateTime values according to the "R"
standard format specifier:
// https://learn.microsoft.com/dotnet/standard/base-types/standard-date-and-
time-format-strings#the-rfc1123-r-r-format-specifier.
public class DateTimeConverterForCustomStandardFormatR :
JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));

if (Utf8Parser.TryParse(reader.ValueSpan, out DateTime value, out _,


'R'))
{
return value;
}

throw new FormatException();


}

public override void Write(Utf8JsonWriter writer, DateTime value,


JsonSerializerOptions options)
{
// The "R" standard format will always be 29 bytes.
Span<byte> utf8Date = new byte[29];

bool result = Utf8Formatter.TryFormat(value, utf8Date, out _, new


StandardFormat('R'));
Debug.Assert(result);

writer.WriteStringValue(utf8Date);
}
}

class Program
{
private static void ParseDateTimeWithDefaultOptions()
{
DateTime _ = JsonSerializer.Deserialize<DateTime>(@"""Thu, 25 Jul
2019 13:36:07 GMT""");
}

private static void ProcessDateTimeWithCustomConverter()


{
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new
DateTimeConverterForCustomStandardFormatR());

string testDateTimeStr = "Thu, 25 Jul 2019 13:36:07 GMT";


string testDateTimeJson = @"""" + testDateTimeStr + @"""";

DateTime resultDateTime = JsonSerializer.Deserialize<DateTime>


(testDateTimeJson, options);
Console.WriteLine(resultDateTime);

Console.WriteLine(JsonSerializer.Serialize(DateTime.Parse(testDateTimeStr),
options));
}

static void Main(string[] args)


{
// Parsing non-compliant format as DateTime fails by default.
try
{
ParseDateTimeWithDefaultOptions();
}
catch (JsonException e)
{
Console.WriteLine(e.Message);
}

// Using converters gives you control over the serializers parsing


and formatting.
ProcessDateTimeWithCustomConverter();
}
}

// The example displays output similar to the following:


// The JSON value could not be converted to System.DateTime.Path: $ |
LineNumber: 0 | BytePositionInLine: 31.
// 7/25/2019 1:36:07 PM
// "Thu, 25 Jul 2019 09:36:07 GMT"

7 Observação

O formato padrão "R" sempre terá 29 caracteres.

O formato "l" ("L" minúsculo) não está documentado com as outras cadeias de
caracteres de formato de data e hora padrão porque tem suporte apenas pelos
tipos Utf8Parser e Utf8Formatter . O formato é RFC 1123 em minúsculas (uma
versão em minúsculas do formato “R”). Por exemplo, "thu, 25 jul 2019 06:36:07
gmt".

Ao usar DateTime(Offset).Parse como um fallback para a análise


nativa do serializador

Se você geralmente espera que sua entrada de dadosDateTime ou DateTimeOffset


esteja em conformidade com o perfil estendido ISO 8601-1:2019, você poderá usar a
lógica de análise nativa do serializador. Você também pode implementar um mecanismo
de fallback. O exemplo a seguir mostra que, depois de não analisar uma representação
de texto DateTime usando TryGetDateTime(DateTime), o conversor analisa com êxito os
dados usando Parse(String):

C#

using System.Diagnostics;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace DateTimeConverterExamples;

public class DateTimeConverterUsingDateTimeParseAsFallback :


JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert == typeof(DateTime));

if (!reader.TryGetDateTime(out DateTime value))


{
value = DateTime.Parse(reader.GetString()!);
}

return value;
}

public override void Write(Utf8JsonWriter writer, DateTime value,


JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString("dd/MM/yyyy"));
}
}

class Program
{
private static void ParseDateTimeWithDefaultOptions()
{
DateTime _ = JsonSerializer.Deserialize<DateTime>(@"""2019-07-16
16:45:27.4937872+00:00""");
}

private static void ProcessDateTimeWithCustomConverter()


{
JsonSerializerOptions options = new JsonSerializerOptions();
options.Converters.Add(new
DateTimeConverterUsingDateTimeParseAsFallback());

string testDateTimeStr = "2019-07-16 16:45:27.4937872+00:00";


string testDateTimeJson = @"""" + testDateTimeStr + @"""";

DateTime resultDateTime = JsonSerializer.Deserialize<DateTime>


(testDateTimeJson, options);
Console.WriteLine(resultDateTime);

string resultDateTimeJson =
JsonSerializer.Serialize(DateTime.Parse(testDateTimeStr), options);
Console.WriteLine(Regex.Unescape(resultDateTimeJson));
}

static void Main(string[] args)


{
// Parsing non-compliant format as DateTime fails by default.
try
{
ParseDateTimeWithDefaultOptions();
}
catch (JsonException e)
{
Console.WriteLine(e.Message);
}

// Using converters gives you control over the serializers parsing


and formatting.
ProcessDateTimeWithCustomConverter();
}
}

// The example displays output similar to the following:


// The JSON value could not be converted to System.DateTime.Path: $ |
LineNumber: 0 | BytePositionInLine: 35.
// 7/16/2019 4:45:27 PM
// "16/07/2019"

Uso do formato de data da época do Unix


Os conversores a seguir manipulam o formato de época Unix com ou sem um
deslocamento de fuso horário (valores como /Date(1590863400000-0700)/ ou
/Date(1590863400000)/ ):

C#

sealed class UnixEpochDateTimeOffsetConverter :


JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset s_epoch = new DateTimeOffset(1970, 1, 1,
0, 0, 0, TimeSpan.Zero);
static readonly Regex s_regex = new Regex("^/Date\\(([+-]*\\d+)([+-])
(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

public override DateTimeOffset Read(ref Utf8JsonReader reader, Type


typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);

if (
!match.Success
|| !long.TryParse(match.Groups[1].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
long unixTime)
|| !int.TryParse(match.Groups[3].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
int hours)
|| !int.TryParse(match.Groups[4].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
int minutes))
{
throw new JsonException();
}

int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;


TimeSpan utcOffset = new TimeSpan(hours * sign, minutes * sign, 0);

return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
}

public override void Write(Utf8JsonWriter writer, DateTimeOffset value,


JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value -
s_epoch).TotalMilliseconds);
TimeSpan utcOffset = value.Offset;

string formatted = string.Create(CultureInfo.InvariantCulture,


$"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}
{utcOffset:hhmm})/");

writer.WriteStringValue(formatted);
}
}

C#

sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>


{
static readonly DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0);
static readonly Regex s_regex = new Regex("^/Date\\(([+-]*\\d+)\\)/$",
RegexOptions.CultureInvariant);

public override DateTime Read(ref Utf8JsonReader reader, Type


typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);

if (
!match.Success
|| !long.TryParse(match.Groups[1].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
long unixTime))
{
throw new JsonException();
}

return s_epoch.AddMilliseconds(unixTime);
}

public override void Write(Utf8JsonWriter writer, DateTime value,


JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value -
s_epoch).TotalMilliseconds);

string formatted = string.Create(CultureInfo.InvariantCulture,


$"/Date({unixTime})/");
writer.WriteStringValue(formatted);
}
}

Ao gravar com Utf8JsonWriter


Se você quiser gravar uma representação de texto personalizada DateTime ou
DateTimeOffset com Utf8JsonWriter, você poderá formatar sua representação
personalizada para String, ReadOnlySpan<Byte> , ReadOnlySpan<Char> ou JsonEncodedText
e, em seguida, passá-la para o método Utf8JsonWriter.WriteStringValue ou
Utf8JsonWriter.WriteString correspondente.

O exemplo a seguir mostra como um formato personalizado DateTime pode ser criado
com ToString(String, IFormatProvider) e, em seguida, gravado com o método
WriteStringValue(String):

C#

using System.Globalization;
using System.Text;
using System.Text.Json;

public class Example


{
public static void Main(string[] args)
{
var options = new JsonWriterOptions
{
Indented = true
};

using (var stream = new MemoryStream())


{
using (var writer = new Utf8JsonWriter(stream, options))
{
string dateStr = DateTime.UtcNow.ToString("F",
CultureInfo.InvariantCulture);

writer.WriteStartObject();
writer.WriteString("date", dateStr);
writer.WriteNumber("temp", 42);
writer.WriteEndObject();
}

string json = Encoding.UTF8.GetString(stream.ToArray());


Console.WriteLine(json);
}
}
}

// The example displays output similar to the following:


// {
// "date": "Tuesday, 27 August 2019 19:21:44",
// "temp": 42
// }

Ao ler com Utf8JsonReader


Para ler uma representação de texto personalizada DateTime ou DateTimeOffset com
Utf8JsonReader, é possível obter o valor do token JSON atual como um String usando o
método GetString() e, em seguida, analisar o valor usando a lógica personalizada.

O exemplo a seguir mostra como uma representação de texto personalizada


DateTimeOffset pode ser recuperada usando o método GetString() e, em seguida,
analisada usando ParseExact(String, String, IFormatProvider):

C#

using System.Globalization;
using System.Text;
using System.Text.Json;

public class Example


{
public static void Main(string[] args)
{
byte[] utf8Data = Encoding.UTF8.GetBytes(@"""Friday, 26 July 2019
00:00:00""");

var json = new Utf8JsonReader(utf8Data);


while (json.Read())
{
if (json.TokenType == JsonTokenType.String)
{
string value = json.GetString();
DateTimeOffset dto = DateTimeOffset.ParseExact(value, "F",
CultureInfo.InvariantCulture);
Console.WriteLine(dto);
}
}
}
}

// The example displays output similar to the following:


// 7/26/2019 12:00:00 AM -04:00

O perfil ISO 8601-1:2019 estendido em


System.Text.Json

Componentes de data e hora


O perfil ISO 8601-1:2019 estendido implementado em System.Text.Json define os
seguintes componentes para representações de data e hora. Esses componentes são
usados para definir vários níveis de granularidade com suporte ao analisar e formatar as
representações DateTime e DateTimeOffset.

Componente Formatar Descrição

Year "yyyy" 0001-9999

Mês "MM" 01-12

Dia "dd" 01-28, 01-29, 01-30, 01-31 com base no


mês/ano.

Hora "HH" 00-23

Minuto "mm" 00-59

Segundo "ss" 00-59

Segunda fração "FFFFFFF" Mínimo de um dígito, máximo de 16 dígitos.

Deslocamento de "K" "Z" ou "('+'/'-')HH':'mm".


horário

Tempo parcial "HH':'mm':'ss[FFFFFFF]" Tempo sem informações de diferença UTC.

Data completa "yyyy'-'MM'-'dd" Data do calendário.

Tempo completo "'Partial time'K" UTC do dia ou hora local do dia com a diferença
de tempo entre a hora local e UTC.

Data e hora "'Full date''T''Full Data e hora do calendário, por exemplo, 2019-
time'" 07-26T16:59:57-05:00.
Suporte para análise
Os seguintes níveis de granularidade são definidos para análise:

1. 'Data completa'
a. "yyyy'-'MM'-'dd"

2. "'Full date''T''Hour'':''Minute'"
a. "yyyy'-'MM'-'dd'T'HH':'mm"

3. "'Full date''T''Partial time'"


a. "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (Especificador de formato ("s") classificável)
b. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"

4. "'Full date''T''Time hour'':''Minute''Time offset'"


a. "yyyy'-'MM'-'dd'T'HH':'mmZ"
b. "yyyy'-'MM'-'dd'T'HH':'mm('+'/'-')HH':'mm"

5. 'Date time'
a. "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
b. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"
c. "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"
d. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"

Esse nível de granularidade está em conformidade com o RFC 3339 , um perfil


amplamente adotado do ISO 8601 usado para trocar informações de data e hora.
No entanto, há algumas restrições na implementação de System.Text.Json .

O RFC 3339 não especifica um número máximo de dígitos de segundo


fracionário, mas especifica que pelo menos um dígito deve seguir o período,
se uma seção fracionária de segundo estiver presente. A implementação em
System.Text.Json permite até 16 dígitos (para dar suporte à

interoperabilidade com outras estruturas e linguagens de programação), mas


analisa apenas os sete primeiros. JsonException será gerado se houver mais
de 16 segundos dígitos fracionários ao gravar instâncias DateTime e
DateTimeOffset .

O RFC 3339 permite que os caracteres "T" e "Z" sejam "t" ou "z",
respectivamente, mas permite que os aplicativos limitem o suporte apenas às
variantes em maiúsculas. A implementação em System.Text.Json exige que
eles sejam "T" e "Z". JsonException será gerado se as cargas de entrada
contiverem "t" ou "z" ao ler as instâncias DateTime e DateTimeOffset .
O RFC 3339 especifica que as seções de data e hora são separadas por "T",
mas permite que os aplicativos os separem por um espaço (" ").
System.Text.Json requer que as seções de data e hora sejam separadas por

"T". JsonException será gerado se as cargas de entrada contiverem um


espaço (" ") ao ler as instâncias DateTime e DateTimeOffset .

Se houver frações decimais por segundos, deve haver pelo menos um dígito. 2019-07-
26T00:00:00. não é permitido. Enquanto até 16 dígitos fracionários são permitidos,

apenas os sete primeiros são analisados. Qualquer coisa além disso é considerada zero.
Por exemplo, 2019-07-26T00:00:00.1234567890 será analisado como se fosse 2019-07-
26T00:00:00.1234567 . Essa abordagem se mantém compatível com a implementação de

DateTime, que é limitada a essa resolução.

Não há suporte para segundos bissextos.

Suporte para formatação


Os seguintes níveis de granularidade são definidos para formatação:

1. "'Full date''T''Partial time'"

a. "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (Especificador de formato ("s") classificável)

Usado para formatar DateTime sem segundos fracionários e sem informações


de diferença.

b. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"

Usado para formatar DateTime com segundos fracionários, mas sem


informações de diferença.

2. 'Date time'

a. "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"

Usado para formatar DateTime sem segundos fracionários, mas com uma
diferença UTC.

b. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"

Usado para formatar DateTime com segundos fracionários e com uma diferença
UTC.

c. "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"

Usado para formatar DateTime ou DateTimeOffset sem segundos fracionários,


mas com uma diferença local.
d. "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"

Usado para formatar DateTime ou DateTimeOffset com segundos fracionários e


com uma diferença local.

Esse nível de granularidade está em conformidade com o RFC 3339 .

Se a representação de formato de ida e volta de uma instância DateTime ou


DateTimeOffset tiver zeros à direita em seus segundos fracionários, JsonSerializer e
Utf8JsonWriter formatará uma representação da instância sem zeros à direita. Por
exemplo, uma instância DateTime cuja representação de formato de ida e volta é 2019-
04-24T14:50:17.1010000Z , será formatada como 2019-04-24T14:50:17.101Z por

JsonSerializer e Utf8JsonWriter.

Se a representação de formato de ida e volta de uma instância DateTime ou


DateTimeOffset tiver todos os zeros em seus segundos fracionários, JsonSerializer e
Utf8JsonWriter formatará uma representação da instância sem segundos fracionários.
Por exemplo, uma instância DateTime cuja representação de formato de ida e volta é
2019-04-24T14:50:17.0000000+02:00 , será formatada como 2019-04-24T14:50:17+02:00

por JsonSerializer e Utf8JsonWriter.

Truncar zeros em dígitos de segundos fracionários permite que a menor saída


necessária para preservar as informações em uma viagem de ida e volta seja gravada.

Um máximo de sete dígitos fracionários são gravados. Esse valor máximo se alinha à
implementação DateTime, que é limitada a essa resolução.

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Visão geral do fuso horário
Artigo • 10/05/2023

A classe TimeZoneInfo simplifica a criação de aplicativos com reconhecimento de fuso


horário. A classe TimeZone dá suporte ao trabalho com o fuso horário local e o UTC
(Tempo Universal Coordenado). A classe TimeZoneInfo dá suporte a esses dois fusos
horários, bem como a qualquer fuso horário sobre quais informações são predefinidas
no registro. Você também pode usar a TimeZoneInfo para definir fusos horários
personalizados dos quais o sistema não tenha nenhuma informação.

Fundamentos sobre fuso horário


Um fuso horário é uma região geográfica na qual o mesmo horário é usado.
Normalmente, mas nem sempre, os fusos horários adjacentes diferem em uma hora. A
hora em cada um dos fusos horários do mundo pode ser expressa como uma diferença
do Tempo Universal Coordenado (UTC).

Muitos dos fusos horários do mundo permitem horário de verão. O horário de verão
tenta maximizar as horas do dia com sol adiantando o horário em uma hora na
primavera ou no começo do verão e retornando ao horário normal (ou padrão) no final
do verão ou no outono. Essas alterações de/para o horário padrão são conhecidas como
regras de ajuste.

A transição de/para o horário de verão em um determinado fuso horário pode ser


definida por uma regra de ajuste fixa ou flutuante. Uma regra de ajuste fixa define uma
data específica na qual a transição de/para o horário de verão ocorre todos os anos. Por
exemplo, uma transição do horário de verão para o horário padrão que ocorre todos os
anos em 25 de outubro segue uma regra de ajuste fixa. Muito mais comuns são as
regras de ajuste flutuante, que definem um dia específico de uma semana específica de
um mês específico para a transição de/para o horário de verão. Por exemplo, uma
transição do horário padrão para o horário de verão que ocorre no terceiro domingo de
março segue uma regra de ajuste flutuante.

Para os fusos horários que oferecem suporte às regras de ajuste, a transição de/para o
horário de verão cria dois tipos de horários anômalos: horários inválidos e horários
ambíguos. Um horário inválido é um horário inexistente criado pela transição do horário
padrão para o horário de verão. Por exemplo, se essa transição ocorrer em um dia
específico às 2:00 da manhã e fizer com que a hora seja alterada para 3:00 da manhã,
cada intervalo de tempo entre 2:00 e 2:59:59 da manhã será inválido. Um horário
ambíguo é aquele que pode ser mapeado para dois horários diferentes em um único
fuso horário. Ele é criado pela transição do horário de verão para o horário padrão. Por
exemplo, se essa transição ocorrer em um dia específico às 2:00 da manhã e fizer com
que a hora seja alterada para 1:00, cada intervalo de tempo entre 1:00 e 1:59:59 da
manhã poderá ser interpretado como uma hora padrão ou horário de verão.

Terminologia de fuso horário


A tabela a seguir define os termos que geralmente são usados ao trabalhar com fusos
horários e ao desenvolver aplicativos com reconhecimento de fuso horário.

Termo Definição

Regra de Uma regra que define quando ocorre a transição do horário padrão para o horário
ajuste de verão e o retorno do horário de verão para o horário padrão. Cada regra de ajuste
tem uma data de início e uma data de término que definem quando a regra está em
vigor (por exemplo, a regra de ajuste está em vigor de 1 de janeiro de 1986 a 31 de
dezembro de 2006), um delta (o período de tempo de mudança do horário padrão
como resultado da aplicação da regra de ajuste) e informações sobre a data e a hora
específicas em que as transições devem ocorrer durante o período de ajuste. As
transições podem seguir uma regra fixa ou uma regra flutuante.

Horário Um horário que pode ser mapeado para dois horários diferentes em um único fuso
ambíguo horário. Ocorre quando o horário do relógio é atrasado, como durante a transição
do horário de verão de um fuso horário para seu horário padrão. Por exemplo, se
essa transição ocorrer em um dia específico às 2:00 da manhã e fizer com que a hora
seja alterada para 1:00, cada intervalo de tempo entre 1:00 e 1:59:59 da manhã
poderá ser interpretado como uma hora padrão ou horário de verão.

Regra fixa Uma regra de ajuste que define uma data específica para a transição de/para o
horário de verão. Por exemplo, uma transição do horário de verão para o horário
padrão que ocorre todos os anos em 25 de outubro segue uma regra de ajuste fixa.

Regra Uma regra de ajuste que define um dia específico de uma semana específica de um
flutuante mês específico para a transição de/para o horário de verão. Por exemplo, uma
transição do horário padrão para o horário de verão que ocorre no terceiro domingo
de março segue uma regra de ajuste flutuante.

Horário Um horário inexistente que é um artefato da transição do horário padrão para o


inválido horário de verão. Ocorre quando o horário do relógio é adiantado, como durante a
transição do horário padrão de um fuso horário para seu horário de verão. Por
exemplo, se essa transição ocorrer em um dia específico às 2:00 da manhã e fizer
com que a hora seja alterada para 3:00 da manhã, cada intervalo de tempo entre
2:00 e 2:59:59 da manhã será inválido.

Horário Informações sobre uma mudança de horário específica, como a mudança do horário
de de verão para o horário padrão ou vice-versa, em um determinado fuso horário.
transição
Fusos horários e a classe TimeZoneInfo
No .NET, um objeto TimeZoneInfo representa um fuso horário. A classe TimeZoneInfo
inclui um método GetAdjustmentRules que retorna uma matriz de objetos
TimeZoneInfo.AdjustmentRule. Cada elemento dessa matriz fornece informações sobre
a transição de e para o horário de verão para um determinado período. (Para fusos
horários que não dão suporte ao horário de verão, o método retorna uma matriz vazia.)
Cada objeto TimeZoneInfo.AdjustmentRule tem uma propriedade
DaylightTransitionStart e uma propriedade DaylightTransitionEnd que define a data e a
hora específicas da transição de e para o horário de verão. A propriedade
IsFixedDateRule indica se essa transição é fixa ou flutuante.

O .NET depende das informações de fuso horário fornecidas pelo sistema operacional
Windows e armazenadas no registro. Devido ao número de fusos horários da Terra, nem
todos os fusos horários são representados no registro. Além disso, como o registro é
uma estrutura dinâmica, os fusos horários predefinidos podem ser adicionados ou
removidos dele. Por fim, o registro não contém necessariamente dados históricos de
fuso horário. Por exemplo, no Windows XP, o registro contém dados sobre apenas um
conjunto de ajustes de fuso horário. O Windows Vista dá suporte a dados dinâmicos de
fuso horário, o que significa que um só fuso horário pode ter várias regras de ajuste que
se aplicam a intervalos específicos de anos. No entanto, a maioria dos fusos horários
definidos no registro do Windows Vista e que dão suporte ao horário de verão tem
apenas uma ou duas regras de ajuste predefinidas.

A dependência da classe TimeZoneInfo no Registro significa que um aplicativo com


reconhecimento de fuso horário não pode ter certeza de que um determinado fuso
horário está definido no Registro. Como resultado, a tentativa de criar uma instância de
um fuso horário específico (que não seja o fuso horário local ou o fuso horário que
representa o UTC) deve usar tratamento de exceção. Ela também deve fornecer algum
método para permitir que o aplicativo continue caso não seja possível criar uma
instância de um objeto TimeZoneInfo obrigatório por meio do Registro.

Para lidar com a ausência de um fuso horário necessário, a classe TimeZoneInfo inclui
um método CreateCustomTimeZone, que você pode usar para criar fusos horários
personalizados que não são encontrados no registro. Para detalhes sobre como criar um
fuso horário personalizado, confira Como criar fusos horários sem regras de ajuste e
Como criar fusos horários com regras de ajuste. Além disso, você pode usar o método
ToSerializedString para converter um fuso horário que acaba de ser criado em uma
cadeia de caracteres e salvá-lo em um armazenamento de dados (como um banco de
dados, um arquivo de texto, o registro ou um recurso de aplicativo). Em seguida, você
pode usar o método FromSerializedString para converter essa cadeia de caracteres de
volta em um objeto TimeZoneInfo. Para detalhes, confira Como salvar fusos horários em
um recurso inserido e Como restaurar fusos horários de um recurso inserido.

Como cada fuso horário é caracterizado por uma diferença base do UTC, bem como por
uma diferença do UTC que reflete as regras de ajuste existentes, um horário em um fuso
horário pode ser facilmente convertido no horário em outro fuso horário. Para essa
finalidade, o objeto TimeZoneInfo inclui vários métodos de conversão, como:

ConvertTimeFromUtc, que converte UTC na hora em um fuso horário designado.

ConvertTimeToUtc, que converte a hora em um fuso horário designado para UTC.

ConvertTime, que converte a hora em um fuso horário designado para o horário


em outro fuso horário designado.

ConvertTimeBySystemTimeZoneId, que usa identificadores de fuso horário (em vez


de objetos TimeZoneInfo) como parâmetros para converter a hora em um fuso
horário designado para o horário em outro fuso horário designado.

Para obter detalhes de como converter horários entre fusos horários, consulte
Convertendo horários entre fusos horários.

Confira também
Datas, horas e fusos horários

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: usar fusos horários em aritmética
de data e hora
Artigo • 07/04/2023

Normalmente, quando você executa a aritmética de data e hora usando valores


DateTime ou DateTimeOffset, o resultado não reflete nenhuma regra de ajuste de fuso
horário. Isso é verdadeiro mesmo quando o fuso horário do valor de data e hora é
claramente identificável (por exemplo, quando a propriedade Kind é definida como
Local). Este tópico mostra como executar operações aritméticas em valores de data e
hora que pertencem a um fuso horário específico. Os resultados das operações
aritméticas refletirão as regras de ajuste do fuso horário.

Para aplicar as regras de ajuste à aritmética de data e


hora
1. Implemente um método de acoplamento próximo de um valor de data e hora com
o fuso horário ao qual ele pertence. Por exemplo, declare uma estrutura que inclui
o valor de data e hora e seu fuso horário. O exemplo a seguir usa essa abordagem
para vincular um valor de DateTime ao seu fuso horário.

C#

// Define a structure for DateTime values for internal use only


internal struct TimeWithTimeZone
{
TimeZoneInfo TimeZone;
DateTime Time;
}

2. Converta uma hora em UTC (Tempo Universal Coordenado) chamando o método


ConvertTimeToUtc ou o método ConvertTime.

3. Execute a operação aritmética na hora UTC.

4. Converta a hora de UTC para o fuso horário associado da hora original chamando
o método TimeZoneInfo.ConvertTime(DateTime, TimeZoneInfo).

Exemplo
O exemplo a seguir adiciona duas horas e trinta minutos a 9 de março de 2008, à 1h30
do Horário Padrão Central. A transição do fuso horário para o horário de verão ocorre
30 minutos depois, às 2h em 9 de março de 2008. Como o exemplo segue as quatro
etapas listadas na seção anterior, ele relata corretamente a hora resultante como 5h em
9 de março de 2008.

C#

using System;

public struct TimeZoneTime


{
public TimeZoneInfo TimeZone;
public DateTime Time;

public TimeZoneTime(TimeZoneInfo tz, DateTime time)


{
if (tz == null)
throw new ArgumentNullException("The time zone cannot be a null
reference.");

this.TimeZone = tz;
this.Time = time;
}

public TimeZoneTime AddTime(TimeSpan interval)


{
// Convert time to UTC
DateTime utcTime = TimeZoneInfo.ConvertTimeToUtc(this.Time,
this.TimeZone);
// Add time interval to time
utcTime = utcTime.Add(interval);
// Convert time back to time in time zone
return new TimeZoneTime(this.TimeZone,
TimeZoneInfo.ConvertTime(utcTime,
TimeZoneInfo.Utc, this.TimeZone));
}
}

public class TimeArithmetic


{
public const string tzName = "Central Standard Time";

public static void Main()


{
try
{
TimeZoneTime cstTime1, cstTime2;

TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);


DateTime time1 = new DateTime(2008, 3, 9, 1, 30, 0);
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

cstTime1 = new TimeZoneTime(cst, time1);


cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time,

twoAndAHalfHours.ToString(),
cstTime2.Time);
}
catch
{
Console.WriteLine("Unable to find {0}.", tzName);
}
}
}

Os valores DateTimee DateTimeOffset serão desassociados de qualquer fuso horário ao


qual possam pertencer. Para executar a aritmética de data e hora de uma maneira que
aplique automaticamente as regras de ajuste de um fuso horário, o fuso horário ao qual
qualquer valor de data e hora pertence deve ser imediatamente identificável. Isso
significa que uma data e hora e seu fuso horário associado devem estar estritamente
acoplados. Há várias maneiras de fazer isso, que incluem o seguinte:

Supor que todas as horas usadas em um aplicativo pertencem a um fuso horário


específico. Embora adequada em alguns casos, essa abordagem oferece
flexibilidade limitada e possivelmente portabilidade limitada.

Definir um tipo que acople estritamente uma data e hora e seu fuso horário
associado incluindo ambas como campos do tipo. Essa abordagem é usada no
exemplo de código, que define uma estrutura para armazenar a data e hora e o
fuso horário em dois campos de membro.

O exemplo ilustra como executar operações aritméticas em valores DateTime para que
as regras de ajuste de fuso horário sejam aplicadas ao resultado. No entanto, os valores
DateTimeOffset podem ser usados com a mesma facilidade. O exemplo a seguir ilustra
como o código no exemplo original pode ser adaptado para usar DateTimeOffset, em
vez de valores DateTime.

C#

using System;

public struct TimeZoneTime


{
public TimeZoneInfo TimeZone;
public DateTimeOffset Time;

public TimeZoneTime(TimeZoneInfo tz, DateTimeOffset time)


{
if (tz == null)
throw new ArgumentNullException("The time zone cannot be a null
reference.");
this.TimeZone = tz;
this.Time = time;
}

public TimeZoneTime AddTime(TimeSpan interval)


{
// Convert time to UTC
DateTimeOffset utcTime = TimeZoneInfo.ConvertTime(this.Time,
TimeZoneInfo.Utc);
// Add time interval to time
utcTime = utcTime.Add(interval);
// Convert time back to time in time zone
return new TimeZoneTime(this.TimeZone,
TimeZoneInfo.ConvertTime(utcTime, this.TimeZone));
}
}

public class TimeArithmetic


{
public const string tzName = "Central Standard Time";

public static void Main()


{
try
{
TimeZoneTime cstTime1, cstTime2;

TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById(tzName);


DateTime time1 = new DateTime(2008, 3, 9, 1, 30, 0);
TimeSpan twoAndAHalfHours = new TimeSpan(2, 30, 0);

cstTime1 = new TimeZoneTime(cst,


new DateTimeOffset(time1, cst.GetUtcOffset(time1)));
cstTime2 = cstTime1.AddTime(twoAndAHalfHours);
Console.WriteLine("{0} + {1} hours = {2}", cstTime1.Time,

twoAndAHalfHours.ToString(),
cstTime2.Time);
}
catch
{
Console.WriteLine("Unable to find {0}.", tzName);
}
}
}

Observe que se esta adição for simplesmente realizada no valor DateTimeOffset sem
primeiro convertê-lo para UTC, o resultado refletirá o ponto no tempo correto, mas seu
deslocamento não refletirá o do fuso horário designado para aquela hora.
Compilando o código
Este exemplo requer:

Que o namespace System seja importado com a instrução using (necessária no


código C#).

Confira também
Datas, horas e fusos horários
Executando operações aritméticas com datas e horas

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Convertendo entre DateTime e
DateTimeOffset
Artigo • 10/05/2023

Embora a estrutura DateTimeOffsetofereça maior grau de reconhecimento de fuso


horário do que a estrutura DateTime, é mais comum usar os parâmetros DateTime em
chamadas de método. Devido a essa abordagem, a capacidade de converter valores de
DateTimeOffset em valores de DateTime e vice-versa é importante. Este artigo mostra
como executar essas conversões de maneira a preservar as informações de fuso horário
tanto quanto possível.

7 Observação

Os tipos DateTime e DateTimeOffset têm algumas limitações para representar


horas em fusos horários. Com a propriedade Kind, DateTime pode refletir somente
o UTC (Tempo Universal Coordenado) e o fuso horário local do sistema.
DateTimeOffset reflete a diferença UTC, mas não reflete o fuso horário real ao qual
essa diferença pertence. Para obter detalhes sobre os valores de hora e o suporte
para fusos horários, confira Como escolher entre DateTime, DateTimeOffset,
TimeSpan e TimeZoneInfo.

Conversões de DateTime para DateTimeOffset


A estrutura DateTimeOffset fornece duas maneiras equivalentes de executar a conversão
de DateTime em DateTimeOffset, que são adequadas para a maioria das conversões:

O construtor DateTimeOffset que cria um novo objeto DateTimeOffset baseado


em um valor DateTime.

O operador de conversão implícita, que permite que você atribua um valor


DateTime a um objeto DateTimeOffset.

Para valores UTC e local de DateTime, a propriedade Offset do valor DateTimeOffset


resultante reflete com precisão a diferença UTC ou do fuso horário local. Por exemplo, o
código a seguir converte uma hora em UTC no valor DateTimeOffset equivalente:

C#

DateTime utcTime1 = new DateTime(2008, 6, 19, 7, 0, 0);


utcTime1 = DateTime.SpecifyKind(utcTime1, DateTimeKind.Utc);
DateTimeOffset utcTime2 = utcTime1;
Console.WriteLine("Converted {0} {1} to a DateTimeOffset value of {2}",
utcTime1,
utcTime1.Kind,
utcTime2);
// This example displays the following output to the console:
// Converted 6/19/2008 7:00:00 AM Utc to a DateTimeOffset value of
6/19/2008 7:00:00 AM +00:00

Nesse caso, o deslocamento da variável utcTime2 é 00:00. Do mesmo modo, o código a


seguir converte uma hora local no valor DateTimeOffset equivalente:

C#

DateTime localTime1 = new DateTime(2008, 6, 19, 7, 0, 0);


localTime1 = DateTime.SpecifyKind(localTime1, DateTimeKind.Local);
DateTimeOffset localTime2 = localTime1;
Console.WriteLine("Converted {0} {1} to a DateTimeOffset value of {2}",
localTime1,
localTime1.Kind,
localTime2);
// This example displays the following output to the console:
// Converted 6/19/2008 7:00:00 AM Local to a DateTimeOffset value of
6/19/2008 7:00:00 AM -07:00

No entanto, para valores DateTime cuja propriedade Kind é DateTimeKind.Unspecified,


esses dois métodos de conversão produzem um valor DateTimeOffset, cuja diferença é
a do fuso horário local. A conversão é mostrada no exemplo a seguir, que é executado
no fuso horário padrão do Pacífico dos EUA:

C#

DateTime time1 = new DateTime(2008, 6, 19, 7, 0, 0); // Kind is


DateTimeKind.Unspecified
DateTimeOffset time2 = time1;
Console.WriteLine("Converted {0} {1} to a DateTimeOffset value of {2}",
time1,
time1.Kind,
time2);
// This example displays the following output to the console:
// Converted 6/19/2008 7:00:00 AM Unspecified to a DateTimeOffset value
of 6/19/2008 7:00:00 AM -07:00

Se o valor DateTime refletir a data e a hora em um resultado diferente do fuso horário


local ou UTC, você poderá convertê-lo para um valor DateTimeOffset e preservar as
informações de fuso horário, chamando o construtor DateTimeOffset sobrecarregado. O
exemplo a seguir instancia um objeto DateTimeOffset que reflete a Hora Padrão Central:
C#

DateTime time1 = new DateTime(2008, 6, 19, 7, 0, 0); // Kind is


DateTimeKind.Unspecified
try
{
DateTimeOffset time2 = new DateTimeOffset(time1,
TimeZoneInfo.FindSystemTimeZoneById("Central Standard
Time").GetUtcOffset(time1));
Console.WriteLine("Converted {0} {1} to a DateTime value of {2}",
time1,
time1.Kind,
time2);
}
// Handle exception if time zone is not defined in registry
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to identify target time zone for conversion.");
}
// This example displays the following output to the console:
// Converted 6/19/2008 7:00:00 AM Unspecified to a DateTime value of
6/19/2008 7:00:00 AM -05:00

O segundo parâmetro para essa sobrecarga de construtor é um objeto TimeSpan que


representa o deslocamento do tempo de UTC. Recupere-o chamando o método
TimeZoneInfo.GetUtcOffset(DateTime) do fuso horário correspondente. O único
parâmetro do método é o valor DateTime, que representa a data e a hora a serem
convertidas. Se o fuso horário der suporte ao horário de verão, este parâmetro permite
que o método determine o deslocamento apropriado para aquela data e hora
determinada.

Conversões de DateTimeOffset em DateTime


É mais comum usar a propriedade DateTime para executar a conversão de
DateTimeOffset em DateTime. No entanto, essa ação retorna um valor DateTime, cuja
propriedade Kind é Unspecified, conforme mostrado no exemplo a seguir:

C#

DateTime baseTime = new DateTime(2008, 6, 19, 7, 0, 0);


DateTimeOffset sourceTime;
DateTime targetTime;

// Convert UTC to DateTime value


sourceTime = new DateTimeOffset(baseTime, TimeSpan.Zero);
targetTime = sourceTime.DateTime;
Console.WriteLine("{0} converts to {1} {2}",
sourceTime,
targetTime,
targetTime.Kind);

// Convert local time to DateTime value


sourceTime = new DateTimeOffset(baseTime,
TimeZoneInfo.Local.GetUtcOffset(baseTime));
targetTime = sourceTime.DateTime;
Console.WriteLine("{0} converts to {1} {2}",
sourceTime,
targetTime,
targetTime.Kind);

// Convert Central Standard Time to a DateTime value


try
{
TimeSpan offset = TimeZoneInfo.FindSystemTimeZoneById("Central Standard
Time").GetUtcOffset(baseTime);
sourceTime = new DateTimeOffset(baseTime, offset);
targetTime = sourceTime.DateTime;
Console.WriteLine("{0} converts to {1} {2}",
sourceTime,
targetTime,
targetTime.Kind);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to create DateTimeOffset based on U.S. Central
Standard Time.");
}
// This example displays the following output to the console:
// 6/19/2008 7:00:00 AM +00:00 converts to 6/19/2008 7:00:00 AM
Unspecified
// 6/19/2008 7:00:00 AM -07:00 converts to 6/19/2008 7:00:00 AM
Unspecified
// 6/19/2008 7:00:00 AM -05:00 converts to 6/19/2008 7:00:00 AM
Unspecified

O exemplo anterior mostra que qualquer informação sobre a relação do valor


DateTimeOffset com o UTC é perdida pela conversão quando a propriedade DateTime é
usada. Esse comportamento também afeta valores DateTimeOffset que correspondem à
hora UTC ou hora local do sistema, pois a estrutura DateTime reflete somente esses dois
fusos horários na propriedade Kind.

Para preservar o máximo possível de informações do fuso horário, ao converter um


valor DateTimeOffset em DateTime, você pode usar as propriedades
DateTimeOffset.UtcDateTime e DateTimeOffset.LocalDateTime.

Conversão de uma hora UTC


Para indicar que um valor DateTime convertido é a hora UTC, você pode recuperar o
valor da propriedade DateTimeOffset.UtcDateTime. Esse valor difere da propriedade
DateTime de duas maneiras:

Ele retorna um valor DateTime, cuja propriedade Kind é Utc.

Se o valor da propriedade Offset não for igual a TimeSpan.Zero, ele converte a


hora em UTC.

7 Observação

Se o aplicativo exigir que os valores DateTime convertidos identifiquem sem


ambiguidade um único ponto no tempo, use a propriedade
DateTimeOffset.UtcDateTime para lidar com todas as conversões de
DateTimeOffset em DateTime.

O código a seguir usar a propriedade UtcDateTime para converter um valor


DateTimeOffset, cuja diferença é igual a TimeSpan.Zero para um valor DateTime:

C#

DateTimeOffset utcTime1 = new DateTimeOffset(2008, 6, 19, 7, 0, 0,


TimeSpan.Zero);
DateTime utcTime2 = utcTime1.UtcDateTime;
Console.WriteLine("{0} converted to {1} {2}",
utcTime1,
utcTime2,
utcTime2.Kind);
// The example displays the following output to the console:
// 6/19/2008 7:00:00 AM +00:00 converted to 6/19/2008 7:00:00 AM Utc

O código a seguir usa a propriedade UtcDateTime para executar uma conversão de fuso
horário e uma conversão de tipo em um valor DateTimeOffset:

C#

DateTimeOffset originalTime = new DateTimeOffset(2008, 6, 19, 7, 0, 0, new


TimeSpan(5, 0, 0));
DateTime utcTime = originalTime.UtcDateTime;
Console.WriteLine("{0} converted to {1} {2}",
originalTime,
utcTime,
utcTime.Kind);
// The example displays the following output to the console:
// 6/19/2008 7:00:00 AM +05:00 converted to 6/19/2008 2:00:00 AM Utc
Conversão de uma hora Local
Para indicar que um valor DateTimeOffset representa a hora local, você pode passar o
valor DateTime retornado pela propriedade DateTimeOffset.DateTime para o static
( Shared no Visual Basic) do método SpecifyKind. O método retorna a data e a hora
passadas para ele como o primeiro parâmetro, mas define a propriedade Kind como o
valor especificado pelo segundo parâmetro. O código a seguir usa o método
SpecifyKind ao converter um valor DateTimeOffset, cuja diferença corresponde à do
fuso horário local:

C#

DateTime sourceDate = new DateTime(2008, 6, 19, 7, 0, 0);


DateTimeOffset utcTime1 = new DateTimeOffset(sourceDate,
TimeZoneInfo.Local.GetUtcOffset(sourceDate));
DateTime utcTime2 = utcTime1.DateTime;
if
(utcTime1.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(utcTime1.DateTime)))
utcTime2 = DateTime.SpecifyKind(utcTime2, DateTimeKind.Local);

Console.WriteLine("{0} converted to {1} {2}",


utcTime1,
utcTime2,
utcTime2.Kind);
// The example displays the following output to the console:
// 6/19/2008 7:00:00 AM -07:00 converted to 6/19/2008 7:00:00 AM Local

Você também pode usar a propriedade DateTimeOffset.LocalDateTime para converter


um valor DateTimeOffset em um valor DateTime local. A propriedade Kind do valor
DateTime retornado é Local. O código a seguir usa a propriedade
DateTimeOffset.LocalDateTime ao converter um valor DateTimeOffset, cuja diferença
corresponde à do fuso horário local:

C#

DateTime sourceDate = new DateTime(2008, 6, 19, 7, 0, 0);


DateTimeOffset localTime1 = new DateTimeOffset(sourceDate,
TimeZoneInfo.Local.GetUtcOffset(sourceDate));
DateTime localTime2 = localTime1.LocalDateTime;

Console.WriteLine("{0} converted to {1} {2}",


localTime1,
localTime2,
localTime2.Kind);
// The example displays the following output to the console:
// 6/19/2008 7:00:00 AM -07:00 converted to 6/19/2008 7:00:00 AM Local
Quando você recupera um valor DateTime usando a propriedade
DateTimeOffset.LocalDateTime, o acessador get da propriedade converte primeiro o
valor DateTimeOffset em UTC e depois converte em hora local, chamando o método
ToLocalTime. Esse comportamento significa que você pode recuperar um valor a partir a
propriedade DateTimeOffset.LocalDateTime para executar uma conversão de fuso
horário, ao mesmo tempo em que executa uma conversão de tipo. Isso também
significa que as regras de ajuste do fuso horário local são aplicadas na execução da
conversão. O código a seguir ilustra o uso da propriedade
DateTimeOffset.LocalDateTime para executar uma conversão de tipo e de fuso horário.
A saída de exemplo é para um computador definido como o Fuso Horário do Pacífico
(EUA e Canadá). A data de novembro é Hora Padrão do Pacífico, que é UTC-8, enquanto
a data de junho é o Horário de Verão, que é UTC-7.

C#

DateTimeOffset originalDate;
DateTime localDate;

// Convert time originating in a different time zone


originalDate = new DateTimeOffset(2008, 6, 18, 7, 0, 0,
new TimeSpan(-5, 0, 0));
localDate = originalDate.LocalDateTime;
Console.WriteLine("{0} converted to {1} {2}",
originalDate,
localDate,
localDate.Kind);
// Convert time originating in a different time zone
// so local time zone's adjustment rules are applied
originalDate = new DateTimeOffset(2007, 11, 4, 4, 0, 0,
new TimeSpan(-5, 0, 0));
localDate = originalDate.LocalDateTime;
Console.WriteLine("{0} converted to {1} {2}",
originalDate,
localDate,
localDate.Kind);
// The example displays the following output to the console,
// when you run it on a machine that is set to Pacific Time (US & Canada):
// 6/18/2008 7:00:00 AM -05:00 converted to 6/18/2008 5:00:00 AM Local
// 11/4/2007 4:00:00 AM -05:00 converted to 11/4/2007 1:00:00 AM Local

Um método de conversão de uso geral


O exemplo a seguir define um método chamado ConvertFromDateTimeOffset , que
converte os valores DateTimeOffset em DateTime. Com base na diferença, ele determina
se o valor DateTimeOffset é uma hora UTC, uma hora local ou outro tipo de hora e
define a propriedade Kind do valor de data e hora retornado de acordo.
C#

static DateTime ConvertFromDateTimeOffset(DateTimeOffset dateTime)


{
if (dateTime.Offset.Equals(TimeSpan.Zero))
return dateTime.UtcDateTime;
else if
(dateTime.Offset.Equals(TimeZoneInfo.Local.GetUtcOffset(dateTime.DateTime)))
return DateTime.SpecifyKind(dateTime.DateTime, DateTimeKind.Local);
else
return dateTime.DateTime;
}

O exemplo a seguir chama o método ConvertFromDateTimeOffset para converter valores


DateTimeOffset que representam uma hora UTC, uma hora local e uma hora no fuso
horário Padrão Central dos E.U.A.

C#

DateTime timeComponent = new DateTime(2008, 6, 19, 7, 0, 0);


DateTime returnedDate;

// Convert UTC time


DateTimeOffset utcTime = new DateTimeOffset(timeComponent, TimeSpan.Zero);
returnedDate = ConvertFromDateTimeOffset(utcTime);
Console.WriteLine("{0} converted to {1} {2}",
utcTime,
returnedDate,
returnedDate.Kind);

// Convert local time


DateTimeOffset localTime = new DateTimeOffset(timeComponent,
TimeZoneInfo.Local.GetUtcOffset(timeComponent));
returnedDate = ConvertFromDateTimeOffset(localTime);
Console.WriteLine("{0} converted to {1} {2}",
localTime,
returnedDate,
returnedDate.Kind);

// Convert Central Standard Time


DateTimeOffset cstTime = new DateTimeOffset(timeComponent,
TimeZoneInfo.FindSystemTimeZoneById("Central Standard
Time").GetUtcOffset(timeComponent));
returnedDate = ConvertFromDateTimeOffset(cstTime);
Console.WriteLine("{0} converted to {1} {2}",
cstTime,
returnedDate,
returnedDate.Kind);
// The example displays the following output to the console:
// 6/19/2008 7:00:00 AM +00:00 converted to 6/19/2008 7:00:00 AM Utc
// 6/19/2008 7:00:00 AM -07:00 converted to 6/19/2008 7:00:00 AM Local
// 6/19/2008 7:00:00 AM -05:00 converted to 6/19/2008 7:00:00 AM
Unspecified

7 Observação

Esse código faz as duas suposições a seguir que, dependendo do aplicativo e da


fonte de seus valores de data e hora, podem não ser sempre válidas:

Ele supõe que um valor de data e hora cuja diferença é TimeSpan.Zero


representa o UTC. Na verdade, UTC não é uma hora em um determinado fuso
horário, mas a hora em relação à qual as horas nos fusos horários do mundo
são padronizadas. Os fusos horários também podem ter uma diferença de
Zero.

Ele supõe que uma data e hora cujo deslocamento é igual ao do fuso horário
local representa o fuso horário local. Como os valores de data e hora são
dissociados do seu fuso horário original, este pode não ser o caso. A data e
hora podem ter sido originadas em outro fuso horário com o mesmo
deslocamento.

Confira também
Datas, horas e fusos horários

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Convertendo horários entre fusos
horários
Artigo • 09/05/2023

Está se tornando cada vez mais importante para qualquer aplicativo que trabalha com
datas e horas lidar com diferenças entre fusos horários. Os aplicativos não podem mais
presumir que todos os horários podem ser expressos na hora local, que é a hora
disponível na estrutura DateTime. Por exemplo, uma página da Web que exibe a hora
atual no leste dos Estados Unidos não terá credibilidade para um cliente no leste da
Ásia. Este artigo explica como converter horas de um fuso horário para outro, bem
como converter valores de DateTimeOffset que têm percepção limitada de fuso horário.

Convertendo para o Tempo Universal


Coordenado
O UTC (Tempo Universal Coordenado) é um padrão de tempo atômico de alta precisão.
Os fusos horários do mundo são expressos como deslocamentos positivos ou negativos
com relação ao UTC. Sendo assim, o UTC fornece uma hora livre de fuso horário ou com
fuso horário neutro. O uso do UTC é recomendado quando a portabilidade da data e
hora entre computadores é importante. Para detalhes e outras práticas recomendadas
usando datas e horas, confira Melhores práticas de codificação usando DateTime no
.NET Framework. Converter fusos horários individuais em UTC facilita comparações de
hora.

7 Observação

Você também pode serializar uma estrutura DateTimeOffset para representar sem
ambiguidade um único ponto no tempo. Uma vez que objetos DateTimeOffset
armazenam um valor de data e hora com seu deslocamento com relação ao UTC,
eles sempre representam um ponto específico no tempo em relação ao UTC.

A maneira mais fácil de converter uma hora em UTC é chamar o método


TimeZoneInfo.ConvertTimeToUtc(DateTime) static ( Shared no Visual Basic). A
conversão exata executada pelo método depende do valor da propriedade Kind do
parâmetro dateTime , conforme é mostrado na tabela a seguir:
DateTime.Kind Conversão

DateTimeKind.Local Converte a hora local para UTC.

DateTimeKind.Unspecified Presume que o parâmetro dateTime é a hora local e converte a hora


local para UTC.

DateTimeKind.Utc Retorna o parâmetro dateTime inalterado.

O código a seguir converte a hora local atual para UTC e exibe o resultado no console:

C#

DateTime dateNow = DateTime.Now;


Console.WriteLine("The date and time are {0} UTC.",
TimeZoneInfo.ConvertTimeToUtc(dateNow));

Se o valor de data e hora não representar a hora local ou o UTC, o método


ToUniversalTime provavelmente retornará um resultado com erro. No entanto, você
pode usar o método TimeZoneInfo.ConvertTimeToUtc para converter a data e hora de
um fuso horário especificado. Para obter detalhes sobre como recuperar um objeto
TimeZoneInfo que representa o fuso horário de destino, confira Encontrando os fusos
horários definidos em um sistema local. O código a seguir usa o método
TimeZoneInfo.ConvertTimeToUtc para converter a Zona de Tempo Oriental para UTC:

C#

DateTime easternTime = new DateTime(2007, 01, 02, 12, 16, 00);


string easternZoneId = "Eastern Standard Time";
try
{
TimeZoneInfo easternZone =
TimeZoneInfo.FindSystemTimeZoneById(easternZoneId);
Console.WriteLine("The date and time are {0} UTC.",
TimeZoneInfo.ConvertTimeToUtc(easternTime,
easternZone));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("Unable to find the {0} zone in the registry.",
easternZoneId);
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the {0} zone has been corrupted.",
easternZoneId);
}
O método TimeZoneInfo.ConvertTimeToUtc gera uma ArgumentException se a
propriedade Kind do objeto DateTime e o fuso horário não corresponderem. Uma
incompatibilidade ocorre se a propriedade Kind é DateTimeKind.Local, mas o objeto
TimeZoneInfo não representa o fuso horário local ou se a propriedade Kind é
DateTimeKind.Utc, mas o objeto TimeZoneInfo não é igual a TimeZoneInfo.Utc.

Todos esses métodos usam valores de DateTime como parâmetros e retornam um valor
de DateTime. Para valores de DateTimeOffset, a estrutura DateTimeOffset tem um
método de instância ToUniversalTime que converte a data e hora da instância atual em
UTC. O exemplo a seguir chama o método ToUniversalTime para converter uma hora
local e várias outras horas para UTC:

C#

DateTimeOffset localTime, otherTime, universalTime;

// Define local time in local time zone


localTime = new DateTimeOffset(new DateTime(2007, 6, 15, 12, 0, 0));
Console.WriteLine("Local time: {0}", localTime);
Console.WriteLine();

// Convert local time to offset 0 and assign to otherTime


otherTime = localTime.ToOffset(TimeSpan.Zero);
Console.WriteLine("Other time: {0}", otherTime);
Console.WriteLine("{0} = {1}: {2}",
localTime, otherTime,
localTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
localTime, otherTime,
localTime.EqualsExact(otherTime));
Console.WriteLine();

// Convert other time to UTC


universalTime = localTime.ToUniversalTime();
Console.WriteLine("Universal time: {0}", universalTime);
Console.WriteLine("{0} = {1}: {2}",
otherTime, universalTime,
universalTime.Equals(otherTime));
Console.WriteLine("{0} exactly equals {1}: {2}",
otherTime, universalTime,
universalTime.EqualsExact(otherTime));
Console.WriteLine();
// The example produces the following output to the console:
// Local time: 6/15/2007 12:00:00 PM -07:00
//
// Other time: 6/15/2007 7:00:00 PM +00:00
// 6/15/2007 12:00:00 PM -07:00 = 6/15/2007 7:00:00 PM +00:00: True
// 6/15/2007 12:00:00 PM -07:00 exactly equals 6/15/2007 7:00:00 PM
+00:00: False
//
// Universal time: 6/15/2007 7:00:00 PM +00:00
// 6/15/2007 7:00:00 PM +00:00 = 6/15/2007 7:00:00 PM +00:00: True
// 6/15/2007 7:00:00 PM +00:00 exactly equals 6/15/2007 7:00:00 PM
+00:00: True

Convertendo UTC em um fuso horário


designado
Para converter UTC em hora local, consulte a seção Convertendo UTC em hora local a
seguir. Para converter de UTC na hora correspondente em qualquer fuso horário que
você designar, chame o método ConvertTimeFromUtc. O método utiliza dois
parâmetros:

O UTC a ser convertido. Esse deve ser um valor DateTime cuja propriedade Kind
está definida como Unspecified ou Utc .

O fuso horário no qual o UTC deve ser convertido.

O código a seguir converte o UTC para o Horário Padrão Central:

C#

DateTime timeUtc = DateTime.UtcNow;


try
{
TimeZoneInfo cstZone = TimeZoneInfo.FindSystemTimeZoneById("Central
Standard Time");
DateTime cstTime = TimeZoneInfo.ConvertTimeFromUtc(timeUtc, cstZone);
Console.WriteLine("The date and time are {0} {1}.",
cstTime,
cstZone.IsDaylightSavingTime(cstTime) ?
cstZone.DaylightName : cstZone.StandardName);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Central Standard Time
zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Central Standard Time zone has
been corrupted.");
}

Convertendo UTC em hora local


Para converter UTC para a hora local, chame o método ToLocalTime do objeto DateTime
cuja hora você deseja converter. O comportamento exato do método depende do valor
da propriedade Kind do objeto, conforme mostrado na tabela a seguir:

DateTime.Kind Conversão

DateTimeKind.Local Retorna o valor DateTime inalterado.

DateTimeKind.Unspecified Pressupõe que o valor DateTime está no UTC e o converte do UTC


para a hora local.

DateTimeKind.Utc Converte o valor DateTime na hora local.

7 Observação

O método TimeZone.ToLocalTime se comporta de maneira idêntica ao método


DateTime.ToLocalTime . Ele usa um só parâmetro, que é o valor de data e hora a ser

convertido.

Você também pode converter a hora em qualquer fuso horário designado em hora local
usando o método static ( Shared no Visual Basic TimeZoneInfo.ConvertTime ). Esta
técnica é abordado na próxima seção.

Convertendo entre dois fusos horários


Você pode converter entre dois fusos horários usando um dos dois seguintes métodos
static ( Shared no Visual Basic) da classe TimeZoneInfo:

ConvertTime

Os parâmetros desse método são o valor de data e hora a ser convertido, um


objeto TimeZoneInfo que representa o fuso horário do valor de data e hora e um
objeto TimeZoneInfo que representa o fuso horário no qual o valor de data e hora
deverá ser convertido.

ConvertTimeBySystemTimeZoneId

Os parâmetros desse método são o valor de data e hora a ser convertido, o


identificador do fuso horário do valor de data e hora e o identificador do fuso
horário para o qual converter o valor de data e hora.

Ambos os métodos exigem que a propriedade Kind do valor de data e hora a ser
convertido e o objeto TimeZoneInfo ou o identificador de fuso horário que representa
seu fuso horário sejam correspondentes. Caso contrário, um ArgumentException será
gerado. Por exemplo, se a propriedade Kind do valor de data e hora for
DateTimeKind.Local , uma exceção será lançada se o objeto TimeZoneInfo passado como

um parâmetro para o método não for igual a TimeZoneInfo.Local . Também é gerada


uma exceção se o identificador passado como parâmetro para o método não for igual a
TimeZoneInfo.Local.Id .

O exemplo a seguir usa o método ConvertTime para converter da Hora Oficial do Havaí
para a hora local:

C#

DateTime hwTime = new DateTime(2007, 02, 01, 08, 00, 00);


try
{
TimeZoneInfo hwZone = TimeZoneInfo.FindSystemTimeZoneById("Hawaiian
Standard Time");
Console.WriteLine("{0} {1} is {2} local time.",
hwTime,
hwZone.IsDaylightSavingTime(hwTime) ? hwZone.DaylightName :
hwZone.StandardName,
TimeZoneInfo.ConvertTime(hwTime, hwZone, TimeZoneInfo.Local));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The registry does not define the Hawaiian Standard
Time zone.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Registry data on the Hawaiian Standard Time zone has
been corrupted.");
}

Convertendo valores de DateTimeOffset


Valores de data e hora representados por objetos DateTimeOffset não são totalmente
cientes do fuso horário porque o objeto é desassociado de seu fuso horário no
momento em que é instanciado. No entanto, em muitos casos o aplicativo precisa
apenas converter uma data e hora com base em dois deslocamentos diferentes do UTC,
em vez de na hora em fusos horários específicos. Para realizar essa conversão, você
pode chamar o método ToOffset da instância atual. O parâmetro único do método é o
deslocamento do novo valor de data e hora que o método retornará.

Por exemplo, se a data e hora da solicitação de um usuário de uma página da Web for
conhecida e for serializada como uma cadeia de caracteres no formato MM/dd/aaaa
hh:mm:ss zzzz, o seguinte método ReturnTimeOnServer converte esse valor de data e
hora para a data e hora no servidor Web:

C#

public DateTimeOffset ReturnTimeOnServer(string clientString)


{
string format = @"M/d/yyyy H:m:s zzz";
TimeSpan serverOffset =
TimeZoneInfo.Local.GetUtcOffset(DateTimeOffset.Now);

try
{
DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString,
format, CultureInfo.InvariantCulture);
DateTimeOffset serverTime = clientTime.ToOffset(serverOffset);
return serverTime;
}
catch (FormatException)
{
return DateTimeOffset.MinValue;
}
}

Se o método passar a cadeia de caracteres "9/1/2007 5:32:07 -05:00," representando a


data e hora em um fuso horário cinco horas anteriores ao UTC, ele retornará “9/1/2007
3:32:07 AM -07:00” para um servidor localizado no fuso horário da Hora Padrão do
Pacífico dos EUA.

A classe TimeZoneInfo também inclui uma sobrecarga do método


TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo) que realiza conversões de
fuso horário com valores de ToOffset(TimeSpan). Os parâmetros do método são um
valor de DateTimeOffset e uma referência ao fuso horário no qual a hora será
convertida. A chamada de método retorna um valor DateTimeOffset. Por exemplo, o
método ReturnTimeOnServer no exemplo anterior poderia ser reescrito como segue para
chamar o método ConvertTime(DateTimeOffset, TimeZoneInfo).

C#

public DateTimeOffset ReturnTimeOnServer(string clientString)


{
string format = @"M/d/yyyy H:m:s zzz";

try
{
DateTimeOffset clientTime = DateTimeOffset.ParseExact(clientString,
format,
CultureInfo.InvariantCulture);
DateTimeOffset serverTime = TimeZoneInfo.ConvertTime(clientTime,
TimeZoneInfo.Local);
return serverTime;
}
catch (FormatException)
{
return DateTimeOffset.MinValue;
}
}

Confira também
TimeZoneInfo
Datas, horas e fusos horários
Encontrando os fusos horários definidos em um sistema local

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: resolver horários ambíguos
Artigo • 09/05/2023

Um horário ambíguo é um horário que aponta para mais de um UTC (Tempo Universal
Coordenado). Ocorre quando o horário do relógio é atrasado, como durante a transição
do horário de verão de um fuso horário para seu horário padrão. Ao processar um
horário ambíguo, você pode executar uma das seguintes ações:

Faça uma suposição de como o tempo aponta para o UTC. Por exemplo, você pode
supor que um horário ambíguo é sempre expresso no horário padrão do fuso
horário.

Se o horário ambíguo for um item de dados inserido pelo usuário, você pode
deixar que o usuário resolva a ambiguidade.

Este tópico mostra como resolver um horário ambíguo supondo que ele representa o
horário padrão do fuso horário.

Para apontar um horário ambíguo para o horário padrão


de um fuso horário
1. Chame o método IsAmbiguousTime para determinar se o horário é ambíguo.

2. Se o horário for ambíguo, subtraia o horário do objeto TimeSpan retornado pela


propriedade BaseUtcOffset do fuso horário.

3. Chame o método static ( Shared no Visual Basic .NET) SpecifyKind para definir a
propriedade Kind do valor de data e de hora UTC como DateTimeKind.Utc.

Exemplo
O exemplo a seguir mostra como converter um horário ambíguo em UTC supondo que
representa o horário padrão do fuso horário local.

C#

private DateTime ResolveAmbiguousTime(DateTime ambiguousTime)


{
// Time is not ambiguous
if (! TimeZoneInfo.Local.IsAmbiguousTime(ambiguousTime))
{
return ambiguousTime;
}
// Time is ambiguous
else
{
DateTime utcTime = DateTime.SpecifyKind(ambiguousTime -
TimeZoneInfo.Local.BaseUtcOffset,
DateTimeKind.Utc);
Console.WriteLine("{0} local time corresponds to {1} {2}.",
ambiguousTime, utcTime, utcTime.Kind.ToString());
return utcTime;
}
}

O exemplo consiste em um método chamado ResolveAmbiguousTime que determina se o


valor DateTime passado para ele é ambíguo. Se o valor for ambíguo, o método retorna
um valor DateTime que representa o horário UTC correspondente. O método trata essa
conversão subtraindo do horário local o valor da propriedade BaseUtcOffset do fuso
horário local.

Normalmente, um horário ambíguo é tratado chamando o método


GetAmbiguousTimeOffsets para recuperar uma matriz de objetos TimeSpan que contêm
as possíveis diferenças de UTC do horário ambíguo. No entanto, esse exemplo faz a
suposição arbitrária de que um horário ambíguo deve sempre apontar para o horário
padrão do fuso horário. A propriedade BaseUtcOffset retorna a diferença entre o UTC e
o horário padrão de um fuso horário.

Neste exemplo, todas as referências ao fuso horário local são feitas através da
propriedade TimeZoneInfo.Local; o fuso horário local nunca é atribuído a uma variável
de objeto. Essa é uma prática recomendada, porque uma chamada ao método
TimeZoneInfo.ClearCachedData invalida todos os objetos aos quais o fuso horário local
esteja atribuído.

Compilando o código
Este exemplo requer:

Que o namespace System seja importado com a instrução using (necessária no


código C#).

Confira também
Datas, horas e fusos horários
Como: permitir que os usuários resolvam horários ambíguos
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: permitir que os usuários
resolvam horários ambíguos
Artigo • 10/05/2023

Um horário ambíguo é um horário que aponta para mais de um UTC (Tempo Universal
Coordenado). Ocorre quando o horário do relógio é atrasado, como durante a transição
do horário de verão de um fuso horário para seu horário padrão. Ao processar um
horário ambíguo, você pode executar uma das seguintes ações:

Se o horário ambíguo for um item de dados inserido pelo usuário, você pode
deixar que o usuário resolva a ambiguidade.

Faça uma suposição de como o tempo aponta para o UTC. Por exemplo, você pode
supor que um horário ambíguo é sempre expresso no horário padrão do fuso
horário.

Este tópico mostra como permitir que um usuário resolva uma 'hora ambígua.

Permitir que o usuário resolva um horário ambíguo


1. Obtenha a entrada de data e hora do usuário.

2. Chame o método IsAmbiguousTime para determinar se a hora é ambígua.

3. Se a hora for ambígua, chame o método GetAmbiguousTimeOffsets para


recuperar uma matriz de objetos TimeSpan. Cada elemento na matriz contém uma
diferença UTC para a qual o tempo ambíguo pode ser mapeado.

4. Permita que o usuário selecione o deslocamento desejado.

5. Obtenha a data e hora de UTC subtraindo o deslocamento selecionado pelo


usuário do horário local.

6. Chame o método static ( Shared no Visual Basic .NET) SpecifyKind para definir a
propriedade Kind do valor de data e de hora UTC como DateTimeKind.Utc.

Exemplo
O exemplo a seguir solicita que o usuário insira uma data e hora e, se ela for ambígua,
permite que o usuário selecione o horário UTC para o qual o horário ambíguo aponta.

C#
private void GetUserDateInput()
{
// Get date and time from user
DateTime inputDate = GetUserDateTime();
DateTime utcDate;

// Exit if date has no significant value


if (inputDate == DateTime.MinValue) return;

if (TimeZoneInfo.Local.IsAmbiguousTime(inputDate))
{
Console.WriteLine("The date you've entered is ambiguous.");
Console.WriteLine("Please select the correct offset from Universal
Coordinated Time:");
TimeSpan[] offsets =
TimeZoneInfo.Local.GetAmbiguousTimeOffsets(inputDate);
for (int ctr = 0; ctr < offsets.Length; ctr++)
{
Console.WriteLine("{0}.) {1} hours, {2} minutes", ctr,
offsets[ctr].Hours, offsets[ctr].Minutes);
}
Console.Write("> ");
int selection = Convert.ToInt32(Console.ReadLine());

// Convert local time to UTC, and set Kind property to


DateTimeKind.Utc
utcDate = DateTime.SpecifyKind(inputDate - offsets[selection],
DateTimeKind.Utc);

Console.WriteLine("{0} local time corresponds to {1} {2}.", inputDate,


utcDate, utcDate.Kind.ToString());
}
else
{
utcDate = inputDate.ToUniversalTime();
Console.WriteLine("{0} local time corresponds to {1} {2}.", inputDate,
utcDate, utcDate.Kind.ToString());
}
}

private DateTime GetUserDateTime()


{
bool exitFlag = false; // flag to exit loop if date is valid
string dateString;
DateTime inputDate = DateTime.MinValue;

Console.Write("Enter a local date and time: ");


while (! exitFlag)
{
dateString = Console.ReadLine();
if (dateString.ToUpper() == "E")
exitFlag = true;

if (DateTime.TryParse(dateString, out inputDate))


exitFlag = true;
else
Console.Write("Enter a valid date and time, or enter 'e' to exit:
");
}

return inputDate;
}

VB

Private Sub GetUserDateInput()


' Get date and time from user
Dim inputDate As Date = GetUserDateTime()
Dim utcDate As Date

' Exit if date has no significant value


If inputDate = Date.MinValue Then Exit Sub

If TimeZoneInfo.Local.IsAmbiguousTime(inputDate) Then
Console.WriteLine("The date you've entered is ambiguous.")
Console.WriteLine("Please select the correct offset from Universal
Coordinated Time:")
Dim offsets() As TimeSpan =
TimeZoneInfo.Local.GetAmbiguousTimeOffsets(inputDate)
For ctr As Integer = 0 to offsets.Length - 1
Dim zoneDescription As String
If offsets(ctr).Equals(TimeZoneInfo.Local.BaseUtcOffset) Then
zoneDescription = TimeZoneInfo.Local.StandardName
Else
zoneDescription = TimeZoneInfo.Local.DaylightName
End If
Console.WriteLine("{0}.) {1} hours, {2} minutes ({3})", _
ctr, offsets(ctr).Hours, offsets(ctr).Minutes,
zoneDescription)
Next
Console.Write("> ")
Dim selection As Integer = CInt(Console.ReadLine())

' Convert local time to UTC, and set Kind property to


DateTimeKind.Utc
utcDate = Date.SpecifyKind(inputDate - offsets(selection),
DateTimeKind.Utc)

Console.WriteLine("{0} local time corresponds to {1} {2}.",


inputDate, utcDate, utcDate.Kind.ToString())
Else
utcDate = inputDate.ToUniversalTime()
Console.WriteLine("{0} local time corresponds to {1} {2}.",
inputDate, utcDate, utcDate.Kind.ToString())
End If
End Sub
Private Function GetUserDateTime() As Date
Dim exitFlag As Boolean = False ' flag to exit loop if date
is valid
Dim dateString As String
Dim inputDate As Date = Date.MinValue

Console.Write("Enter a local date and time: ")


Do While Not exitFlag
dateString = Console.ReadLine()
If dateString.ToUpper = "E" Then exitFlag = True
If Date.TryParse(dateString, inputDate) Then
exitFlag = true
Else
Console.Write("Enter a valid date and time, or enter 'e' to
exit: ")
End If
Loop

Return inputDate
End Function

O núcleo do código de exemplo usa uma matriz de objetos TimeSpan para indicar
possíveis diferenças da hora ambígua do UTC. No entanto, esses deslocamentos
provavelmente não serão significativos para o usuário. Para esclarecer o significado dos
deslocamentos, o código também observa se um deslocamento representa o horário
padrão do fuso horário local ou seu horário de verão. O código determina qual hora é
padrão e qual hora é a de verão comparando a diferença com o valor da propriedade
BaseUtcOffset. Essa propriedade indica a diferença entre o UTC e o horário padrão do
fuso horário.

Neste exemplo, todas as referências ao fuso horário local são feitas por meio da
propriedade TimeZoneInfo.Local. O fuso horário local nunca é atribuído a uma variável
de objeto. Essa é uma prática recomendada, porque uma chamada ao método
TimeZoneInfo.ClearCachedData invalida todos os objetos aos quais o fuso horário local
esteja atribuído.

Compilando o código
Este exemplo requer:

Que o namespace System seja importado com a instrução using (necessária no


código C#).

Confira também
Datas, horas e fusos horários
Como: resolver horários ambíguos

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Criando uma instância de um objeto
DateTimeOffset
Artigo • 02/06/2023

A estrutura DateTimeOffset oferece várias maneiras de criar novos valores


DateTimeOffset. Muitos deles correspondem diretamente aos métodos disponíveis para
instanciar novos valores DateTime, com aprimoramentos que permitem especificar o
deslocamento do valor de data e hora do Tempo Universal Coordenado (UTC). Em
particular, você pode instanciar um valor DateTimeOffset das seguintes maneiras:

Usando um literal de data e hora.

Chamando um construtor DateTimeOffset.

Convertendo implicitamente um valor para o valor DateTimeOffset.

Analisando a representação de cadeia de caracteres de data e hora.

Este tópico fornece mais detalhes e exemplos de código que ilustram esses métodos de
instanciação de novos valores DateTimeOffset.

Literais de data e hora


Para linguagens compatíveis, uma das maneiras mais comuns de instanciar um valor
DateTime é fornecer a data e a hora como um valor literal codificado. Por exemplo, o
código Visual Basic a seguir cria um objeto DateTime cujo valor é 1º de maio de 2008,
às 8:06:32 AM.

VB

Dim literalDate1 As Date = #05/01/2008 8:06:32 AM#


Console.WriteLine(literalDate1.ToString())
' Displays:
' 5/1/2008 8:06:32 AM

Os valores DateTimeOffset também podem ser inicializados usando literais de data e


hora ao usar linguagens que suportam literais DateTime. Por exemplo, o código Visual
Basic a seguir cria um objeto DateTimeOffset.

VB
Dim literalDate As DateTimeOffset = #05/01/2008 8:06:32 AM#
Console.WriteLine(literalDate.ToString())
' Displays:
' 5/1/2008 8:06:32 AM -07:00

Como a saída do console mostra, o valor DateTimeOffset criado dessa maneira recebe o
deslocamento do fuso horário local. Isso significa que um valor DateTimeOffset
atribuído usando um literal de caractere não identifica um único ponto no tempo se o
código for executado em computadores diferentes.

Construtores DateTimeOffset
O tipo DateTimeOffset define seis construtores. Quatro deles correspondem
diretamente aos construtores DateTime, com um parâmetro adicional do tipo TimeSpan
que define o deslocamento de data e hora do UTC. Eles permitem que você defina um
valor DateTimeOffset com base no valor de seus componentes individuais de data e
hora. Por exemplo, o código a seguir usa esses quatro construtores para criar uma
instância de objetos DateTimeOffset com valores idênticos de 01/05/2008 8:06:32
+01:00.

C#

DateTimeOffset dateAndTime;

// Instantiate date and time using years, months, days,


// hours, minutes, and seconds
dateAndTime = new DateTimeOffset(2008, 5, 1, 8, 6, 32,
new TimeSpan(1, 0, 0));
Console.WriteLine(dateAndTime);
// Instantiate date and time using years, months, days,
// hours, minutes, seconds, and milliseconds
dateAndTime = new DateTimeOffset(2008, 5, 1, 8, 6, 32, 545,
new TimeSpan(1, 0, 0));
Console.WriteLine("{0} {1}", dateAndTime.ToString("G"),
dateAndTime.ToString("zzz"));

// Instantiate date and time using Persian calendar with years,


// months, days, hours, minutes, seconds, and milliseconds
dateAndTime = new DateTimeOffset(1387, 2, 12, 8, 6, 32, 545,
new PersianCalendar(),
new TimeSpan(1, 0, 0));
// Note that the console output displays the date in the Gregorian
// calendar, not the Persian calendar.
Console.WriteLine("{0} {1}", dateAndTime.ToString("G"),
dateAndTime.ToString("zzz"));

// Instantiate date and time using number of ticks


// 05/01/2008 8:06:32 AM is 633,452,259,920,000,000 ticks
dateAndTime = new DateTimeOffset(633452259920000000, new TimeSpan(1, 0, 0));
Console.WriteLine(dateAndTime);
// The example displays the following output to the console:
// 5/1/2008 8:06:32 AM +01:00
// 5/1/2008 8:06:32 AM +01:00
// 5/1/2008 8:06:32 AM +01:00
// 5/1/2008 8:06:32 AM +01:00

Observe que, quando o valor do objeto DateTimeOffset instanciado usando um objeto


PersianCalendar como um dos argumentos para seu construtor é exibido no console, ele
é expresso como uma data no calendário gregoriano em vez do calendário persa. Para
gerar uma data usando o calendário persa, consulte o exemplo no tópico
PersianCalendar.

Os outros dois construtores criam um objeto DateTimeOffset a partir de um valor


DateTime. O primeiro deles tem um único parâmetro, o valor DateTime para converter
em um valor DateTimeOffset. O deslocamento do valor DateTimeOffset resultante
depende da propriedade Kind do parâmetro único do construtor. Se seu valor for
DateTimeKind.Utc, o deslocamento será igual a TimeSpan.Zero. Caso contrário, o
deslocamento será definido como igual ao fuso horário local. O exemplo a seguir ilustra
o uso desse construtor para instanciar objetos DateTimeOffset que representam o UTC e
o fuso horário local:

C#

// Declare date; Kind property is DateTimeKind.Unspecified


DateTime sourceDate = new DateTime(2008, 5, 1, 8, 30, 0);
DateTimeOffset targetTime;

// Instantiate a DateTimeOffset value from a UTC time


DateTime utcTime = DateTime.SpecifyKind(sourceDate, DateTimeKind.Utc);
targetTime = new DateTimeOffset(utcTime);
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM +00:00
// Because the Kind property is DateTimeKind.Utc,
// the offset is TimeSpan.Zero.

// Instantiate a DateTimeOffset value from a UTC time with a zero offset


targetTime = new DateTimeOffset(utcTime, TimeSpan.Zero);
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM +00:00
// Because the Kind property is DateTimeKind.Utc,
// the call to the constructor succeeds

// Instantiate a DateTimeOffset value from a UTC time with a negative offset


try
{
targetTime = new DateTimeOffset(utcTime, new TimeSpan(-2, 0, 0));
Console.WriteLine(targetTime);
}
catch (ArgumentException)
{
Console.WriteLine("Attempt to create DateTimeOffset value from {0}
failed.",
targetTime);
}
// Throws exception and displays the following to the console:
// Attempt to create DateTimeOffset value from 5/1/2008 8:30:00 AM +00:00
failed.

// Instantiate a DateTimeOffset value from a local time


DateTime localTime = DateTime.SpecifyKind(sourceDate, DateTimeKind.Local);
targetTime = new DateTimeOffset(localTime);
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM -07:00
// Because the Kind property is DateTimeKind.Local,
// the offset is that of the local time zone.

// Instantiate a DateTimeOffset value from an unspecified time


targetTime = new DateTimeOffset(sourceDate);
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM -07:00
// Because the Kind property is DateTimeKind.Unspecified,
// the offset is that of the local time zone.

7 Observação

Chamar a sobrecarga do construtor DateTimeOffset que tem um único parâmetro


DateTime é equivalente a realizar uma conversão implícita de um valor DateTime
em um valor DateTimeOffset.

O segundo construtor que cria um objeto DateTimeOffset a partir de um valor DateTime


tem dois parâmetros: o valor DateTime a ser convertido e um valor TimeSpan que
representa o deslocamento de data e hora do UTC. Esse valor de deslocamento deve
corresponder à propriedade Kind do primeiro parâmetro do construtor ou um
ArgumentException é gerado. Se a propriedade Kind do primeiro parâmetro for
DateTimeKind.Utc, o valor do segundo parâmetro deverá ser TimeSpan.Zero. Se a
propriedade Kind do primeiro parâmetro for DateTimeKind.Local, o valor do segundo
parâmetro deverá ser o deslocamento do fuso horário do sistema local. Se a
propriedade Kind do primeiro parâmetro for DateTimeKind.Unspecified, o deslocamento
pode ser qualquer valor válido. O código a seguir ilustra chamadas para esse construtor
para converter valores DateTime para DateTimeOffset.

C#
DateTime sourceDate = new DateTime(2008, 5, 1, 8, 30, 0);
DateTimeOffset targetTime;

// Instantiate a DateTimeOffset value from a UTC time with a zero offset.


DateTime utcTime = DateTime.SpecifyKind(sourceDate, DateTimeKind.Utc);
targetTime = new DateTimeOffset(utcTime, TimeSpan.Zero);
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM +00:00
// Because the Kind property is DateTimeKind.Utc,
// the call to the constructor succeeds

// Instantiate a DateTimeOffset value from a UTC time with a non-zero


offset.
try
{
targetTime = new DateTimeOffset(utcTime, new TimeSpan(-2, 0, 0));
Console.WriteLine(targetTime);
}
catch (ArgumentException)
{
Console.WriteLine("Attempt to create DateTimeOffset value from {0}
failed.",
utcTime);
}
// Throws exception and displays the following to the console:
// Attempt to create DateTimeOffset value from 5/1/2008 8:30:00 AM failed.

// Instantiate a DateTimeOffset value from a local time with


// the offset of the local time zone
DateTime localTime = DateTime.SpecifyKind(sourceDate, DateTimeKind.Local);
targetTime = new DateTimeOffset(localTime,
TimeZoneInfo.Local.GetUtcOffset(localTime));
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM -07:00
// Because the Kind property is DateTimeKind.Local and the offset matches
// that of the local time zone, the call to the constructor succeeds.

// Instantiate a DateTimeOffset value from a local time with a zero offset.


try
{
targetTime = new DateTimeOffset(localTime, TimeSpan.Zero);
Console.WriteLine(targetTime);
}
catch (ArgumentException)
{
Console.WriteLine("Attempt to create DateTimeOffset value from {0}
failed.",
localTime);
}
// Throws exception and displays the following to the console:
// Attempt to create DateTimeOffset value from 5/1/2008 8:30:00 AM failed.

// Instantiate a DateTimeOffset value with an arbitrary time zone.


string timeZoneName = "Central Standard Time";
TimeSpan offset = TimeZoneInfo.FindSystemTimeZoneById(timeZoneName).
GetUtcOffset(sourceDate);
targetTime = new DateTimeOffset(sourceDate, offset);
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM -05:00

Conversão implícita de tipo


O tipo DateTimeOffset suporta uma conversão de tipo implícita: de um valor DateTime
para um valor DateTimeOffset. (Uma conversão de tipo implícita é uma conversão de
um tipo para outro que não requer uma conversão explícita (em C#) ou conversão (em
Visual Basic) e que não perde informações.) Isso torna o código, como o seguinte,
possível.

C#

DateTimeOffset targetTime;

// The Kind property of sourceDate is DateTimeKind.Unspecified


DateTime sourceDate = new DateTime(2008, 5, 1, 8, 30, 0);
targetTime = sourceDate;
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM -07:00

// define a UTC time (Kind property is DateTimeKind.Utc)


DateTime utcTime = DateTime.SpecifyKind(sourceDate, DateTimeKind.Utc);
targetTime = utcTime;
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM +00:00

// Define a local time (Kind property is DateTimeKind.Local)


DateTime localTime = DateTime.SpecifyKind(sourceDate, DateTimeKind.Local);
targetTime = localTime;
Console.WriteLine(targetTime);
// Displays 5/1/2008 8:30:00 AM -07:00

O deslocamento do valor DateTimeOffset resultante depende do valor da propriedade


DateTime.Kind. Se seu valor for DateTimeKind.Utc, o deslocamento será igual a
TimeSpan.Zero. Se seu valor for DateTimeKind.Local ou DateTimeKind.Unspecified, o
deslocamento será definido igual ao do fuso horário local.

Analisando a representação de string de uma


data e hora
O tipo DateTimeOffset oferece suporte a quatro métodos que permitem converter a
representação de string de uma data e hora em um valor DateTimeOffset:

Parse, que tenta converter a representação de string de uma data e hora em um


valor DateTimeOffset e gera uma exceção se a conversão falhar.

TryParse, que tenta converter a representação de string de uma data e hora em um


valor DateTimeOffset e retorna false se a conversão falhar.

ParseExact, que tenta converter a representação de string de uma data e hora em


um formato especificado em um valor DateTimeOffset. Na ocorrência de erros, o
método lança uma exceção.

TryParseExact, que tenta converter a representação de string de uma data e hora


em um formato especificado em um valor DateTimeOffset. O método retornará
false se a conversão falhar.

O exemplo a seguir ilustra chamadas para cada um desses quatro métodos de


conversão de string para instanciar um valor DateTimeOffset.

C#

string timeString;
DateTimeOffset targetTime;

timeString = "05/01/2008 8:30 AM +01:00";


try
{
targetTime = DateTimeOffset.Parse(timeString);
Console.WriteLine(targetTime);
}
catch (FormatException)
{
Console.WriteLine("Unable to parse {0}.", timeString);
}

timeString = "05/01/2008 8:30 AM";


if (DateTimeOffset.TryParse(timeString, out targetTime))
Console.WriteLine(targetTime);
else
Console.WriteLine("Unable to parse {0}.", timeString);

timeString = "Thursday, 01 May 2008 08:30";


try
{
targetTime = DateTimeOffset.ParseExact(timeString, "f",
CultureInfo.InvariantCulture);
Console.WriteLine(targetTime);
}
catch (FormatException)
{
Console.WriteLine("Unable to parse {0}.", timeString);
}

timeString = "Thursday, 01 May 2008 08:30 +02:00";


string formatString;
formatString = CultureInfo.InvariantCulture.DateTimeFormat.LongDatePattern +
" " +
CultureInfo.InvariantCulture.DateTimeFormat.ShortTimePattern
+
" zzz";
if (DateTimeOffset.TryParseExact(timeString,
formatString,
CultureInfo.InvariantCulture,
DateTimeStyles.AllowLeadingWhite,
out targetTime))
Console.WriteLine(targetTime);
else
Console.WriteLine("Unable to parse {0}.", timeString);
// The example displays the following output to the console:
// 5/1/2008 8:30:00 AM +01:00
// 5/1/2008 8:30:00 AM -07:00
// 5/1/2008 8:30:00 AM -07:00
// 5/1/2008 8:30:00 AM +02:00

Confira também
Datas, horas e fusos horários

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: criar fusos horários sem regras
de ajuste
Artigo • 07/04/2023

As informações precisas de fuso horário exigidas por um aplicativo podem não estar
presentes em um sistema específico por vários motivos:

O fuso horário nunca foi definido no registro do sistema local.

Os dados sobre o fuso horário foram modificados ou removidos do registro.

O fuso horário existe, mas não tem informações precisas sobre ajustes de fuso
horário para um determinado período histórico.

Nesses casos, você pode chamar o método CreateCustomTimeZone para definir o fuso
horário exigido pelo aplicativo. Você pode usar as sobrecargas desse método para criar
um fuso horário com ou sem regras de ajuste. Se o fuso horário de verão der suporte ao
horário de verão, você poderá definir ajustes com regras de ajuste fixas ou flutuantes.
(Para definições desses termos, confira a seção "Terminologia do fuso horário" na Visão
geral do fuso horário.)

) Importante

Fusos horários personalizados criados chamando o método


CreateCustomTimeZone não são adicionados ao registro. Em vez disso, eles só
podem ser acessados por meio da referência de objeto retornada pela chamada de
método CreateCustomTimeZone.

Este tópico mostra como criar um fuso horário sem regras de ajuste. Para criar um fuso
horário compatível com regras de ajuste de horário de verão, confira Como criar fusos
horários com regras de ajuste.

Para criar um fuso horário sem regras de ajuste


1. Defina o nome de exibição do fuso horário.

O nome de exibição segue um formato bastante padrão no qual o deslocamento


do fuso horário do UTC (Tempo Universal Coordenado) é colocado entre
parênteses e é seguido por uma cadeia de caracteres que identifica o fuso horário,
uma ou mais das cidades no fuso horário ou um ou mais dos países ou regiões no
fuso horário.

2. O nome do horário padrão do novo fuso horário. Normalmente, essa cadeia de


caracteres também é usada como o identificador do fuso horário.

3. Se você quiser usar um identificador diferente do nome padrão do fuso horário,


defina o identificador de fuso horário.

4. Instancie um objeto TimeSpan que define o deslocamento do fuso horário de UTC.


Fusos horários com horários posteriores ao UTC têm um deslocamento positivo.
Fusos horários com horários anteriores ao UTC têm um deslocamento negativo.

5. Chame o método TimeZoneInfo.CreateCustomTimeZone(String, TimeSpan, String,


String) para instanciar o novo fuso horário.

Exemplo
O exemplo a seguir define um fuso horário personalizado para Mawson, Antártida, que
não tem regras de ajuste.

C#

string displayName = "(GMT+06:00) Antarctica/Mawson Time";


string standardName = "Mawson Time";
TimeSpan offset = new TimeSpan(06, 00, 00);
TimeZoneInfo mawson = TimeZoneInfo.CreateCustomTimeZone(standardName,
offset, displayName, standardName);
Console.WriteLine("The current time is {0} {1}",
TimeZoneInfo.ConvertTime(DateTime.Now, TimeZoneInfo.Local,
mawson),
mawson.StandardName);

A cadeia de caracteres atribuída à propriedade DisplayName segue um formato padrão


no qual o deslocamento do fuso horário de UTC é seguido por uma descrição amigável
do fuso horário.

Compilando o código
Este exemplo requer:

Que os seguintes namespaces sejam importados:

C#
using System.Collections.Generic;
using System.Collections.ObjectModel;

Confira também
Datas, horas e fusos horários
Visão geral do fuso horário
Como: criar fusos horários com regras de ajuste

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: criar fusos horários com regras
de ajuste
Artigo • 07/04/2023

As informações precisas de fuso horário exigidas por um aplicativo podem não estar
presentes em um sistema específico por vários motivos:

O fuso horário nunca foi definido no registro do sistema local.

Os dados sobre o fuso horário foram modificados ou removidos do registro.

O fuso horário não tem informações precisas sobre ajustes de fuso horário para
um determinado período histórico.

Nesses casos, você pode chamar o método CreateCustomTimeZone para definir o fuso
horário exigido pelo aplicativo. Você pode usar as sobrecargas desse método para criar
um fuso horário com ou sem regras de ajuste. Se o fuso horário de verão der suporte ao
horário de verão, você poderá definir ajustes com regras de ajuste fixas ou flutuantes.
(Para definições desses termos, confira a seção "Terminologia do fuso horário" na Visão
geral do fuso horário.)

) Importante

Fusos horários personalizados criados chamando o método


CreateCustomTimeZone não são adicionados ao registro. Em vez disso, eles só
podem ser acessados por meio da referência de objeto retornada pela chamada de
método CreateCustomTimeZone.

Este tópico mostra como criar um fuso horário com regras de ajuste. Para criar um fuso
horário que não dê suporte a regras de ajuste de horário de verão, confira Como criar
fusos horários sem regras de ajuste.

Para criar um fuso horário com regras de ajuste flutuante


1. Para cada ajuste (ou seja, para cada transição que se afasta e volta para o horário
padrão em um intervalo de tempo específico), faça o seguinte:

a. Defina o tempo de transição inicial para o ajuste de fuso horário.

Você deve chamar o método


TimeZoneInfo.TransitionTime.CreateFloatingDateRule e passá-lo como um valor
DateTime que define o tempo da transição, um valor inteiro que define o mês
da transição, um valor inteiro que define a semana em que a transição ocorre e
um valor DayOfWeek que define o dia da semana em que a transição ocorre.
Essa chamada de método instancia um objeto TimeZoneInfo.TransitionTime.

b. Defina o tempo de transição final para o ajuste de fuso horário. Isso requer
outra chamada para o método
TimeZoneInfo.TransitionTime.CreateFloatingDateRule. Essa chamada de método
cria uma instância de um segundo objeto TimeZoneInfo.TransitionTime.

c. Chame o método CreateAdjustmentRule e passe as datas efetivas de início e


término do ajuste, um objeto TimeSpan que define a quantidade de tempo na
transição e os dois objetos TimeZoneInfo.TransitionTime que definem quando
as transições de e para o horário de verão ocorrem. Essa chamada de método
instancia um objeto TimeZoneInfo.AdjustmentRule.

d. Atribua o objeto TimeZoneInfo.AdjustmentRule a uma matriz de objetos


TimeZoneInfo.AdjustmentRule.

2. Defina o nome de exibição do fuso horário. O nome de exibição segue um formato


bastante padrão no qual o deslocamento do fuso horário do UTC (Tempo Universal
Coordenado) é colocado entre parênteses e é seguido por uma cadeia de
caracteres que identifica o fuso horário, uma ou mais das cidades no fuso horário
ou um ou mais dos países ou regiões no fuso horário.

3. O nome do horário padrão do novo fuso horário. Normalmente, essa cadeia de


caracteres também é usada como o identificador do fuso horário.

4. Defina o nome do horário de verão do fuso horário.

5. Se você quiser usar um identificador diferente do nome padrão do fuso horário,


defina o identificador de fuso horário.

6. Instancie um objeto TimeSpan que define o deslocamento do fuso horário de UTC.


Fusos horários com horários posteriores ao UTC têm um deslocamento positivo.
Fusos horários com horários anteriores ao UTC têm um deslocamento negativo.

7. Chame o método TimeZoneInfo.CreateCustomTimeZone(String, TimeSpan, String,


String, String, TimeZoneInfo+AdjustmentRule[]) para instanciar o novo fuso
horário.

Exemplo
O exemplo a seguir define um fuso horário padrão central para o Estados Unidos que
inclui regras de ajuste para uma variedade de intervalos de tempo de 1918 até o
presente.

C#

TimeZoneInfo cst;
// Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
TimeSpan delta = new TimeSpan(1, 0, 0);
TimeZoneInfo.AdjustmentRule adjustment;
List<TimeZoneInfo.AdjustmentRule> adjustmentList = new
List<TimeZoneInfo.AdjustmentRule>();
// Declare transition time variables to hold transition time information
TimeZoneInfo.TransitionTime transitionRuleStart, transitionRuleEnd;

// Define new Central Standard Time zone 6 hours earlier than UTC
// Define rule 1 (for 1918-1919)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 03, 05, DayOfWeek.Sunday);
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 10, 05, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1918, 1, 1), new DateTime(1919, 12, 31), delta,

transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 2 (for 1942)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 02, 09);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1942, 1, 1), new DateTime(1942, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 3 (for 1945)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new
DateTime(1, 1, 1, 23, 0, 0), 08, 14);
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 09, 30);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1945, 1, 1), new DateTime(1945, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define end rule (for 1967-2006)
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 10, 5, DayOfWeek.Sunday);
// Define rule 4 (for 1967-73)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 04, 05, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1967, 1, 1), new DateTime(1973, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 5 (for 1974 only)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 01, 06);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1974, 1, 1), new DateTime(1974, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 6 (for 1975 only)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFixedDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 02, 23);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1975, 1, 1), new DateTime(1975, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 7 (1976-1986)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 04, 05, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1976, 1, 1), new DateTime(1986, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 8 (1987-2006)
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 04, 01, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1987, 1, 1), new DateTime(2006, 12, 31),
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule 9 (2007- )
transitionRuleStart = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 03, 02, DayOfWeek.Sunday);
transitionRuleEnd = TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new
DateTime(1, 1, 1, 2, 0, 0), 11, 01, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(2007, 1, 1), DateTime.MaxValue.Date,
delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);

// Convert list of adjustment rules to an array


TimeZoneInfo.AdjustmentRule[] adjustments = new
TimeZoneInfo.AdjustmentRule[adjustmentList.Count];
adjustmentList.CopyTo(adjustments);

cst = TimeZoneInfo.CreateCustomTimeZone("Central Standard Time", new


TimeSpan(-6, 0, 0),
"(GMT-06:00) Central Time (US Only)", "Central Standard Time",
"Central Daylight Time", adjustments);
O fuso horário criado neste exemplo tem várias regras de ajuste. É necessário ter
cuidado para garantir que as datas efetivas de início e término de qualquer regra de
ajuste não se sobreponham às datas de outra regra de ajuste. Se houver uma
sobreposição, um InvalidTimeZoneException será gerado.

Para regras de ajuste flutuante, o valor 5 é passado para o parâmetro week do método
CreateFloatingDateRule para indicar que a transição ocorre na última semana de um
mês específico.

Ao criar a matriz de objetos TimeZoneInfo.AdjustmentRule a serem usados na chamada


de método TimeZoneInfo.CreateCustomTimeZone(String, TimeSpan, String, String,
String, TimeZoneInfo+AdjustmentRule[]), o código pode inicializar a matriz para o
tamanho exigido pelo número de ajustes a serem criados para o fuso horário. Em vez
disso, este exemplo de código chama o método Add para adicionar cada regra de ajuste
a uma coleção List<T> genérica de objetos TimeZoneInfo.AdjustmentRule. Em seguida,
o código chama o método CopyTo para copiar os membros dessa coleção para a matriz.

O exemplo também usa o método CreateFixedDateRule para definir ajustes de data fixa.
Isso é semelhante a chamar o método CreateFloatingDateRule, exceto que ele requer
apenas a hora, o mês e o dia dos parâmetros de transição.

O exemplo pode ser testado usando código como o seguinte:

C#

TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard


Time");

DateTime pastDate1 = new DateTime(1942, 2, 11);


Console.WriteLine("Is {0} daylight saving time: {1}", pastDate1,
cst.IsDaylightSavingTime(pastDate1));

DateTime pastDate2 = new DateTime(1967, 10, 29, 1, 30, 00);


Console.WriteLine("Is {0} ambiguous: {1}", pastDate2,
cst.IsAmbiguousTime(pastDate2));

DateTime pastDate3 = new DateTime(1974, 1, 7, 2, 59, 00);


Console.WriteLine("{0} {1} is {2} {3}", pastDate3,
est.IsDaylightSavingTime(pastDate3) ?
est.DaylightName : est.StandardName,
TimeZoneInfo.ConvertTime(pastDate3, est, cst),

cst.IsDaylightSavingTime(TimeZoneInfo.ConvertTime(pastDate3, est, cst)) ?


cst.DaylightName : cst.StandardName);
//
// This code produces the following output to the console:
//
// Is 2/11/1942 12:00:00 AM daylight saving time: True
// Is 10/29/1967 1:30:00 AM ambiguous: True
// 1/7/1974 2:59:00 AM Eastern Standard Time is 1/7/1974 2:59:00 AM
Central Daylight Time

Compilando o código
Este exemplo requer:

Que os seguintes namespaces sejam importados:

C#

using System.Collections.Generic;
using System.Collections.ObjectModel;

Confira também
Datas, horas e fusos horários
Visão geral do fuso horário
Como: criar fusos horários sem regras de ajuste

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Encontrando os fusos horários definidos
em um sistema local
Artigo • 07/04/2023

A classe TimeZoneInfo não expõe um construtor público. Como resultado, a palavra-


chave new não pode ser usada para criar um novo objeto TimeZoneInfo. Em vez disso,
uma instância dos objetos TimeZoneInfo é criada recuperando informações sobre fusos
horários predefinidos do registro ou criando um fuso horário personalizado. Este tópico
discute criação de instância de um fuso horário a partir dos dados armazenados no
registro. Além disso, as propriedades static ( shared no Visual Basic) da classe
TimeZoneInfo fornecem acesso ao UTC (Tempo Universal Coordenado) e ao fuso horário
local.

7 Observação

Para fusos horários que não são definidos no registro, você pode criar fusos
horários personalizados chamando as sobrecargas do método
CreateCustomTimeZone. A criação de um fuso horário personalizado é discutida
nos tópicos Como: criar fusos horários sem regras de ajuste e Como: criar fusos
horários com regras de ajuste. Além disso, você pode criar uma instância de um
objeto TimeZoneInfo restaurando-o de uma cadeia de caracteres serializada com o
método FromSerializedString. A serialização e a desserialização de um objeto
TimeZoneInfo são discutidas nos tópicos Como: salvar fusos horários em um
recurso inserido e Como: restaurar fusos horários de um recurso inserido.

Acessar fusos horários individuais


A classe TimeZoneInfo fornece dois objetos de fuso horário predefinidos que
representam o horário UTC e o fuso horário local. Eles estão disponíveis nas
propriedades Utc e Local, respectivamente. Para obter instruções sobre como acessar o
UTC ou fusos horários locais, veja Como: acessar os objetos de UTC e fuso horário local
predefinidos.

Você também pode criar uma instância de um objeto TimeZoneInfo que representa
qualquer fuso horário definido pelo registro. Para obter instruções sobre como
instanciar um objeto de fuso horário específico, veja Como: criar uma instância de um
objeto TimeZoneInfo.
Identificadores de fuso horário
O identificador do fuso horário é um campo de chave que identifica exclusivamente o
fuso horário. Enquanto a maioria das chaves são relativamente curtas, o identificador de
fuso horário é comparativamente longo. Na maioria dos casos, seu valor corresponde ao
da propriedade TimeZoneInfo.StandardName, que é usada para fornecer o nome da
hora padrão do fuso horário. No entanto, há exceções. A melhor maneira de certificar-se
de que você forneça um identificador válido é enumerar os fusos horários disponíveis
no sistema e observar os identificadores associados.

Confira também
Datas, horas e fusos horários
Como: acessar os objetos de fuso horário predefinidos UTC e local
Como: criar uma instância de um objeto TimeZoneInfo
Convertendo horários entre fusos horários

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: enumerar os fusos horários
presentes em um computador
Artigo • 09/05/2023

Trabalhar com êxito com um fuso horário designado requer que informações sobre o
fuso horário em questão estejam disponíveis no sistema. Os sistemas operacionais
Windows XP e Windows Vista armazenam essas informações no registro. Embora o
número total de fusos horários existentes seja grande, o Registro contém informações
apenas sobre um subconjunto deles. Além disso, o Registro em si é uma estrutura
dinâmica cujo conteúdo está sujeito a alterações deliberadas ou acidentais. Como
resultado, um aplicativo nem sempre pode presumir que um determinado fuso horário
esteja definido e disponível no sistema. A primeira etapa para muitos aplicativos que
usam informações de fuso horário é determinar se os fusos horários necessários estão
disponíveis no sistema local ou dar ao usuário uma lista dos fusos horários entre os
quais escolher. Isso requer que o aplicativo enumere os fusos horários definidos no
sistema local.

7 Observação

Se um aplicativo depende da presença de um fuso horário específico que pode não


estar definido em um sistema local, o aplicativo poderá garantir sua presença
serializando e desserializando informações sobre o fuso horário. O fuso horário
então pode ser adicionado a um controle de lista para que o usuário do aplicativo
possa selecioná-lo. Para detalhes, confira Como salvar fusos horários em um
recurso inserido e Como restaurar fusos horários de um recurso inserido.

Para enumerar os fusos horários presentes no sistema


local
1. Chame o método TimeZoneInfo.GetSystemTimeZones . O método retorna uma
coleção ReadOnlyCollection<T> genérica de objetos TimeZoneInfo. As entradas na
coleção são classificadas pela propriedade DisplayName. Por exemplo:

C#

ReadOnlyCollection<TimeZoneInfo> tzCollection;
tzCollection = TimeZoneInfo.GetSystemTimeZones();
2. Enumere os objetos TimeZoneInfo individuais na coleção usando um loop
foreach e (em C#) ou um For Each … Loop Next (no Visual Basic) e realize qualquer

processamento necessário em cada objeto. Por exemplo, o código a seguir


enumera a coleção ReadOnlyCollection<T>de objetos TimeZoneInfo retornados
na etapa 1 e lista o nome de exibição de cada fuso horário no console.

C#

foreach (TimeZoneInfo timeZone in tzCollection)


Console.WriteLine(" {0}: {1}", timeZone.Id, timeZone.DisplayName);

Para apresentar ao usuário uma lista de fusos horários


presentes no sistema local
1. Chame o método TimeZoneInfo.GetSystemTimeZones . O método retorna uma
coleção ReadOnlyCollection<T> genérica de objetos TimeZoneInfo.

2. Atribua a coleção retornada na etapa 1 à propriedade DataSource de um controle


de lista do ASP.NET ou Windows Forms.

3. Recupere o objeto TimeZoneInfo que o usuário selecionou.

O exemplo apresenta uma ilustração para um aplicativo do Windows.

Exemplo
O exemplo inicia um aplicativo do Windows que exibe os fusos horários definidos em
um sistema em uma caixa de listagem. O exemplo então exibe uma caixa de diálogo
que contém o valor da propriedade DisplayName do objeto de fuso horário selecionado
pelo usuário.

C#

private void Form1_Load(object sender, EventArgs e)


{
ReadOnlyCollection<TimeZoneInfo> tzCollection;
tzCollection = TimeZoneInfo.GetSystemTimeZones();
this.timeZoneList.DataSource = tzCollection;
}

private void OkButton_Click(object sender, EventArgs e)


{
TimeZoneInfo selectedTimeZone = (TimeZoneInfo)
this.timeZoneList.SelectedItem;
MessageBox.Show("You selected the " + selectedTimeZone.ToString() + "
time zone.");
}

A maioria dos controles de lista (como o controle System.Windows.Forms.ListBox ou


System.Web.UI.WebControls.BulletedList) permite que você atribua uma coleção de
variáveis de objeto à propriedade DataSource , desde que essa coleção implemente a
interface IEnumerable. (A classe genérica ReadOnlyCollection<T> faz isso.) Para exibir
um objeto individual na coleção, o controle chama o método desse objeto ToString
para extrair a cadeia de caracteres usada para representar o objeto. No caso de objetos
TimeZoneInfo, o método ToString retorna o nome de exibição do objeto TimeZoneInfo
(o valor de sua propriedade DisplayName).

7 Observação

Como os controles de lista chamam o método de um objeto ToString , você pode


atribuir uma coleção de objetos TimeZoneInfo ao controle, fazer com que o
controle exiba um nome significativo para cada objeto e recuperar o objeto
TimeZoneInfo selecionado pelo usuário. Isso elimina a necessidade de extrair uma
cadeia de caracteres para cada objeto na coleção, atribuir a cadeia de caracteres a
uma coleção que, por sua vez, é atribuída à propriedade do controle DataSource ,
recuperar a cadeia de caracteres selecionada pelo usuário e então usar essa cadeia
de caracteres para extrair o objeto que ela descreve.

Compilando o código
Este exemplo requer:

Que os seguintes namespaces sejam importados:

System (no código C#)

System.Collections.ObjectModel

Confira também
Datas, horas e fusos horários
Como: salvar fusos horários em um recurso inserido
Como: restaurar fusos horários de um recurso inserido
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: acessar os objetos de fuso
horário predefinidos UTC e local
Artigo • 07/04/2023

A classe TimeZoneInfo fornece duas propriedades, Utc e Local, que fornecem seu acesso
de código a objetos de fuso horário predefinidos. Este tópico discute como acessar os
objetos TimeZoneInfo retornados por essas propriedades.

Para acessar o objeto TimeZoneInfo de UTC (Tempo


Universal Coordenado)
1. Use a propriedade static ( Shared no Visual Basic) TimeZoneInfo.Utc para acessar
o Tempo Universal Coordenado.

2. Em vez de atribuir o objeto TimeZoneInfo retornado pela propriedade a uma


variável de objeto, continue acessando o Tempo Universal Coordenado por meio
da propriedade TimeZoneInfo.Utc.

Para acessar o fuso horário local


1. Use a propriedade static ( Shared no Visual Basic) TimeZoneInfo.Local para
acessar o fuso horário do sistema local.

2. Em vez de atribuir o objeto TimeZoneInfo retornado pela propriedade a uma


variável de objeto, continue acessando o fuso horário local por meio da
propriedade TimeZoneInfo.Local.

Exemplo
O código a seguir usa as propriedades TimeZoneInfo.Local e TimeZoneInfo.Utc para
converter um horário do fuso horário Padrão do Leste do Canadá e dos EUA, e também
para exibir o nome do fuso horário no console.

C#

// Create Eastern Standard Time value and TimeZoneInfo object


DateTime estTime = new DateTime(2007, 1, 1, 00, 00, 00);
string timeZoneName = "Eastern Standard Time";
try
{
TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById(timeZoneName);
// Convert EST to local time
DateTime localTime = TimeZoneInfo.ConvertTime(estTime, est,
TimeZoneInfo.Local);
Console.WriteLine("At {0} {1}, the local time is {2} {3}.",
estTime,
est,
localTime,
TimeZoneInfo.Local.IsDaylightSavingTime(localTime) ?
TimeZoneInfo.Local.DaylightName :
TimeZoneInfo.Local.StandardName);

// Convert EST to UTC


DateTime utcTime = TimeZoneInfo.ConvertTime(estTime, est,
TimeZoneInfo.Utc);
Console.WriteLine("At {0} {1}, the time is {2} {3}.",
estTime,
est,
utcTime,
TimeZoneInfo.Utc.StandardName);
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The {0} zone cannot be found in the registry.",
timeZoneName);
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("The registry contains invalid data for the {0} zone.",
timeZoneName);
}

// The example produces the following output to the console:


// At 1/1/2007 12:00:00 AM (UTC-05:00) Eastern Time (US & Canada), the
local time is 1/1/2007 12:00:00 AM Eastern Standard Time.
// At 1/1/2007 12:00:00 AM (UTC-05:00) Eastern Time (US & Canada), the
time is 1/1/2007 5:00:00 AM UTC.

Você deve sempre acessar o fuso horário local por meio da propriedade
TimeZoneInfo.Local, em vez de atribuir o fuso horário local a uma variável de objeto
TimeZoneInfo. Da mesma forma, você deve sempre acessar o Tempo Universal
Coordenado por meio da propriedade TimeZoneInfo.Utc, em vez de atribuir a zona UTC
a uma variável de objeto TimeZoneInfo. Isso impede que a variável de objeto
TimeZoneInfo seja invalidada por uma chamada ao método
TimeZoneInfo.ClearCachedData.

Compilando o código
Este exemplo requer:
Que o namespace System seja importado com a instrução using (necessária no
código C#).

Confira também
Datas, horas e fusos horários
Encontrando os fusos horários definidos em um sistema local
Como: criar uma instância de um objeto TimeZoneInfo

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Instruções: como obter um objeto
TimeZoneInfo
Artigo • 15/03/2024

A maneira mais comum de obter um objeto TimeZoneInfo é recuperar informações


sobre ele do Registro. Para obter o objeto, chame o método static ( Shared no Visual
Basic) TimeZoneInfo.FindSystemTimeZoneById, que examina o registro. Manipule as
exceções geradas pelo método, particularmente a TimeZoneNotFoundException gerada
se o fuso horário não estiver definido no registro.

7 Observação

A partir do .NET 8, TimeZoneInfo.FindSystemTimeZoneById retorna um objeto


TimeZoneInfo armazenado em cache em vez de instanciar um novo objeto. Para
obter mais informações, confira FindSystemTimeZoneById não retorna um novo
objeto.

Exemplo
O código a seguir recupera um objeto TimeZoneInfo que representa o fuso horário
padrão do Leste dos EUA e exibe a hora oficial do Leste dos EUA que corresponde à
hora local.

C#

DateTime timeNow = DateTime.Now;


try
{
TimeZoneInfo easternZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern
Standard Time");
DateTime easternTimeNow = TimeZoneInfo.ConvertTime(
timeNow,
TimeZoneInfo.Local,
easternZone
);
Console.WriteLine("{0} {1} corresponds to {2} {3}.",
timeNow,
TimeZoneInfo.Local.IsDaylightSavingTime(timeNow) ?
TimeZoneInfo.Local.DaylightName :
TimeZoneInfo.Local.StandardName,
easternTimeNow,
easternZone.IsDaylightSavingTime(easternTimeNow) ?
easternZone.DaylightName :
easternZone.StandardName);
}
// Handle exception
//
// As an alternative to simply displaying an error message, an alternate
Eastern
// Standard Time TimeZoneInfo object could be instantiated here either by
restoring
// it from a serialized string or by providing the necessary data to the
// CreateCustomTimeZone method.
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The Eastern Standard Time Zone cannot be found on the
local system.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("The Eastern Standard Time Zone contains invalid or
missing data.");
}
catch (SecurityException)
{
Console.WriteLine("The application lacks permission to read time zone
information from the registry.");
}
catch (OutOfMemoryException)
{
Console.WriteLine("Not enough memory is available to load information on
the Eastern Standard Time zone.");
}
// If we weren't passing FindSystemTimeZoneById a literal string, we also
// would handle an ArgumentNullException.

O parâmetro único do método TimeZoneInfo.FindSystemTimeZoneById é o identificador


do fuso horário que você deseja recuperar, que corresponde à propriedade
TimeZoneInfo.Id do objeto. O identificador do fuso horário é um campo de chave que
identifica exclusivamente o fuso horário. Enquanto a maioria das chaves são
relativamente curtas, o identificador de fuso horário é comparativamente longo. Na
maioria dos casos, o valor dele corresponde ao da propriedade StandardName de um
objeto TimeZoneInfo, que é usada para fornecer o nome da hora padrão do fuso
horário. No entanto, há exceções. A melhor maneira de certificar-se de que você forneça
um identificador válido é enumerar os fusos horários disponíveis no sistema e observar
os identificadores dos fusos horários presentes nele. Para obter uma ilustração, veja
Como enumerar os fusos horários presentes em um computador. O artigo Localizar os
fusos horários definidos em um sistema local também contém uma lista de
identificadores de fuso horário selecionados.

Se o fuso horário for encontrado, o método retornará o objeto TimeZoneInfo dele. Se o


fuso horário não for encontrado, o método lançará uma TimeZoneNotFoundException.
Se o fuso horário for encontrado, mas seus dados estiverem corrompidos ou
incompletos, o método lançará uma InvalidTimeZoneException.

Se o aplicativo depender de um fuso horário que deve estar presente, primeiro chame o
método FindSystemTimeZoneById para recuperar as informações de fuso horário do
registro. Se a chamada de método falhar, o manipulador de exceção deverá criar uma
instância do fuso horário ou criá-la novamente desserializando um objeto TimeZoneInfo
serializado. Confira Como restaurar os fusos horários de um recurso inserido para obter
um exemplo.

Confira também
Datas, horas e fusos horários
Encontrando os fusos horários definidos em um sistema local
Como acessar os objetos de fuso horário predefinidos UTC e local

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Salvar e restaurar fusos horários
Artigo • 07/04/2023

A classe TimeZoneInfo depende do registro para recuperar dados de fuso horário


predefinidos. No entanto, o registro é uma estrutura dinâmica. Além disso, as
informações de fuso horário que o registro contém são usadas pelo sistema operacional
principalmente para lidar com ajustes de tempo e conversões para o ano atual. Isso tem
duas implicações principais para aplicativos que dependem de dados precisos de fuso
horário:

Um fuso horário exigido por um aplicativo pode não estar definido no registro ou
pode ter sido renomeado ou removido do registro.

Um fuso horário definido no registro pode não ter informações sobre as regras de
ajuste específicas necessárias para conversões de fuso horário histórico.

A classe TimeZoneInfo aborda essas limitações por meio de seu suporte para
serialização (salvamento) e desserialização (restauração) de dados de fuso horário.

Serialização e desserialização de fuso horário


Salvar e restaurar um fuso horário serializando e desserializando dados de fuso horário
envolve apenas duas chamadas de método:

Você pode serializar um objeto TimeZoneInfo chamando o método desse objeto


ToSerializedString. O método não usa parâmetros e retorna uma cadeia de
caracteres que contém informações de fuso horário.

Você pode desserializar um objeto TimeZoneInfo de uma cadeia de caracteres


serializada passando essa cadeia de caracteres para o método static ( Shared no
Visual Basic TimeZoneInfo.FromSerializedString).

Cenários de serialização e desserialização


A capacidade de salvar (ou serializar) um objeto TimeZoneInfo em uma cadeia de
caracteres e restaurá-lo (ou desserializá-lo) para uso posterior aumenta o utilitário e a
flexibilidade da classe TimeZoneInfo. Esta seção examina algumas das situações em que
a serialização e a desserialização são mais úteis.
Como serializar e desserializar dados de fuso horário em
um aplicativo
Um fuso horário serializado pode ser restaurado de uma cadeia de caracteres quando
necessário. Um aplicativo poderá fazer isso se o fuso horário recuperado do registro não
conseguir converter corretamente uma data e hora em um intervalo de datas específico.
Por exemplo, os dados de fuso horário no Registro do Windows XP dão suporte a uma
só regra de ajuste, enquanto os fusos horários definidos no registro do Windows Vista
normalmente fornecem informações sobre duas regras de ajuste. Isso significa que as
conversões de tempo histórico podem ser imprecisas. A serialização e a desserialização
de dados de fuso horário podem lidar com essa limitação.

No exemplo a seguir, uma classe TimeZoneInfo personalizada que não tem regras de
ajuste é definida para representar o fuso horário padrão do leste dos EUA de 1883 a
1917, antes da introdução do horário de verão no Estados Unidos. O fuso horário
personalizado é serializado em uma variável que tem escopo global. Ao método de
conversão de fuso horário, ConvertUtcTime , são passados horários UTC (Tempo Universal
Coordenado) para converter. Se a data e a hora ocorrerem em 1917 ou antes, o fuso
horário padrão oriental personalizado será restaurado de uma cadeia de caracteres
serializada e substituirá o fuso horário recuperado do registro.

C#

using System;

public class TimeZoneSerialization


{
static string serializedEst;

public static void Main()


{
// Retrieve Eastern Standard Time zone from registry
try
{
TimeZoneSerialization tzs = new TimeZoneSerialization();
TimeZoneInfo est = TimeZoneInfo.FindSystemTimeZoneById("Eastern
Standard Time");
// Create custom Eastern Time Zone for historical (pre-1918)
conversions
CreateTimeZone();
// Call conversion function with one current and one pre-1918 date
and time
Console.WriteLine(ConvertUtcTime(DateTime.UtcNow, est));
Console.WriteLine(ConvertUtcTime(new DateTime(1900, 11, 15, 9, 32,
00, DateTimeKind.Utc), est));
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The Eastern Standard Time zone is not in the
registry.");
}
catch (InvalidTimeZoneException)
{
Console.WriteLine("Data on the Eastern Standard Time Zone in the
registry is corrupted.");
}
}

private static void CreateTimeZone()


{
// Create a simple Eastern Standard time zone
// without adjustment rules for 1883-1918
TimeZoneInfo earlyEstZone = TimeZoneInfo.CreateCustomTimeZone("Eastern
Standard Time",
new TimeSpan(-5, 0, 0),
" (GMT-05:00) Eastern Time (United
States)",
"Eastern Standard Time");
serializedEst = earlyEstZone.ToSerializedString();
}

private static DateTime ConvertUtcTime(DateTime utcDate, TimeZoneInfo tz)


{
// Use time zone object from registry
if (utcDate.Year > 1917)
{
return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
}
// Handle dates before introduction of DST
else
{
// Restore serialized time zone object
tz = TimeZoneInfo.FromSerializedString(serializedEst);
return TimeZoneInfo.ConvertTimeFromUtc(utcDate, tz);
}
}
}

Como manipular exceções de fuso horário


Como o registro é uma estrutura dinâmica, seu conteúdo está sujeito a modificações
acidentais ou deliberadas. Isso significa que um fuso horário que deve ser definido no
registro e que é necessário para um aplicativo ser executado com êxito pode estar
ausente. Sem suporte para serialização e desserialização de fuso horário, você não tem
escolha a não ser lidar com o resultado encerrando o aplicativo
TimeZoneNotFoundException. No entanto, usando serialização e desserialização de fuso
horário, você pode lidar com um TimeZoneNotFoundException inesperado restaurando
o fuso horário necessário de uma cadeia de caracteres serializada e o aplicativo
continuará sendo executado.

O exemplo a seguir cria e serializa um fuso horário padrão central personalizado. Em


seguida, ele tenta recuperar o fuso horário padrão central do registro. Se a operação de
recuperação gerar um TimeZoneNotFoundException ou um InvalidTimeZoneException,
o manipulador de exceção desserializará o fuso horário.

C#

using System;
using System.Collections.Generic;

public class TimeZoneApplication


{
// Define collection of custom time zones
private Dictionary<string, string> customTimeZones = new
Dictionary<string, string>();
private TimeZoneInfo cst;

public TimeZoneApplication()
{
// Create custom Central Standard Time
//
// Declare necessary TimeZoneInfo.AdjustmentRule objects for time zone
TimeZoneInfo customTimeZone;
TimeSpan delta = new TimeSpan(1, 0, 0);
TimeZoneInfo.AdjustmentRule adjustment;
List<TimeZoneInfo.AdjustmentRule> adjustmentList = new
List<TimeZoneInfo.AdjustmentRule>();
// Declare transition time variables to hold transition time
information
TimeZoneInfo.TransitionTime transitionRuleStart, transitionRuleEnd;

// Define end rule (for 1976-2006)


transitionRuleEnd =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2,
0, 0), 10, 5, DayOfWeek.Sunday);
// Define rule (1976-1986)
transitionRuleStart =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2,
0, 0), 04, 05, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1976, 1, 1), new DateTime(1986, 12, 31), delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule (1987-2006)
transitionRuleStart =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2,
0, 0), 04, 01, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(1987, 1, 1), new DateTime(2006, 12, 31), delta,
transitionRuleStart, transitionRuleEnd);
adjustmentList.Add(adjustment);
// Define rule (2007- )
transitionRuleStart =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2,
0, 0), 03, 02, DayOfWeek.Sunday);
transitionRuleEnd =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 2,
0, 0), 11, 01, DayOfWeek.Sunday);
adjustment = TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new
DateTime(2007, 01, 01), DateTime.MaxValue.Date, delta, transitionRuleStart,
transitionRuleEnd);
adjustmentList.Add(adjustment);

// Create custom U.S. Central Standard Time zone


customTimeZone = TimeZoneInfo.CreateCustomTimeZone("Central Standard
Time",
new TimeSpan(-6, 0, 0),
"(GMT-06:00) Central Time (US Only)", "Central
Standard Time",
"Central Daylight Time", adjustmentList.ToArray());
// Add time zone to collection
customTimeZones.Add(customTimeZone.Id,
customTimeZone.ToSerializedString());

// Create any other required time zones


}

public static void Main()


{
TimeZoneApplication tza = new TimeZoneApplication();
tza.AppEntryPoint();
}

private void AppEntryPoint()


{
try
{
cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
}
catch (TimeZoneNotFoundException)
{
if (customTimeZones.ContainsKey("Central Standard Time"))
HandleTimeZoneException("Central Standard Time");
}
catch (InvalidTimeZoneException)
{
if (customTimeZones.ContainsKey("Central Standard Time"))
HandleTimeZoneException("Central Standard Time");
}
if (cst == null)
{
Console.WriteLine("Unable to load Central Standard Time zone.");
return;
}
DateTime currentTime = DateTime.Now;
Console.WriteLine("The current {0} time is {1}.",
TimeZoneInfo.Local.IsDaylightSavingTime(currentTime)
?
TimeZoneInfo.Local.StandardName :
TimeZoneInfo.Local.DaylightName,
currentTime.ToString("f"));
Console.WriteLine("The current {0} time is {1}.",
cst.IsDaylightSavingTime(currentTime) ?
cst.StandardName :
cst.DaylightName,
TimeZoneInfo.ConvertTime(currentTime,
TimeZoneInfo.Local, cst).ToString("f"));
}

private void HandleTimeZoneException(string timeZoneName)


{
string tzString = customTimeZones[timeZoneName];
cst = TimeZoneInfo.FromSerializedString(tzString);
}
}

Como armazenar uma cadeia de caracteres serializada e


restaurará-la quando necessário
Os exemplos anteriores armazenavam informações de fuso horário em uma variável de
cadeia de caracteres e as restauravam quando necessário. No entanto, a cadeia de
caracteres que contém informações de fuso horário serializada pode ser armazenada
em algum meio de armazenamento, como um arquivo externo, um arquivo de recurso
inserido no aplicativo ou no Registro. (Observe que as informações sobre fusos horários
personalizados devem ser armazenadas além das chaves de fuso horário do sistema no
Registro.)

Armazenar uma cadeia de caracteres de fuso horário serializada dessa maneira também
separa a rotina de criação de fuso horário do aplicativo em si. Por exemplo, uma rotina
de criação de fuso horário pode executar e criar um arquivo de dados que contém
informações históricas de fuso horário que um aplicativo pode usar. Em seguida, o
arquivo de dados pode ser instalado com o aplicativo e aberto e um ou mais de seus
fusos horários podem ser desserializados quando o aplicativo exigir.

Para um exemplo que usa um recurso inserido para armazenar dados de fuso horário
serializados, confira Como salvar fusos horários em um recurso inserido e Como
restaurar fusos horários de um recurso inserido.

Confira também
Datas, horas e fusos horários

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: salvar fusos horários em um
recurso inserido
Artigo • 10/05/2023

Um aplicativo com reconhecimento de fuso horário geralmente requer a presença de


um fuso horário específico. No entanto, como a disponibilidade de objetos
TimeZoneInfo individuais depende de informações armazenadas no registro do sistema
local, até mesmo fusos horários disponíveis normalmente podem estar faltando. Além
disso, as informações sobre fusos horários personalizados instanciados usando o
método CreateCustomTimeZone não são armazenadas com outras informações de fuso
horário no registro. Para garantir que esses fusos horários estejam disponíveis quando
necessário, você pode salvá-los serializando-os e restaurá-los posteriormente
desserializando-os.

Normalmente, a serialização de um objeto TimeZoneInfo ocorre além do aplicativo com


reconhecimento de fuso horário. Dependendo do armazenamento de dados usado para
armazenar objetos TimeZoneInfo serializados, os dados de fuso horário podem ser
serializados como parte de uma rotina de instalação ou instalação (por exemplo,
quando os dados são armazenados em uma chave de aplicativo do Registro) ou como
parte de uma rotina de utilitário executada antes da compilação do aplicativo final (por
exemplo, quando os dados serializados são armazenados em um arquivo de recurso
.NET XML (.resx)).

Além de um arquivo de recurso compilado com o aplicativo, vários outros


armazenamentos de dados podem ser usados para informações de fuso horário. Elas
incluem o seguinte:

O registro. Observe que um aplicativo deve usar as subchaves de sua própria


chave de aplicativo para armazenar dados de fuso horário personalizados em vez
de usar as subchaves de HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows
NT\CurrentVersion\Time Zones.

Arquivos de configuração.

Outros arquivos do sistema.

Para salvar um fuso horário serializando-o em um arquivo


.resx
1. Recupere um fuso horário ou crie um fuso horário.
Para recuperar um fuso horário, confira Como acessar os objetos de fuso horário
utc e local predefinidos e Como criar uma instância de um objeto TimeZoneInfo.

Para criar um fuso horário, chame uma das sobrecargas do método


CreateCustomTimeZone. Para mais informações, confira Como criar fusos horários
sem regras de ajuste e Como criar fusos horários com regras de ajuste.

2. Chame o método ToSerializedString para criar uma cadeia de caracteres que


contém os dados do fuso horário.

3. Instancie um objeto StreamWriter fornecendo o nome e, opcionalmente, o


caminho do arquivo .resx para o construtor de classe StreamWriter.

4. Instancie um objeto ResXResourceWriter passando o objeto StreamWriter para o


construtor de classe ResXResourceWriter.

5. Passe a cadeia de caracteres serializada do fuso horário para o método


ResXResourceWriter.AddResource.

6. Chame o método ResXResourceWriter.Generate .

7. Chame o método ResXResourceWriter.Close .

8. Feche o objeto StreamWriter chamando seu método Close.

9. Adicione o arquivo .resx gerado ao projeto do Visual Studio do aplicativo.

10. Usando a janela Propriedades no Visual Studio, verifique se a propriedade Build


Action do arquivo .resx está definida como Recurso Inserido.

Exemplo
O exemplo a seguir serializa um objeto TimeZoneInfo que representa o Horário Padrão
Central e um objeto TimeZoneInfo que representa a Estação Palmer, hora da Antártida
para um arquivo de recurso XML do .NET chamado SerializedTimeZones.resx. O Horário
Padrão Central é normalmente definido no registro; Estação Palmer, Antártida é um fuso
horário personalizado.

C#

TimeZoneSerialization()
{
TextWriter writeStream;
Dictionary<string, string> resources = new Dictionary<string, string>();
// Determine if .resx file exists
if (File.Exists(resxName))
{
// Open reader
TextReader readStream = new StreamReader(resxName);
ResXResourceReader resReader = new ResXResourceReader(readStream);
foreach (DictionaryEntry item in resReader)
{
if (! (((string) item.Key) == "CentralStandardTime" ||
((string) item.Key) == "PalmerStandardTime" ))
resources.Add((string)item.Key, (string) item.Value);
}
readStream.Close();
// Delete file, since write method creates duplicate xml headers
File.Delete(resxName);
}

// Open stream to write to .resx file


try
{
writeStream = new StreamWriter(resxName, true);
}
catch (FileNotFoundException e)
{
// Handle failure to find file
Console.WriteLine("{0}: The file {1} could not be found.",
e.GetType().Name, resxName);
return;
}

// Get resource writer


ResXResourceWriter resWriter = new ResXResourceWriter(writeStream);

// Add resources from existing file


foreach (KeyValuePair<string, string> item in resources)
{
resWriter.AddResource(item.Key, item.Value);
}

// Serialize Central Standard Time


try
{
TimeZoneInfo cst = TimeZoneInfo.FindSystemTimeZoneById("Central
Standard Time");
resWriter.AddResource(cst.Id.Replace(" ", string.Empty),
cst.ToSerializedString());
}
catch (TimeZoneNotFoundException)
{
Console.WriteLine("The Central Standard Time zone could not be
found.");
}

// Create time zone for Palmer, Antarctica


//
// Define transition times to/from DST
TimeZoneInfo.TransitionTime startTransition =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 4,
0, 0),

10, 2, DayOfWeek.Sunday);
TimeZoneInfo.TransitionTime endTransition =
TimeZoneInfo.TransitionTime.CreateFloatingDateRule(new DateTime(1, 1, 1, 3,
0, 0),

3, 2, DayOfWeek.Sunday);
// Define adjustment rule
TimeSpan delta = new TimeSpan(1, 0, 0);
TimeZoneInfo.AdjustmentRule adjustment =
TimeZoneInfo.AdjustmentRule.CreateAdjustmentRule(new DateTime(1999, 10, 1),
DateTime.MaxValue.Date, delta,
startTransition, endTransition);
// Create array for adjustment rules
TimeZoneInfo.AdjustmentRule[] adjustments = {adjustment};
// Define other custom time zone arguments
string DisplayName = "(GMT-04:00) Antarctica/Palmer Time";
string standardName = "Palmer Standard Time";
string daylightName = "Palmer Daylight Time";
TimeSpan offset = new TimeSpan(-4, 0, 0);
TimeZoneInfo palmer = TimeZoneInfo.CreateCustomTimeZone(standardName,
offset, DisplayName, standardName, daylightName, adjustments);
resWriter.AddResource(palmer.Id.Replace(" ", String.Empty),
palmer.ToSerializedString());

// Save changes to .resx file


resWriter.Generate();
resWriter.Close();
writeStream.Close();
}

Este exemplo serializa objetos TimeZoneInfo para que eles estejam disponíveis em um
arquivo de recurso no tempo de compilação.

Como o método ResXResourceWriter.Generate adiciona informações completas de


cabeçalho a um arquivo de recurso .NET XML, ele não pode ser usado para adicionar
recursos a um arquivo. O exemplo lida com isso verificando o arquivo
SerializedTimeZones.resx e, se existir, armazenando todos os seus recursos além dos
dois fusos horários serializados em um objeto genérico Dictionary<TKey,TValue>. O
arquivo é então excluído e os recursos são adicionados a um novo arquivo
SerializedTimeZones.resx. Os dados de fuso horário serializados também são
adicionados a esse arquivo.

Os campos de chave (ou Nome) dos recursos não devem conter espaços inseridos. O
método Replace(String, String) é chamado para remover todos os espaços inseridos nos
identificadores de fuso horário antes de serem atribuídos ao arquivo de recurso.
Compilando o código
Este exemplo requer:

Que uma referência a System.Windows.Forms.dll e System.Core.dll seja adicionada


ao projeto.

Que os seguintes namespaces sejam importados:

C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;

Confira também
Datas, horas e fusos horários
Visão geral do fuso horário
Como: restaurar fusos horários de um recurso inserido

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: restaurar fusos horários de um
recurso inserido
Artigo • 10/05/2023

Este tópico descreve como restaurar fusos horários que foram salvos em um arquivo de
recurso. Para informações e instruções sobre como economizar fusos horários, confira
Como salvar fusos horários em um recurso inserido.

Para desserializar um objeto TimeZoneInfo de um recurso


inserido
1. Se o fuso horário a ser recuperado não for um fuso horário personalizado, tente
instanciá-lo usando o método FindSystemTimeZoneById.

2. Instancie um objeto ResourceManager passando o nome totalmente qualificado


do arquivo de recurso inserido e uma referência ao assembly que contém o
arquivo de recurso.

Se você não puder determinar o nome totalmente qualificado do arquivo de


recurso inserido, use o Ildasm.exe (Il Disassembler) para examinar o manifesto do
assembly. Uma entrada .mresource identifica o recurso. No exemplo, o nome
totalmente qualificado do recurso é SerializeTimeZoneData.SerializedTimeZones .

Se o arquivo de recurso estiver inserido no mesmo assembly que contém o código


de instanciação de fuso horário, você poderá recuperar uma referência a ele
chamando o método GetExecutingAssembly static ( Shared no Visual Basic).

3. Se a chamada ao método FindSystemTimeZoneById falhar ou se um fuso horário


personalizado for instanciado, recupere uma cadeia de caracteres que contém o
fuso horário serializado chamando o método ResourceManager.GetString.

4. Desserialize os dados de fuso horário chamando o método FromSerializedString.

Exemplo
O exemplo a seguir desserializa um objeto TimeZoneInfo armazenado em um arquivo
de recurso .NET XML inserido.

C#
private void DeserializeTimeZones()
{
TimeZoneInfo cst, palmer;
string timeZoneString;
ResourceManager resMgr = new
ResourceManager("SerializeTimeZoneData.SerializedTimeZones",
this.GetType().Assembly);

// Attempt to retrieve time zone from system


try
{
cst = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
}
catch (TimeZoneNotFoundException)
{
// Time zone not in system; retrieve from resource
timeZoneString = resMgr.GetString("CentralStandardTime");
if (! String.IsNullOrEmpty(timeZoneString))
{
cst = TimeZoneInfo.FromSerializedString(timeZoneString);
}
else
{
MessageBox.Show("Unable to create Central Standard Time Zone.
Application must exit.", "Application Error");
return;
}
}
// Retrieve custom time zone
try
{
timeZoneString = resMgr.GetString("PalmerStandardTime");
palmer = TimeZoneInfo.FromSerializedString(timeZoneString);
}
catch (MissingManifestResourceException)
{
MessageBox.Show("Unable to retrieve the Palmer Standard Time Zone from
the resource file. Application must exit.");
return;
}
}

Esse código ilustra o tratamento de exceções para garantir que um objeto TimeZoneInfo
exigido pelo aplicativo esteja presente. Primeiro, ele tenta criar uma instância de um
objeto TimeZoneInfo recuperando-o do Registro usando o método
FindSystemTimeZoneById. Se o fuso horário não puder ser instanciado, o código o
recuperará do arquivo de recurso inserido.

Como os dados para fusos horários personalizados (fusos horários instanciados usando
o método CreateCustomTimeZone) não são armazenados no Registro, o código não
chama a instanciação FindSystemTimeZoneById do fuso horário para Palmer, Antártica.
Em vez disso, ele procura imediatamente o arquivo de recurso inserido para recuperar
uma cadeia de caracteres que contém os dados do fuso horário antes de chamar o
método FromSerializedString.

Compilando o código
Este exemplo requer:

Que uma referência a System.Windows.Forms.dll e System.Core.dll seja adicionada


ao projeto.

Que os seguintes namespaces sejam importados:

C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Resources;
using System.Windows.Forms;

Confira também
Datas, horas e fusos horários
Visão geral do fuso horário
Como: salvar fusos horários em um recurso inserido

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
be found on GitHub, where you source. Provide feedback here.
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
System.DateTime struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

) Importante

As eras nos calendários japoneses se baseiam no reinado do Imperador e, portanto,


estão sujeitas a alterações. Por exemplo, 1º de maio de 2019 marcou o início da era
Reiwa no JapaneseCalendar e no JapaneseLunisolarCalendar. Tal alteração de eras
afeta todos os aplicativos que usam esses calendários. Para obter mais informações
e determinar se seus aplicativos são afetados, consulte Manipulando uma nova era
no calendário japonês no .NET. Para obter informações sobre como testar seus
aplicativos em sistemas Windows para garantir sua prontidão para a mudança de
era, consulte Preparar seu aplicativo para a mudança de era japonesa. Para
recursos no .NET que oferecem suporte a calendários com várias eras e para obter
práticas recomendadas ao trabalhar com calendários que oferecem suporte a várias
eras, consulte Trabalhando com eras.

Visão geral
O DateTime tipo de valor representa datas e horas com valores que variam de 00:00:00
(meia-noite), 1º de janeiro de 0001 Anno Domini (Era Comum) até 23:59:59, 31 de
dezembro de 9999 d.C. (c.e.) no calendário gregoriano.

Os valores de tempo são medidos em unidades de 100 nanossegundos chamadas


carrapatos. Uma data específica é o número de carrapatos desde 12:00 meia-noite, 1 de
janeiro de 0001 A.D. (C.E.) no GregorianCalendar calendário. O número exclui os ticks
que seriam adicionados por segundos bissextos. Por exemplo, um valor de ticks de
31241376000000000L representa a data sexta-feira, janeiro 01, 0100 12:00:00 meia-
noite. Um DateTime valor é sempre expresso no contexto de um calendário explícito ou
padrão.

7 Observação

Se você estiver trabalhando com um valor de ticks que deseja converter em algum
outro intervalo de tempo, como minutos ou segundos, use a constante , ,
TimeSpan.TicksPerHourTimeSpan.TicksPerMinute, TimeSpan.TicksPerSecondou
TimeSpan.TicksPerMillisecond para executar a TimeSpan.TicksPerDayconversão.
Por exemplo, para adicionar o número de segundos representado por um número
especificado de ticks ao Second componente de um DateTime valor, você pode
usar a expressão dateValue.Second + nTicks/Timespan.TicksPerSecond .

Você pode exibir a fonte para todo o conjunto de exemplos deste artigo em Visual
Basic , F# ou C# .

7 Observação

Uma alternativa à DateTime estrutura para trabalhar com valores de data e hora
em fusos horários específicos é a DateTimeOffset estrutura. A DateTimeOffset
estrutura armazena informações de data e hora em um campo privado e o número
de minutos pelos quais essa data e hora diferem do UTC em um campo privado
DateTimeInt16 . Isso possibilita que um valor reflita a hora em um fuso horário
específico, enquanto um DateTimeOffsetDateTime valor pode refletir
inequivocamente apenas UTC e a hora do fuso horário local. Para obter uma
discussão sobre quando usar a estrutura ou a DateTimeDateTimeOffset estrutura
ao trabalhar com valores de data e hora, consulte Escolhendo entre DateTime,
DateTimeOffset, TimeSpan e TimeZoneInfo.

Links rápidos para código de exemplo

7 Observação

Alguns exemplos de C# neste artigo são executados no playground e no


executador de código embutido Try.NET . Clique no botão Executar para executar
um exemplo em uma janela interativa. Ao executar o código, é possível modificá-lo
e executar o código modificado clicando em Executar novamente. O código
modificado será executado na janela interativa ou, se a compilação falhar, a janela
interativa exibirá todos as mensagens de erro do compilador C#.

O fuso horário local do executador de código embutido Try.NET e do


playground é o Tempo Universal Coordenado ou UTC. Isso pode afetar o
comportamento e a saída dos exemplos que ilustram os tipos DateTime,
DateTimeOffset e TimeZoneInfo e seus membros.

Este artigo inclui vários exemplos que usam o DateTime tipo:


Exemplos de inicialização
Invocar um construtor
Invocar o construtor implícito sem parâmetros
Atribuição do valor de retorno
Analisando uma cadeia de caracteres que representa uma data e hora
Sintaxe do Visual Basic para inicializar uma data e hora

Formatar DateTime objetos como exemplos de cadeias de


caracteres
Usar o formato de data e hora padrão
Formatar uma data e hora usando uma cultura específica
Formatar uma data e hora usando uma cadeia de caracteres de formato padrão ou
personalizada
Especificar uma cadeia de caracteres de formato e uma cultura específica
Formatar uma data e hora usando o padrão ISO 8601 para serviços Web

Analisar cadeias de caracteres como DateTime exemplos


de objetos
Usar Parse ou TryParse converter uma cadeia de caracteres em uma data e hora
Usar ParseExact ou TryParseExact converter uma cadeia de caracteres em um
formato conhecido
Converter da representação de cadeia de caracteres ISO 8601 para uma data e
hora

DateTime Exemplos de resolução

Explore a resolução de valores de data e hora


Comparando para igualdade dentro de uma tolerância

Exemplos de cultura e calendários


Exibir valores de data e hora usando calendários específicos da cultura
Analisar cadeias de caracteres de acordo com um calendário específico da cultura
Inicializar uma data e hora a partir do calendário de uma cultura específica
Acessando propriedades de data e hora usando o calendário de uma cultura
específica
Recuperando a semana do ano usando calendários específicos da cultura
Exemplos de persistência
Persistência de valores de data e hora como cadeias de caracteres no fuso horário
local
Persistência de valores de data e hora como cadeias de caracteres em um formato
invariante de cultura e hora
Persistência de valores de data e hora como inteiros
Persistência de valores de data e hora usando o XmlSerializer

Inicializar um objeto DateTime


Você pode atribuir um valor inicial a um novo DateTime valor de muitas maneiras
diferentes:

Chamando um construtor, seja aquele em que você especifica argumentos para


valores ou usa o construtor implícito sem parâmetros.
Atribuindo um DateTime ao valor de retorno de uma propriedade ou método.
Analisando um DateTime valor de sua representação de cadeia de caracteres.
Usando recursos de linguagem específicos do Visual Basic para instanciar um
DateTime arquivo .

Os trechos de código a seguir mostram exemplos de cada um.

Invocar construtores
Você chama qualquer uma das sobrecargas do construtor que especificam elementos
do DateTime valor de data e hora (como o ano, mês e dia, ou o número de ticks). O
código a seguir cria uma data específica usando o construtor especificando o DateTime
ano, mês, dia, hora, minuto e segundo.

C#

var date1 = new DateTime(2008, 5, 1, 8, 30, 52);


Console.WriteLine(date1);

Você invoca o DateTime construtor implícito sem parâmetros da estrutura quando


deseja um DateTime inicializado para seu valor padrão. (Para obter detalhes sobre o
construtor implícito sem parâmetros de um tipo de valor, consulte Tipos de valor.)
Alguns compiladores também oferecem suporte à declaração de um valor sem atribuir
explicitamente um DateTime valor a ele. Criar um valor sem uma inicialização explícita
também resulta no valor padrão. O exemplo a seguir ilustra o DateTime construtor
implícito sem parâmetros em C# e Visual Basic, bem como uma DateTime declaração
sem atribuição no Visual Basic.

C#

var dat1 = new DateTime();


// The following method call displays 1/1/0001 12:00:00 AM.
Console.WriteLine(dat1.ToString(System.Globalization.CultureInfo.InvariantCu
lture));
// The following method call displays True.
Console.WriteLine(dat1.Equals(DateTime.MinValue));

Atribuir um valor calculado


Você pode atribuir ao DateTime objeto um valor de data e hora retornado por uma
propriedade ou método. O exemplo a seguir atribui a data e a hora atuais, a data e a
hora UTC (Tempo Universal Coordenado) atuais e a data atual a três novas DateTime
variáveis.

C#

DateTime date1 = DateTime.Now;


DateTime date2 = DateTime.UtcNow;
DateTime date3 = DateTime.Today;

Analisar uma cadeia de caracteres que representa um


DateTime
Os Parsemétodos , ParseExact, TryParsee todos convertem uma cadeia de caracteres em
seu valor de data e TryParseExact hora equivalente. Os exemplos a seguir usam os Parse
métodos e para analisar uma cadeia de caracteres e ParseExact convertê-la em um
DateTime valor. O segundo formato usa um formulário suportado pelo padrão ISO
8601 para uma data e hora de representação no formato de cadeia de caracteres. Essa
representação padrão é frequentemente usada para transferir informações de data em
serviços Web.

C#

var dateString = "5/1/2008 8:30:52 AM";


DateTime date1 = DateTime.Parse(dateString,

System.Globalization.CultureInfo.InvariantCulture);
var iso8601String = "20080501T08:30:52Z";
DateTime dateISO8602 = DateTime.ParseExact(iso8601String,
"yyyyMMddTHH:mm:ssZ",

System.Globalization.CultureInfo.InvariantCulture);

Os TryParse métodos and TryParseExact indicam se uma cadeia de caracteres é uma


representação válida de um DateTime valor e, se for, executa a conversão.

Sintaxe específica da linguagem para Visual Basic


A instrução Visual Basic a seguir inicializa um novo DateTime valor.

VB

Dim date1 As Date = #5/1/2008 8:30:52AM#

Valores DateTime e suas representações de


cadeia de caracteres
Internamente, todos os DateTime valores são representados como o número de
carrapatos (o número de intervalos de 100 nanossegundos) decorridos desde 12:00:00
meia-noite, 1 de janeiro de 0001. O valor real DateTime é independente da maneira
como esse valor aparece quando exibido. A aparência de um valor é o resultado de uma
operação de formatação que converte um DateTime valor em sua representação de
cadeia de caracteres.

A aparência dos valores de data e hora depende da cultura, dos padrões internacionais,
dos requisitos do aplicativo e da preferência pessoal. A DateTime estrutura oferece
flexibilidade na formatação de valores de data e hora através de sobrecargas de
ToString. O método padrão retorna a representação de cadeia de caracteres de um valor
de data e hora usando o padrão DateTime.ToString() de data curta e tempo longo da
cultura atual. O exemplo a seguir usa o método padrão DateTime.ToString() . Ele exibe a
data e a hora usando o padrão de data curta e tempo longo para a cultura atual. A
cultura en-US é a cultura atual no computador em que o exemplo foi executado.

C#

var date1 = new DateTime(2008, 3, 1, 7, 0, 0);


Console.WriteLine(date1.ToString());
// For en-US culture, displays 3/1/2008 7:00:00 AM
Talvez seja necessário formatar datas em uma cultura específica para oferecer suporte a
cenários da Web em que o servidor pode estar em uma cultura diferente do cliente.
Você especifica a cultura usando o DateTime.ToString(IFormatProvider) método para
criar a representação de data curta e longa em uma cultura específica. O exemplo a
seguir usa o método para exibir a data e a hora usando o padrão de data curta e tempo
longo para a DateTime.ToString(IFormatProvider) cultura fr-FR.

C#

var date1 = new DateTime(2008, 3, 1, 7, 0, 0);


Console.WriteLine(date1.ToString(System.Globalization.CultureInfo.CreateSpec
ificCulture("fr-FR")));
// Displays 01/03/2008 07:00:00

Outros aplicativos podem exigir representações de cadeia de caracteres diferentes de


uma data. O DateTime.ToString(String) método retorna a representação de cadeia de
caracteres definida por um especificador de formato padrão ou personalizado usando
as convenções de formatação da cultura atual. O exemplo a seguir usa o método para
exibir o padrão completo de data e hora para a cultura en-US, a cultura atual no
computador no qual o DateTime.ToString(String) exemplo foi executado.

C#

var date1 = new DateTime(2008, 3, 1, 7, 0, 0);


Console.WriteLine(date1.ToString("F"));
// Displays Saturday, March 01, 2008 7:00:00 AM

Finalmente, você pode especificar a cultura e o formato usando o


DateTime.ToString(String, IFormatProvider) método. O exemplo a seguir usa o método
para exibir o padrão completo de data e hora para a DateTime.ToString(String,
IFormatProvider) cultura fr-FR.

C#

var date1 = new DateTime(2008, 3, 1, 7, 0, 0);


Console.WriteLine(date1.ToString("F", new
System.Globalization.CultureInfo("fr-FR")));
// Displays samedi 1 mars 2008 07:00:00

A DateTime.ToString(String) sobrecarga também pode ser usada com uma cadeia de


caracteres de formato personalizado para especificar outros formatos. O exemplo a
seguir mostra como formatar uma cadeia de caracteres usando o formato padrão ISO
8601 frequentemente usado para serviços Web. O formato Iso 8601 não tem uma
cadeia de caracteres de formato padrão correspondente.
C#

var date1 = new DateTime(2008, 3, 1, 7, 0, 0, DateTimeKind.Utc);


Console.WriteLine(date1.ToString("yyyy-MM-ddTHH:mm:sszzz",
System.Globalization.CultureInfo.InvariantCulture));
// Displays 2008-03-01T07:00:00+00:00

Para obter mais informações sobre como formatar DateTime valores, consulte Cadeias
de caracteres de formato de data e hora padrão e Cadeias de caracteres de formato de
data e hora personalizadas.

Analisar valores DateTime de cadeias de


caracteres
A análise converte a representação de cadeia de caracteres de uma data e hora em um
DateTime valor. Normalmente, as cadeias de caracteres de data e hora têm dois usos
diferentes em aplicativos:

Uma data e hora assumem uma variedade de formas e refletem as convenções da


cultura atual ou de uma cultura específica. Por exemplo, um aplicativo permite que
um usuário cuja cultura atual é en-US insira um valor de data como "15/12/2013"
ou "15 de dezembro de 2013". Ele permite que um usuário cuja cultura atual é en-
gb insira um valor de data como "15/12/2013" ou "15 de dezembro de 2013".

Uma data e hora são representadas em um formato predefinido. Por exemplo, um


aplicativo serializa uma data como "20130103" independentemente da cultura na
qual o aplicativo está sendo executado. Um aplicativo pode exigir que as datas
sejam inseridas no formato de data curta da cultura atual.

Use o Parse método or TryParse para converter uma cadeia de caracteres de um dos
formatos comuns de data e hora usados por uma cultura em um DateTime valor. O
exemplo a seguir mostra como você pode usar TryParse para converter cadeias de
caracteres de data em diferentes formatos específicos de cultura em um DateTime valor.
Ele altera a cultura atual para inglês (Reino Unido) e chama o GetDateTimeFormats()
método para gerar uma matriz de cadeias de caracteres de data e hora. Em seguida, ele
passa cada elemento na matriz para o TryParse método. A saída do exemplo mostra que
o método de análise foi capaz de converter com êxito cada uma das cadeias de
caracteres de data e hora específicas da cultura.

C#

System.Threading.Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.CreateSpecificCulture("en-GB");
var date1 = new DateTime(2013, 6, 1, 12, 32, 30);
var badFormats = new List<String>();

Console.WriteLine($"{"Date String",-37} {"Date",-19}\n");


foreach (var dateString in date1.GetDateTimeFormats())
{
DateTime parsedDate;
if (DateTime.TryParse(dateString, out parsedDate))
Console.WriteLine($"{dateString,-37}
{DateTime.Parse(dateString),-19}");
else
badFormats.Add(dateString);
}

// Display strings that could not be parsed.


if (badFormats.Count > 0)
{
Console.WriteLine("\nStrings that could not be parsed: ");
foreach (var badFormat in badFormats)
Console.WriteLine($" {badFormat}");
}
// Press "Run" to see the output.

Use os ParseExact métodos e TryParseExact para converter uma cadeia de caracteres que
deve corresponder a um determinado formato ou formatos em um DateTime valor.
Você especifica uma ou mais cadeias de caracteres de formato de data e hora como um
parâmetro para o método de análise. O exemplo a seguir usa o TryParseExact(String,
String[], IFormatProvider, DateTimeStyles, DateTime) método para converter cadeias de
caracteres que devem estar em um formato "aaaaMMdd" ou um formato "HHmmss" em
DateTime valores.

C#

string[] formats = { "yyyyMMdd", "HHmmss" };


string[] dateStrings = { "20130816", "20131608", " 20130816 ",
"115216", "521116", " 115216 " };
DateTime parsedDate;

foreach (var dateString in dateStrings)


{
if (DateTime.TryParseExact(dateString, formats, null,

System.Globalization.DateTimeStyles.AllowWhiteSpaces |

System.Globalization.DateTimeStyles.AdjustToUniversal,
out parsedDate))
Console.WriteLine($"{dateString} --> {parsedDate:g}");
else
Console.WriteLine($"Cannot convert {dateString}");
}
// The example displays the following output:
// 20130816 --> 8/16/2013 12:00 AM
// Cannot convert 20131608
// 20130816 --> 8/16/2013 12:00 AM
// 115216 --> 4/22/2013 11:52 AM
// Cannot convert 521116
// 115216 --> 4/22/2013 11:52 AM

Um uso comum é ParseExact converter uma representação de cadeia de caracteres de


um serviço Web, geralmente no formato padrão ISO 8601 . O código a seguir mostra
a sequência de caracteres de formato correto a ser usada:

C#

var iso8601String = "20080501T08:30:52Z";


DateTime dateISO8602 = DateTime.ParseExact(iso8601String,
"yyyyMMddTHH:mm:ssZ",
System.Globalization.CultureInfo.InvariantCulture);
Console.WriteLine($"{iso8601String} --> {dateISO8602:g}");

Se uma cadeia de caracteres não puder ser analisada, os Parse métodos e ParseExact
lançarão uma exceção. Os TryParse métodos e TryParseExact retornam um Boolean valor
que indica se a conversão foi bem-sucedida ou falhou. Você deve usar os métodos or
TryParseExact em cenários em que o TryParse desempenho é importante. A operação de
análise para cadeias de caracteres de data e hora tende a ter uma alta taxa de falha, e o
tratamento de exceções é caro. Use esses métodos se as cadeias de caracteres forem
inseridas por usuários ou provenientes de uma fonte desconhecida.

Para obter mais informações sobre como analisar valores de data e hora, consulte
Analisando cadeias de caracteres de data e hora.

Valores DateTime
As descrições dos valores de tempo no DateTime tipo geralmente são expressas usando
o padrão UTC (Tempo Universal Coordenado). Tempo Universal Coordenado é o nome
internacionalmente reconhecido para Greenwich Mean Time (GMT). Tempo Universal
Coordenado é o tempo medido a zero graus de longitude, o ponto de origem UTC. O
horário de verão não se aplica ao UTC.

A hora local é relativa a um fuso horário específico. Um fuso horário está associado a
um deslocamento de fuso horário. Um deslocamento de fuso horário é o deslocamento
do fuso horário medido em horas a partir do ponto de origem UTC. Além disso, a hora
local é opcionalmente afetada pelo horário de verão, que adiciona ou subtrai um ajuste
de intervalo de tempo. A hora local é calculada adicionando o deslocamento de fuso
horário ao UTC e ajustando para o horário de verão, se necessário. O deslocamento de
fuso horário no ponto de origem UTC é zero.

A hora UTC é adequada para cálculos, comparações e armazenamento de datas e horas


em arquivos. A hora local é apropriada para exibição em interfaces de usuário de
aplicativos de desktop. Os aplicativos com reconhecimento de fuso horário (como
muitos aplicativos Web) também precisam trabalhar com vários outros fusos horários.

Se a propriedade de um DateTime objeto for DateTimeKind.Unspecified, não será


especificado se a Kind hora representada é hora local, hora UTC ou uma hora em algum
outro fuso horário.

Resolução DateTime

7 Observação

Como alternativa para executar aritmética de data e hora em DateTime valores


para medir o tempo decorrido, você pode usar a Stopwatch classe.

A Ticks propriedade expressa valores de data e hora em unidades de um décimo


milionésimo de segundo. A Millisecond propriedade retorna os milésimos de segundo
em um valor de data e hora. O uso de chamadas repetidas para a propriedade para
medir o DateTime.Now tempo decorrido depende do relógio do sistema. O relógio do
sistema nos sistemas Windows 7 e Windows 8 tem uma resolução de aproximadamente
15 milissegundos. Essa resolução afeta pequenos intervalos de tempo inferiores a 100
milissegundos.

O exemplo a seguir ilustra a dependência dos valores de data e hora atuais na resolução
do relógio do sistema. No exemplo, um loop externo se repete 20 vezes, e um loop
interno serve para atrasar o loop externo. Se o valor do contador de loop externo for 10,
uma chamada para o Thread.Sleep método introduz um atraso de cinco milissegundos.
O exemplo a seguir mostra o número de milissegundos retornados pelas alterações de
DateTime.Now.Milliseconds propriedade somente após a chamada para Thread.Sleep.

C#

string output = "";


for (int ctr = 0; ctr <= 20; ctr++)
{
output += String.Format($"{DateTime.Now.Millisecond}\n");
// Introduce a delay loop.
for (int delay = 0; delay <= 1000; delay++)
{ }
if (ctr == 10)
{
output += "Thread.Sleep called...\n";
System.Threading.Thread.Sleep(5);
}
}
Console.WriteLine(output);
// Press "Run" to see the output.

Operações DateTime
Um cálculo usando uma DateTime estrutura, como Add ou Subtract, não modifica o
valor da estrutura. Em vez disso, o cálculo retorna uma nova DateTime estrutura cujo
valor é o resultado do cálculo.

As operações de conversão entre fusos horários (como entre UTC e hora local, ou entre
um fuso horário e outro) levam em conta o horário de verão, mas as operações
aritméticas e de comparação não.

A DateTime estrutura em si oferece suporte limitado para a conversão de um fuso


horário para outro. Você pode usar o método para converter UTC para hora local, ou
você pode usar o ToLocalTimeToUniversalTime método para converter de hora local
para UTC. No entanto, um conjunto completo de métodos de conversão de fuso horário
está disponível na TimeZoneInfo classe. Você converte a hora em qualquer um dos fusos
horários do mundo para a hora em qualquer outro fuso horário usando esses métodos.

Cálculos e comparações de DateTime objetos são significativos somente se os objetos


representam horas no mesmo fuso horário. Você pode usar um objeto para representar
o fuso horário de um TimeZoneInfoDateTime valor, embora os dois estejam fracamente
acoplados. Um DateTime objeto não tem uma propriedade que retorna um objeto que
representa o fuso horário desse valor de data e hora. A Kind propriedade indica se um
representa UTC DateTime , hora local ou não é especificado. Em um aplicativo com
reconhecimento de fuso horário, você deve confiar em algum mecanismo externo para
determinar o fuso horário no qual um DateTime objeto foi criado. Você pode usar uma
estrutura que encapsula o valor e o objeto que representa o DateTime fuso
DateTimeTimeZoneInfo horário do valor. Para obter detalhes sobre como usar o UTC em
cálculos e comparações com valores, consulte Executando operações aritméticas com
DateTime datas e horas.

Cada DateTime membro usa implicitamente o calendário gregoriano para executar sua
operação. As exceções são métodos que especificam implicitamente um calendário. Isso
inclui construtores que especificam um calendário e métodos com um parâmetro
derivado de IFormatProvider, como System.Globalization.DateTimeFormatInfo.

As operações por membros do DateTime tipo levam em conta detalhes como anos
bissextos e o número de dias em um mês.

Valores e calendários DateTime


A biblioteca de classes .NET inclui várias classes de calendário, todas derivadas da
Calendar classe. São elas:

A classe ChineseLunisolarCalendar.
A classe EastAsianLunisolarCalendar.
A classe GregorianCalendar.
A classe HebrewCalendar.
A classe HijriCalendar.
A classe JapaneseCalendar.
A classe JapaneseLunisolarCalendar.
A classe JulianCalendar.
A classe KoreanCalendar.
A classe KoreanLunisolarCalendar.
A classe PersianCalendar.
A classe TaiwanCalendar.
A classe TaiwanLunisolarCalendar.
A classe ThaiBuddhistCalendar.
A classe UmAlQuraCalendar.

) Importante

As eras nos calendários japoneses se baseiam no reinado do Imperador e, portanto,


estão sujeitas a alterações. Por exemplo, 1º de maio de 2019 marcou o início da era
Reiwa no JapaneseCalendar e no JapaneseLunisolarCalendar. Tal alteração de eras
afeta todos os aplicativos que usam esses calendários. Para obter mais informações
e determinar se seus aplicativos são afetados, consulte Manipulando uma nova era
no calendário japonês no .NET. Para obter informações sobre como testar seus
aplicativos em sistemas Windows para garantir sua prontidão para a mudança de
era, consulte Preparar seu aplicativo para a mudança de era japonesa. Para
recursos no .NET que oferecem suporte a calendários com várias eras e para obter
práticas recomendadas ao trabalhar com calendários que oferecem suporte a várias
eras, consulte Trabalhando com eras.
Cada cultura usa um calendário padrão definido por sua propriedade somente
CultureInfo.Calendar leitura. Cada cultura pode oferecer suporte a um ou mais
calendários definidos por sua propriedade somente CultureInfo.OptionalCalendars
leitura. O calendário usado atualmente por um objeto específico CultureInfo é definido
por sua DateTimeFormatInfo.Calendar propriedade. Deve ser um dos calendários
encontrados na CultureInfo.OptionalCalendars matriz.

O calendário atual de uma cultura é usado em todas as operações de formatação dessa


cultura. Por exemplo, o calendário padrão da cultura budista tailandesa é o calendário
da Era Budista Tailandesa, que é representado pela ThaiBuddhistCalendar classe.
Quando um CultureInfo objeto que representa a cultura budista tailandesa é usado em
uma operação de formatação de data e hora, o calendário da Era Budista Tailandesa é
usado por padrão. O calendário gregoriano será usado somente se a propriedade da
DateTimeFormatInfo.Calendar cultura for alterada, como mostra o exemplo a seguir:

C#

var thTH = new System.Globalization.CultureInfo("th-TH");


var value = new DateTime(2016, 5, 28);

Console.WriteLine(value.ToString(thTH));

thTH.DateTimeFormat.Calendar = new System.Globalization.GregorianCalendar();


Console.WriteLine(value.ToString(thTH));
// The example displays the following output:
// 28/5/2559 0:00:00
// 28/5/2016 0:00:00

O calendário atual de uma cultura também é usado em todas as operações de análise


dessa cultura, como mostra o exemplo a seguir.

C#

var thTH = new System.Globalization.CultureInfo("th-TH");


var value = DateTime.Parse("28/05/2559", thTH);
Console.WriteLine(value.ToString(thTH));

thTH.DateTimeFormat.Calendar = new System.Globalization.GregorianCalendar();


Console.WriteLine(value.ToString(thTH));
// The example displays the following output:
// 28/5/2559 0:00:00
// 28/5/2016 0:00:00

Você instancia um valor usando os elementos de data e hora (número do ano, mês e
dia) de um calendário específico chamando um construtor DateTime que inclui um
parâmetro e passando-lhe um DateTimeCalendarcalendar objeto que representa esse
calendário. O exemplo a seguir usa os elementos de data e hora do
ThaiBuddhistCalendar calendário.

C#

var thTH = new System.Globalization.CultureInfo("th-TH");


var dat = new DateTime(2559, 5, 28, thTH.DateTimeFormat.Calendar);
Console.WriteLine($"Thai Buddhist era date: {dat.ToString("d", thTH)}");
Console.WriteLine($"Gregorian date: {dat:d}");
// The example displays the following output:
// Thai Buddhist Era Date: 28/5/2559
// Gregorian Date: 28/05/2016

DateTime construtores que não incluem um calendar parâmetro assumem que os


elementos de data e hora são expressos como unidades no calendário gregoriano.

Todas as outras DateTime propriedades e métodos usam o calendário gregoriano. Por


exemplo, a DateTime.Year propriedade retorna o ano no calendário gregoriano e o
método assume que o DateTime.IsLeapYear(Int32) year parâmetro é um ano no
calendário gregoriano. Cada DateTime membro que usa o calendário gregoriano tem
um membro correspondente da Calendar classe que usa um calendário específico. Por
exemplo, o método retorna o ano em um calendário específico e o método interpreta o
Calendar.GetYearCalendar.IsLeapYear year parâmetro como um número de ano em um
calendário específico. O exemplo a seguir usa os membros e os DateTime membros
correspondentes da ThaiBuddhistCalendar classe.

C#

var thTH = new System.Globalization.CultureInfo("th-TH");


var cal = thTH.DateTimeFormat.Calendar;
var dat = new DateTime(2559, 5, 28, cal);
Console.WriteLine("Using the Thai Buddhist Era calendar:");
Console.WriteLine($"Date: {dat.ToString("d", thTH)}");
Console.WriteLine($"Year: {cal.GetYear(dat)}");
Console.WriteLine($"Leap year: {cal.IsLeapYear(cal.GetYear(dat))}\n");

Console.WriteLine("Using the Gregorian calendar:");


Console.WriteLine($"Date: {dat:d}");
Console.WriteLine($"Year: {dat.Year}");
Console.WriteLine($"Leap year: {DateTime.IsLeapYear(dat.Year)}");
// The example displays the following output:
// Using the Thai Buddhist Era calendar
// Date : 28/5/2559
// Year: 2559
// Leap year : True
//
// Using the Gregorian calendar
// Date : 28/05/2016
// Year: 2016
// Leap year : True

A DateTime estrutura inclui uma DayOfWeek propriedade que retorna o dia da semana
no calendário gregoriano. Ele não inclui um membro que permite que você recupere o
número da semana do ano. Para recuperar a semana do ano, chame o método do
Calendar.GetWeekOfYear calendário individual. O exemplo a seguir ilustra esse cenário.

C#

var thTH = new System.Globalization.CultureInfo("th-TH");


var thCalendar = thTH.DateTimeFormat.Calendar;
var dat = new DateTime(1395, 8, 18, thCalendar);
Console.WriteLine("Using the Thai Buddhist Era calendar:");
Console.WriteLine($"Date: {dat.ToString("d", thTH)}");
Console.WriteLine($"Day of Week: {thCalendar.GetDayOfWeek(dat)}");
Console.WriteLine($"Week of year: {thCalendar.GetWeekOfYear(dat,
System.Globalization.CalendarWeekRule.FirstDay, DayOfWeek.Sunday)}\n");

var greg = new System.Globalization.GregorianCalendar();


Console.WriteLine("Using the Gregorian calendar:");
Console.WriteLine($"Date: {dat:d}");
Console.WriteLine($"Day of Week: {dat.DayOfWeek}");
Console.WriteLine($"Week of year: {greg.GetWeekOfYear(dat,
System.Globalization.CalendarWeekRule.FirstDay,DayOfWeek.Sunday)}");
// The example displays the following output:
// Using the Thai Buddhist Era calendar
// Date : 18/8/1395
// Day of Week: Sunday
// Week of year: 34
//
// Using the Gregorian calendar
// Date : 18/08/0852
// Day of Week: Sunday
// Week of year: 34

Para obter mais informações sobre datas e calendários, consulte Trabalhando com
calendários.

Manter valores de DateTime


Você pode persistir DateTime valores das seguintes maneiras:

Converta-os em cadeias de caracteres e persista as cadeias de caracteres.


Converta-os em valores inteiros de 64 bits (o valor da Ticks propriedade) e persista
os inteiros.
Serialize os valores DateTime.
Você deve garantir que a rotina que restaura os DateTime valores não perca dados ou
lance uma exceção, independentemente da técnica escolhida. DateTime valores devem
ser de ida e volta. Ou seja, o valor original e o valor restaurado devem ser os mesmos. E
se o valor original DateTime representa um único instante de tempo, ele deve identificar
o mesmo momento de tempo em que é restaurado.

Valores persistentes como cadeias de caracteres


Para restaurar DateTime com êxito os valores que são persistentes como cadeias de
caracteres, execute estas regras:

Faça as mesmas suposições sobre a formatação específica da cultura ao restaurar a


cadeia de caracteres como quando a persistiu. Para garantir que uma cadeia de
caracteres possa ser restaurada em um sistema cuja cultura atual seja diferente da
cultura do sistema em que foi salva, chame a sobrecarga para salvar a cadeia de
caracteres usando as convenções da cultura invariante ToString . Chame o
Parse(String, IFormatProvider, DateTimeStyles) ou TryParse(String, IFormatProvider,
DateTimeStyles, DateTime) sobrecargas para restaurar a cadeia de caracteres
usando as convenções da cultura invariante. Nunca use o ToString(), ou
TryParse(String, DateTime) sobrecargas, Parse(String)que usam as convenções da
cultura atual.

Se a data representar um único momento de tempo, certifique-se de que ela


represente o mesmo momento no tempo em que foi restaurada, mesmo em um
fuso horário diferente. Converta o DateTime valor em Tempo Universal
Coordenado (UTC) antes de salvá-lo ou usar DateTimeOffset.

O erro mais comum cometido ao persistir DateTime valores como cadeias de caracteres
é confiar nas convenções de formatação da cultura padrão ou atual. Os problemas
surgem se a cultura atual for diferente ao salvar e restaurar as cadeias de caracteres. O
exemplo a seguir ilustra esses problemas. Ele salva cinco datas usando as convenções
de formatação da cultura atual, que neste caso é o inglês (Estados Unidos). Ele restaura
as datas usando as convenções de formatação de uma cultura diferente, que neste caso
é o inglês (Reino Unido). Como as convenções de formatação das duas culturas são
diferentes, duas das datas não podem ser restauradas e as três datas restantes são
interpretadas incorretamente. Além disso, se os valores de data e hora originais
representarem momentos únicos no tempo, os horários restaurados estarão incorretos
porque as informações de fuso horário serão perdidas.

C#

public static void PersistAsLocalStrings()


{
SaveLocalDatesAsString();
RestoreLocalDatesFromString();
}

private static void SaveLocalDatesAsString()


{
DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0),
new DateTime(2014, 7, 10, 23, 49, 0),
new DateTime(2015, 1, 10, 1, 16, 0),
new DateTime(2014, 12, 20, 21, 45, 0),
new DateTime(2014, 6, 2, 15, 14, 0) };
string? output = null;

Console.WriteLine($"Current Time Zone:


{TimeZoneInfo.Local.DisplayName}");
Console.WriteLine($"The dates on an
{Thread.CurrentThread.CurrentCulture.Name} system:");
for (int ctr = 0; ctr < dates.Length; ctr++)
{
Console.WriteLine(dates[ctr].ToString("f"));
output += dates[ctr].ToString() + (ctr != dates.Length - 1 ? "|" :
"");
}
var sw = new StreamWriter(filenameTxt);
sw.Write(output);
sw.Close();
Console.WriteLine("Saved dates...");
}

private static void RestoreLocalDatesFromString()


{
TimeZoneInfo.ClearCachedData();
Console.WriteLine($"Current Time Zone:
{TimeZoneInfo.Local.DisplayName}");
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-GB");
StreamReader sr = new StreamReader(filenameTxt);
string[] inputValues = sr.ReadToEnd().Split(new char[] { '|' },

StringSplitOptions.RemoveEmptyEntries);
sr.Close();
Console.WriteLine("The dates on an {0} system:",
Thread.CurrentThread.CurrentCulture.Name);
foreach (var inputValue in inputValues)
{
DateTime dateValue;
if (DateTime.TryParse(inputValue, out dateValue))
{
Console.WriteLine($"'{inputValue}' --> {dateValue:f}");
}
else
{
Console.WriteLine($"Cannot parse '{inputValue}'");
}
}
Console.WriteLine("Restored dates...");
}
// When saved on an en-US system, the example displays the following output:
// Current Time Zone: (UTC-08:00) Pacific Time (US & Canada)
// The dates on an en-US system:
// Saturday, June 14, 2014 6:32 AM
// Thursday, July 10, 2014 11:49 PM
// Saturday, January 10, 2015 1:16 AM
// Saturday, December 20, 2014 9:45 PM
// Monday, June 02, 2014 3:14 PM
// Saved dates...
//
// When restored on an en-GB system, the example displays the following
output:
// Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London
// The dates on an en-GB system:
// Cannot parse //6/14/2014 6:32:00 AM//
// //7/10/2014 11:49:00 PM// --> 07 October 2014 23:49
// //1/10/2015 1:16:00 AM// --> 01 October 2015 01:16
// Cannot parse //12/20/2014 9:45:00 PM//
// //6/2/2014 3:14:00 PM// --> 06 February 2014 15:14
// Restored dates...

Para valores de ida DateTime e volta com êxito, execute estas etapas:

1. Se os valores representarem momentos únicos de tempo, converta-os da hora


local para UTC chamando o ToUniversalTime método.
2. Converta as datas em suas representações de cadeia de caracteres chamando a
ToString(String, IFormatProvider) sobrecarga ou String.Format(IFormatProvider,
String, Object[]) . Use as convenções de formatação da cultura invariante
especificando CultureInfo.InvariantCulture como o provider argumento.
Especifique que o valor deve ser de ida e volta usando a cadeia de caracteres de
formato padrão "O" ou "R".

Para restaurar os valores persistentes DateTime sem perda de dados, execute estas
etapas:

1. Analise os dados chamando a ParseExact sobrecarga ou TryParseExact . Especifique


CultureInfo.InvariantCulture como o argumento e use a mesma cadeia de
caracteres de formato padrão usada para o provider argumento durante a format
conversão. Inclua o DateTimeStyles.RoundtripKind styles valor no argumento.
2. Se os DateTime valores representarem momentos únicos no tempo, chame o
ToLocalTime método para converter a data analisada de UTC para hora local.

O exemplo a seguir usa a cultura invariante e a cadeia de caracteres de formato padrão


"O" para garantir que DateTime os valores salvos e restaurados representem o mesmo
momento no tempo, independentemente do sistema, cultura ou fuso horário dos
sistemas de origem e destino.

C#

public static void PersistAsInvariantStrings()


{
SaveDatesAsInvariantStrings();
RestoreDatesAsInvariantStrings();
}

private static void SaveDatesAsInvariantStrings()


{
DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0),
new DateTime(2014, 7, 10, 23, 49, 0),
new DateTime(2015, 1, 10, 1, 16, 0),
new DateTime(2014, 12, 20, 21, 45, 0),
new DateTime(2014, 6, 2, 15, 14, 0) };
string? output = null;

Console.WriteLine($"Current Time Zone:


{TimeZoneInfo.Local.DisplayName}");
Console.WriteLine($"The dates on an
{Thread.CurrentThread.CurrentCulture.Name} system:");
for (int ctr = 0; ctr < dates.Length; ctr++)
{
Console.WriteLine(dates[ctr].ToString("f"));
output += dates[ctr].ToUniversalTime().ToString("O",
CultureInfo.InvariantCulture)
+ (ctr != dates.Length - 1 ? "|" : "");
}
var sw = new StreamWriter(filenameTxt);
sw.Write(output);
sw.Close();
Console.WriteLine("Saved dates...");
}

private static void RestoreDatesAsInvariantStrings()


{
TimeZoneInfo.ClearCachedData();
Console.WriteLine("Current Time Zone: {0}",
TimeZoneInfo.Local.DisplayName);
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-GB");
StreamReader sr = new StreamReader(filenameTxt);
string[] inputValues = sr.ReadToEnd().Split(new char[] { '|' },

StringSplitOptions.RemoveEmptyEntries);
sr.Close();
Console.WriteLine("The dates on an {0} system:",
Thread.CurrentThread.CurrentCulture.Name);
foreach (var inputValue in inputValues)
{
DateTime dateValue;
if (DateTime.TryParseExact(inputValue, "O",
CultureInfo.InvariantCulture,
DateTimeStyles.RoundtripKind, out dateValue))
{
Console.WriteLine($"'{inputValue}' -->
{dateValue.ToLocalTime():f}");
}
else
{
Console.WriteLine("Cannot parse '{0}'", inputValue);
}
}
Console.WriteLine("Restored dates...");
}
// When saved on an en-US system, the example displays the following output:
// Current Time Zone: (UTC-08:00) Pacific Time (US & Canada)
// The dates on an en-US system:
// Saturday, June 14, 2014 6:32 AM
// Thursday, July 10, 2014 11:49 PM
// Saturday, January 10, 2015 1:16 AM
// Saturday, December 20, 2014 9:45 PM
// Monday, June 02, 2014 3:14 PM
// Saved dates...
//
// When restored on an en-GB system, the example displays the following
output:
// Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London
// The dates on an en-GB system:
// '2014-06-14T13:32:00.0000000Z' --> 14 June 2014 14:32
// '2014-07-11T06:49:00.0000000Z' --> 11 July 2014 07:49
// '2015-01-10T09:16:00.0000000Z' --> 10 January 2015 09:16
// '2014-12-21T05:45:00.0000000Z' --> 21 December 2014 05:45
// '2014-06-02T22:14:00.0000000Z' --> 02 June 2014 23:14
// Restored dates...

Valores persistentes como inteiros


Você pode persistir uma data e hora como um valor que representa um Int64 número
de ticks. Nesse caso, você não precisa considerar a cultura dos sistemas em que os
DateTime valores são persistidos e restaurados.

Para manter um valor como um DateTime inteiro:

1. Se os valores representarem momentos únicos no tempo, converta-os DateTime


em UTC chamando o ToUniversalTime método.
2. Recupere o número de ticks representados pelo DateTime valor de sua Ticks
propriedade.

Para restaurar um valor que foi persistido como um DateTime inteiro:


1. Instancie um novo DateTime objeto passando o Int64 valor para o DateTime(Int64)
construtor.
2. Se o valor representar um único momento no tempo, converta-o de UTC para a
hora local chamando o DateTimeToLocalTime método.

O exemplo a seguir persiste uma matriz de DateTime valores como inteiros em um


sistema no fuso horário do Pacífico dos EUA. Ele o restaura em um sistema na zona UTC.
O arquivo que contém os inteiros inclui um Int32 valor que indica o número total de
Int64 valores que imediatamente o seguem.

C#

public static void PersistAsIntegers()


{
SaveDatesAsInts();
RestoreDatesAsInts();
}

private static void SaveDatesAsInts()


{
DateTime[] dates = { new DateTime(2014, 6, 14, 6, 32, 0),
new DateTime(2014, 7, 10, 23, 49, 0),
new DateTime(2015, 1, 10, 1, 16, 0),
new DateTime(2014, 12, 20, 21, 45, 0),
new DateTime(2014, 6, 2, 15, 14, 0) };

Console.WriteLine($"Current Time Zone:


{TimeZoneInfo.Local.DisplayName}");
Console.WriteLine($"The dates on an
{Thread.CurrentThread.CurrentCulture.Name} system:");
var ticks = new long[dates.Length];
for (int ctr = 0; ctr < dates.Length; ctr++)
{
Console.WriteLine(dates[ctr].ToString("f"));
ticks[ctr] = dates[ctr].ToUniversalTime().Ticks;
}
var fs = new FileStream(filenameInts, FileMode.Create);
var bw = new BinaryWriter(fs);
bw.Write(ticks.Length);
foreach (var tick in ticks)
bw.Write(tick);

bw.Close();
Console.WriteLine("Saved dates...");
}

private static void RestoreDatesAsInts()


{
TimeZoneInfo.ClearCachedData();
Console.WriteLine($"Current Time Zone:
{TimeZoneInfo.Local.DisplayName}");
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-GB");
FileStream fs = new FileStream(filenameInts, FileMode.Open);
BinaryReader br = new BinaryReader(fs);
int items;
DateTime[] dates;

try
{
items = br.ReadInt32();
dates = new DateTime[items];

for (int ctr = 0; ctr < items; ctr++)


{
long ticks = br.ReadInt64();
dates[ctr] = new DateTime(ticks).ToLocalTime();
}
}
catch (EndOfStreamException)
{
Console.WriteLine("File corruption detected. Unable to restore
data...");
return;
}
catch (IOException)
{
Console.WriteLine("Unspecified I/O error. Unable to restore
data...");
return;
}
// Thrown during array initialization.
catch (OutOfMemoryException)
{
Console.WriteLine("File corruption detected. Unable to restore
data...");
return;
}
finally
{
br.Close();
}

Console.WriteLine($"The dates on an
{Thread.CurrentThread.CurrentCulture.Name} system:");
foreach (var value in dates)
Console.WriteLine(value.ToString("f"));

Console.WriteLine("Restored dates...");
}
// When saved on an en-US system, the example displays the following output:
// Current Time Zone: (UTC-08:00) Pacific Time (US & Canada)
// The dates on an en-US system:
// Saturday, June 14, 2014 6:32 AM
// Thursday, July 10, 2014 11:49 PM
// Saturday, January 10, 2015 1:16 AM
// Saturday, December 20, 2014 9:45 PM
// Monday, June 02, 2014 3:14 PM
// Saved dates...
//
// When restored on an en-GB system, the example displays the following
output:
// Current Time Zone: (UTC) Dublin, Edinburgh, Lisbon, London
// The dates on an en-GB system:
// 14 June 2014 14:32
// 11 July 2014 07:49
// 10 January 2015 09:16
// 21 December 2014 05:45
// 02 June 2014 23:14
// Restored dates...

Serializar valores DateTime


Você pode persistir DateTime valores por meio de serialização em um fluxo ou arquivo
e, em seguida, restaurá-los por meio de desserialização. DateTime Os dados são
serializados em algum formato de objeto especificado. Os objetos são restaurados
quando são desserializados. Um formatador ou serializador, como JsonSerializer ou
XmlSerializer, lida com o processo de serialização e desserialização. Para obter mais
informações sobre serialização e os tipos de serialização suportados pelo .NET, consulte
Serialização.

O exemplo a seguir usa a XmlSerializer classe para serializar e desserializar DateTime


valores. Os valores representam todos os dias bissextos do século XXI. A saída
representa o resultado se o exemplo for executado em um sistema cuja cultura atual
seja o inglês (Reino Unido). Como você desserializou o objeto em si, o DateTime código
não precisa lidar com diferenças culturais nos formatos de data e hora.

C#

public static void PersistAsXML()


{
// Serialize the data.
var leapYears = new List<DateTime>();
for (int year = 2000; year <= 2100; year += 4)
{
if (DateTime.IsLeapYear(year))
leapYears.Add(new DateTime(year, 2, 29));
}
DateTime[] dateArray = leapYears.ToArray();

var serializer = new XmlSerializer(dateArray.GetType());


TextWriter sw = new StreamWriter(filenameXml);

try
{
serializer.Serialize(sw, dateArray);
}
catch (InvalidOperationException e)
{
Console.WriteLine(e.InnerException?.Message);
}
finally
{
if (sw != null) sw.Close();
}

// Deserialize the data.


DateTime[]? deserializedDates;
using (var fs = new FileStream(filenameXml, FileMode.Open))
{
deserializedDates = (DateTime[]?)serializer.Deserialize(fs);
}

// Display the dates.


Console.WriteLine($"Leap year days from 2000-2100 on an
{Thread.CurrentThread.CurrentCulture.Name} system:");
int nItems = 0;
if (deserializedDates is not null)
{
foreach (var dat in deserializedDates)
{
Console.Write($" {dat:d} ");
nItems++;
if (nItems % 5 == 0)
Console.WriteLine();
}
}
}
// The example displays the following output:
// Leap year days from 2000-2100 on an en-GB system:
// 29/02/2000 29/02/2004 29/02/2008 29/02/2012
29/02/2016
// 29/02/2020 29/02/2024 29/02/2028 29/02/2032
29/02/2036
// 29/02/2040 29/02/2044 29/02/2048 29/02/2052
29/02/2056
// 29/02/2060 29/02/2064 29/02/2068 29/02/2072
29/02/2076
// 29/02/2080 29/02/2084 29/02/2088 29/02/2092
29/02/2096

O exemplo anterior não inclui informações de tempo. Se um valor representa um


DateTime momento no tempo e é expresso como uma hora local, converta-o de hora
local para UTC antes de serializá-lo chamando o ToUniversalTime método. Depois de
desserializá-lo, converta-o de UTC para hora local chamando o ToLocalTime método.
DateTime x TimeSpan
Os DateTime tipos de valor e TimeSpan diferem em que a representa um instante no
tempo, enquanto a DateTimeTimeSpan representa um intervalo de tempo. Você pode
subtrair uma instância de outra para obter um TimeSpan objeto que representa o
intervalo de DateTime tempo entre elas. Ou você pode adicionar um positivo TimeSpan
ao atual DateTime para obter um DateTime valor que represente uma data futura.

Você pode adicionar ou subtrair um intervalo de tempo de um DateTime objeto. Os


intervalos de tempo podem ser negativos ou positivos, e podem ser expressos em
unidades como ticks, segundos ou como um TimeSpan objeto.

Compare para igualdade dentro da tolerância


As comparações de igualdade para DateTime valores são exatas. Para serem
considerados iguais, dois valores devem ser expressos como o mesmo número de
carrapatos. Essa precisão é muitas vezes desnecessária ou mesmo incorreta para muitas
aplicações. Muitas vezes, você deseja testar se DateTime os objetos são
aproximadamente iguais.

O exemplo a seguir demonstra como comparar valores aproximadamente equivalentes


DateTime . Aceita uma pequena margem de diferença ao declará-los iguais.

C#

public static bool RoughlyEquals(DateTime time, DateTime timeWithWindow, int


windowInSeconds, int frequencyInSeconds)
{
long delta = (long)((TimeSpan)(timeWithWindow - time)).TotalSeconds %
frequencyInSeconds;
delta = delta > windowInSeconds ? frequencyInSeconds - delta : delta;
return Math.Abs(delta) < windowInSeconds;
}

public static void TestRoughlyEquals()


{
int window = 10;
int freq = 60 * 60 * 2; // 2 hours;

DateTime d1 = DateTime.Now;

DateTime d2 = d1.AddSeconds(2 * window);


DateTime d3 = d1.AddSeconds(-2 * window);
DateTime d4 = d1.AddSeconds(window / 2);
DateTime d5 = d1.AddSeconds(-window / 2);

DateTime d6 = (d1.AddHours(2)).AddSeconds(2 * window);


DateTime d7 = (d1.AddHours(2)).AddSeconds(-2 * window);
DateTime d8 = (d1.AddHours(2)).AddSeconds(window / 2);
DateTime d9 = (d1.AddHours(2)).AddSeconds(-window / 2);

Console.WriteLine($"d1 ({d1}) ~= d1 ({d1}): {RoughlyEquals(d1, d1,


window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d2 ({d2}): {RoughlyEquals(d1, d2,
window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d3 ({d3}): {RoughlyEquals(d1, d3,
window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d4 ({d4}): {RoughlyEquals(d1, d4,
window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d5 ({d5}): {RoughlyEquals(d1, d5,
window, freq)}");

Console.WriteLine($"d1 ({d1}) ~= d6 ({d6}): {RoughlyEquals(d1, d6,


window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d7 ({d7}): {RoughlyEquals(d1, d7,
window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d8 ({d8}): {RoughlyEquals(d1, d8,
window, freq)}");
Console.WriteLine($"d1 ({d1}) ~= d9 ({d9}): {RoughlyEquals(d1, d9,
window, freq)}");
}
// The example displays output similar to the following:
// d1 (1/28/2010 9:01:26 PM) ~= d1 (1/28/2010 9:01:26 PM): True
// d1 (1/28/2010 9:01:26 PM) ~= d2 (1/28/2010 9:01:46 PM): False
// d1 (1/28/2010 9:01:26 PM) ~= d3 (1/28/2010 9:01:06 PM): False
// d1 (1/28/2010 9:01:26 PM) ~= d4 (1/28/2010 9:01:31 PM): True
// d1 (1/28/2010 9:01:26 PM) ~= d5 (1/28/2010 9:01:21 PM): True
// d1 (1/28/2010 9:01:26 PM) ~= d6 (1/28/2010 11:01:46 PM): False
// d1 (1/28/2010 9:01:26 PM) ~= d7 (1/28/2010 11:01:06 PM): False
// d1 (1/28/2010 9:01:26 PM) ~= d8 (1/28/2010 11:01:31 PM): True
// d1 (1/28/2010 9:01:26 PM) ~= d9 (1/28/2010 11:01:21 PM): True

Considerações de interoperabilidade COM


Um DateTime valor que é transferido para um aplicativo COM, em seguida, é transferido
de volta para um aplicativo gerenciado, é dito para ida e volta. No entanto, um valor
que especifica apenas um DateTime horário não faz ida e volta como você poderia
esperar.

Se você fizer uma viagem de ida e volta apenas um horário, como 15h, a data e hora
finais são 30 de dezembro de 1899 d.C. às 15:00, em vez de 1 de janeiro de 0001 d.C. às
15:00. NET e COM assumem uma data padrão quando apenas uma hora é especificada.
No entanto, o sistema COM assume uma data-base de 30 de dezembro de 1899 d.C.,
enquanto o .NET assume uma data-base de 1 de janeiro de 0001 d.C.
Quando apenas um tempo é passado do .NET para COM, um processamento especial é
executado que converte a hora para o formato usado por COM. Quando apenas um
tempo é passado de COM para .NET, nenhum processamento especial é executado
porque isso corromperia datas e horas legítimas em ou antes de 30 de dezembro de
1899. Se uma data começar sua viagem de ida e volta a partir de COM, .NET e COM
preservam a data.

O comportamento do .NET e COM significa que, se o aplicativo fizer viagens de ida e


volta a especificar apenas uma hora, o aplicativo deverá se lembrar de DateTime
modificar ou ignorar a data incorreta do objeto final DateTime .

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Métodos System.DateTime.ToBinary e
FromBinary
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Use o ToBinary método para converter o valor do objeto atual DateTime em um valor
binário. Posteriormente, use o valor binário e o método para recriar o FromBinary objeto
original DateTime .

) Importante

Em alguns casos, o DateTime valor retornado pelo FromBinary método não é


idêntico ao valor original DateTime fornecido ao ToBinary método. Para obter mais
informações, consulte a próxima seção, "Ajuste de hora local".

Uma DateTime estrutura consiste em um campo privado, que indica se o valor de hora
especificado é baseado na hora local, Tempo Universal Coordenado (UTC) ou nenhum
dos dois, concatenado a um campo privado KindTicks , que contém o número de ticks
de 100 nanossegundos que especificam uma data e hora.

Ajuste de hora local


Uma hora local, que é uma Hora Universal Coordenada ajustada ao fuso horário local, é
representada por uma DateTime estrutura cuja Kind propriedade tem o valor Local. Ao
restaurar um valor local DateTime da representação binária que é produzida pelo
ToBinary método, o método pode ajustar o FromBinary valor recriado para que ele não
seja igual ao valor original. Isso pode ocorrer nas seguintes condições:

Se um objeto local for serializado em um fuso horário pelo método e, em seguida,


desserializado FromBinary em um fuso horário diferente pelo método, a hora local
DateTime representada pelo objeto resultante DateTime será ajustada
automaticamente para o segundo fuso ToBinary horário.

Por exemplo, considere um DateTime objeto que representa uma hora local de
15h. Um aplicativo que está sendo executado no fuso horário do Pacífico dos EUA
usa o ToBinary método para converter esse DateTime objeto em um valor binário.
Outro aplicativo que está sendo executado no fuso horário leste dos EUA usa o
método para converter o FromBinary valor binário em um novo DateTime objeto.
O valor do novo DateTime objeto é 6 P.M., que representa o mesmo ponto no
tempo que o valor original 3 P.M., mas é ajustado para a hora local no fuso horário
leste.

Se a representação binária de um valor local DateTime representar uma hora


inválida no fuso horário local do sistema no qual FromBinary é chamado, a hora
será ajustada para que seja válida.

Por exemplo, a transição do horário padrão para o horário de verão ocorre no fuso
horário do Pacífico dos Estados Unidos em 14 de março de 2010, às 2:00 da
manhã, quando o tempo avança em uma hora, para 3:00 da manhã. Esse intervalo
de hora é um tempo inválido, ou seja, um intervalo de tempo que não existe nesse
fuso horário. O exemplo a seguir mostra que quando um tempo que se enquadra
nesse intervalo é convertido em um valor binário pelo método e, em seguida, é
restaurado pelo ToBinaryFromBinary método, o valor original é ajustado para se
tornar um tempo válido. É possível determinar se um valor de data e hora
específico podem estar sujeito à modificação passando-o para o método
TimeZoneInfo.IsInvalidTime, como o exemplo ilustra.

C#

using System;

public class Example


{
public static void Main()
{
DateTime localDate = new DateTime(2010, 3, 14, 2, 30, 0,
DateTimeKind.Local);
long binLocal = localDate.ToBinary();
if (TimeZoneInfo.Local.IsInvalidTime(localDate))
Console.WriteLine("{0} is an invalid time in the {1} zone.",
localDate,
TimeZoneInfo.Local.StandardName);

DateTime localDate2 = DateTime.FromBinary(binLocal);


Console.WriteLine("{0} = {1}: {2}",
localDate, localDate2,
localDate.Equals(localDate2));
}
}
// The example displays the following output:
// 3/14/2010 2:30:00 AM is an invalid time in the Pacific Standard
Time zone.
// 3/14/2010 2:30:00 AM = 3/14/2010 3:30:00 AM: False
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.DateTime.TryParse
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O DateTime.TryParse(String, IFormatProvider, DateTimeStyles, DateTime) método analisa


uma cadeia de caracteres que pode conter informações de data, hora e fuso horário. É
semelhante ao DateTime.Parse(String, IFormatProvider, DateTimeStyles) método, exceto
que o DateTime.TryParse(String, DateTime) método não lança uma exceção se a
conversão falhar.

Esse método tenta ignorar dados não reconhecidos e analisar a cadeia de caracteres de
entrada ( s ) completamente. Se s contiver uma hora, mas nenhuma data, o método por
padrão substituirá a data atual ou, se styles incluir o NoCurrentDateDefault sinalizador,
substituirá DateTime.Date.MinValue . Se s contiver uma data, mas nenhuma hora, 12:00
meia-noite é usado como a hora padrão. Se uma data estiver presente, mas seu
componente de ano consistir em apenas dois dígitos, ela será convertida em um ano no
calendário atual do parâmetro com base no provider valor da
Calendar.TwoDigitYearMax propriedade. Todos os caracteres s de espaço em branco à
esquerda, internos ou à direita são ignorados. A data e a hora podem ser colocadas
entre colchetes com um par de caracteres NUMBER SIGN à esquerda e à direita ('#',
U+0023), e podem ser rastreadas com um ou mais caracteres NULL (U+0000).

Formatos válidos específicos para elementos de data e hora, bem como os nomes e
símbolos usados em datas e horas, são definidos pelo provider parâmetro, que pode
ser qualquer um dos seguintes:

Um CultureInfo objeto que representa a cultura cuja formatação é usada no s


parâmetro. O DateTimeFormatInfo objeto retornado pela
CultureInfo.DateTimeFormat propriedade define a formatação usada no s .
Um DateTimeFormatInfo objeto que define a formatação usada no s .
Uma implementação personalizada IFormatProvider . Seu
IFormatProvider.GetFormat método retorna um DateTimeFormatInfo objeto que
define a formatação usada no s .

Se provider for null , a cultura atual será usada.

Se s for a representação de cadeia de caracteres de um dia bissexto em um ano


bissexto no calendário atual, o método analisará s com êxito. Se s for a representação
de cadeia de caracteres de um dia bissexto em um ano não bissexto no calendário atual
de , a operação de provider análise falhará e o método retornará false .

O styles parâmetro define a interpretação precisa da cadeia de caracteres analisada e


como a operação de análise deve manipulá-la. Ele pode ser um ou mais membros da
DateTimeStyles enumeração, conforme descrito na tabela a seguir.

ノ Expandir a tabela

Membro Descrição
DateTimeStyles

AdjustToUniversal Analisa s e, caso necessário, converte-o em UTC. Caso s inclua um


deslocamento de fuso horário ou caso s não contenha informações de
fuso horário, mas styles inclua o sinalizador DateTimeStyles.AssumeLocal,
o método analisa a cadeia de caracteres, chama ToUniversalTime para
converter o valor DateTime retornado em UTC e define a propriedade Kind
como DateTimeKind.Utc. Caso s indique que ele representa o UTC ou caso
s não contenha informações de fuso horário, mas styles inclua o
sinalizador DateTimeStyles.AssumeUniversal, o método analisa a cadeia de
caracteres, não executa nenhuma conversão de fuso horário no valor
DateTime retornado e define a propriedade Kind a DateTimeKind.Utc. Em
todos os outros casos, o sinalizador não entra em vigor.

AllowInnerWhite Embora válido, esse valor é ignorado. O espaço em branco interno é


permitido nos elementos de data e hora do s .

AllowLeadingWhite Embora válido, esse valor é ignorado. O espaço em branco à esquerda é


permitido nos elementos de data e hora do s .

AllowTrailingWhite Embora válido, esse valor é ignorado. O espaço em branco à direita é


permitido nos elementos de data e hora do s .

AllowWhiteSpaces Especifica que s pode conter espaços em branco à esquerda, internos e à


direita. Esse é o comportamento padrão. Ele não pode ser substituído
fornecendo um valor de enumeração mais restritivo DateTimeStyles , como
DateTimeStyles.None.

AssumeLocal Especifica que, caso s não tenha informações de fuso horário, pressupõe-
se que ele represente uma hora local. A menos que o sinalizador
DateTimeStyles.AdjustToUniversal esteja presente, a propriedade Kind do
valor DateTime retornado é definida como DateTimeKind.Local.

AssumeUniversal Especifica que, caso s não tenha informações de fuso horário, pressupõe-
se que ele represente o UTC. A menos que o sinalizador
DateTimeStyles.AdjustToUniversal esteja presente, o método converte o
valor DateTime retornado do UTC na hora local e define a propriedade Kind
como DateTimeKind.Local.
Membro Descrição
DateTimeStyles

None Embora válido, esse valor é ignorado.

RoundtripKind Para cadeias de caracteres que contêm informações de fuso horário, tenta
impedir a conversão de uma cadeia de caracteres de data e hora em um
DateTime valor com sua Kind propriedade definida como
DateTimeKind.Local. Normalmente, essa cadeia de caracteres é criada
chamando o método usando os especificadores de formato padrão "o
DateTime.ToString(String) ", "r" ou "u".

Se s não contiver informações de fuso horário, o DateTime.TryParse(String,


IFormatProvider, DateTimeStyles, DateTime) método retornará um valor cuja Kind
propriedade éDateTimeKind.Unspecified, a menos que um DateTime sinalizador indique
o styles contrário. Se s incluir informações de deslocamento de fuso horário ou fuso
horário, o método executará DateTime.TryParse(String, IFormatProvider, DateTimeStyles,
DateTime) qualquer conversão de tempo necessária e retornará um dos seguintes:

Um DateTime valor cuja data e hora refletem a hora local e cuja Kind propriedade
é DateTimeKind.Local.
Ou, se styles incluir o AdjustToUniversal sinalizador, um DateTime valor cuja data
e hora refletem UTC e cuja Kind propriedade é DateTimeKind.Utc.

Esse comportamento pode ser substituído usando o DateTimeStyles.RoundtripKind


sinalizador.

Analisar culturas personalizadas


Se você analisar uma cadeia de caracteres de data e hora gerada para uma cultura
personalizada, use o TryParseExactTryParse método em vez do método para melhorar a
probabilidade de que a operação de análise seja bem-sucedida. Uma cadeia de
caracteres de data e hora de cultura personalizada pode ser complicada e difícil de
analisar. O TryParse método tenta analisar uma cadeia de caracteres com vários padrões
de análise implícitos, todos os quais podem falhar. Por outro lado, o TryParseExact
método requer que você designe explicitamente um ou mais padrões de análise exatos
que provavelmente terão êxito.

Para obter mais informações sobre culturas personalizadas, consulte a


System.Globalization.CultureAndRegionInfoBuilder classe.
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.TimeSpan struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Um TimeSpan objeto representa um intervalo de tempo (duração do tempo ou tempo


decorrido) que é medido como um número positivo ou negativo de dias, horas,
minutos, segundos e frações de segundo. A TimeSpan estrutura também pode ser
usada para representar a hora do dia, mas apenas se a hora não estiver relacionada a
uma data específica. Caso contrário, a DateTime estrutura ou DateTimeOffset deve ser
usada em vez disso. (Para obter mais informações sobre como usar a estrutura para
refletir a TimeSpan hora do dia, consulte Escolhendo entre DateTime, DateTimeOffset,
TimeSpan e TimeZoneInfo.)

7 Observação

Um TimeSpan valor representa um intervalo de tempo e pode ser expresso como


um determinado número de dias, horas, minutos, segundos e milissegundos. Por
representar um intervalo geral sem referência a um ponto inicial ou final específico,
ele não pode ser expresso em termos de anos e meses, ambos com um número
variável de dias. Ele difere de um valor, que representa uma data e hora sem
referência a um fuso horário específico, ou um valor, que representa um
DateTimeDateTimeOffset momento específico do tempo.

A maior unidade de tempo que a estrutura usa para medir a TimeSpan duração é um
dia. Os intervalos de tempo são medidos em dias para consistência, porque o número
de dias em unidades maiores de tempo, como meses e anos, varia.

O valor de um TimeSpan objeto é o número de ticks que são iguais ao intervalo de


tempo representado. Um carrapato é igual a 100 nanossegundos, ou um décimo
milionésimo de segundo. O valor de um TimeSpan objeto pode variar de
TimeSpan.MinValue a TimeSpan.MaxValue.

Instanciar um valor TimeSpan


Você pode instanciar um TimeSpan valor de várias maneiras:

Chamando seu construtor implícito sem parâmetros. Isso cria um objeto cujo valor
é TimeSpan.Zero, como mostra o exemplo a seguir.
C#

TimeSpan interval = new TimeSpan();


Console.WriteLine(interval.Equals(TimeSpan.Zero)); // Displays
"True".

Chamando um de seus construtores explícitos. O exemplo a seguir inicializa um


valor para um TimeSpan número especificado de horas, minutos e segundos.

C#

TimeSpan interval = new TimeSpan(2, 14, 18);


Console.WriteLine(interval.ToString());

// Displays "02:14:18".

Chamando um método ou executando uma operação que retorna um TimeSpan


valor. Por exemplo, você pode instanciar um TimeSpan valor que representa o
intervalo entre dois valores de data e hora, como mostra o exemplo a seguir.

C#

DateTime departure = new DateTime(2010, 6, 12, 18, 32, 0);


DateTime arrival = new DateTime(2010, 6, 13, 22, 47, 0);
TimeSpan travelTime = arrival - departure;
Console.WriteLine("{0} - {1} = {2}", arrival, departure, travelTime);

// The example displays the following output:


// 6/13/2010 10:47:00 PM - 6/12/2010 6:32:00 PM = 1.04:15:00

Você também pode inicializar um objeto para um TimeSpan valor de tempo zero
dessa maneira, como mostra o exemplo a seguir.

C#

Random rnd = new Random();

TimeSpan timeSpent = TimeSpan.Zero;

timeSpent += GetTimeBeforeLunch();
timeSpent += GetTimeAfterLunch();

Console.WriteLine("Total time: {0}", timeSpent);

TimeSpan GetTimeBeforeLunch()
{
return new TimeSpan(rnd.Next(3, 6), 0, 0);
}
TimeSpan GetTimeAfterLunch()
{
return new TimeSpan(rnd.Next(3, 6), 0, 0);
}

// The example displays output like the following:


// Total time: 08:00:00

TimeSpan valores são retornados por operadores aritméticos e métodos do


DateTime, DateTimeOffsete TimeSpan estruturas.

Analisando a representação de cadeia de caracteres de um TimeSpan valor. Você


pode usar os Parse métodos e TryParse para converter cadeias de caracteres que
contêm intervalos de tempo em TimeSpan valores. O exemplo a seguir usa o Parse
método para converter uma matriz de cadeias de caracteres em TimeSpan valores.

C#

string[] values = { "12", "31.", "5.8:32:16", "12:12:15.95", ".12"};


foreach (string value in values)
{
try {
TimeSpan ts = TimeSpan.Parse(value);
Console.WriteLine("'{0}' --> {1}", value, ts);
}
catch (FormatException) {
Console.WriteLine("Unable to parse '{0}'", value);
}
catch (OverflowException) {
Console.WriteLine("'{0}' is outside the range of a TimeSpan.",
value);
}
}

// The example displays the following output:


// '12' --> 12.00:00:00
// Unable to parse '31.'
// '5.8:32:16' --> 5.08:32:16
// '12:12:15.95' --> 12:12:15.9500000
// Unable to parse '.12'

Além disso, você pode definir o formato preciso da cadeia de caracteres de


entrada a ser analisada e convertida em um TimeSpan valor chamando o
ParseExact método or TryParseExact .

Executar operações em valores de TimeSpan


Você pode adicionar e subtrair durações de tempo usando os operadores e ou
chamando os Addition métodos e SubtractionSubtract.Add Você também pode
comparar duas durações de tempo chamando os Comparemétodos , CompareToe
Equals . A TimeSpan estrutura também inclui os Duration e Negate métodos, que
convertem intervalos de tempo em valores positivos e negativos,

O intervalo de TimeSpan valores é MinValue para MaxValue.

Formatar um valor TimeSpan


Um TimeSpan valor pode ser representado como [-]d.hh: mm: ss.ff, onde o sinal de
menos opcional indica um intervalo de tempo negativo, o componente d é dias, hh é
horas medidas em um relógio de 24 horas, mm é minutos, ss é segundos e ff é frações de
segundo. Ou seja, um intervalo de tempo consiste em um número positivo ou negativo
de dias sem uma hora do dia, ou um número de dias com uma hora do dia, ou apenas
uma hora do dia.

A partir do .NET Framework 4, a estrutura oferece suporte à formatação sensível à


TimeSpan cultura por meio das sobrecargas de seu ToString método, que converte um
TimeSpan valor em sua representação de cadeia de caracteres. O método padrão
TimeSpan.ToString() retorna um intervalo de tempo usando um formato invariante que é
idêntico ao seu valor de retorno em versões anteriores do .NET Framework. A
TimeSpan.ToString(String) sobrecarga permite especificar uma cadeia de caracteres de
formato que define a representação de cadeia de caracteres do intervalo de tempo. A
TimeSpan.ToString(String, IFormatProvider) sobrecarga permite especificar uma cadeia
de caracteres de formato e a cultura cujas convenções de formatação são usadas para
criar a representação de cadeia de caracteres do intervalo de tempo. TimeSpan suporta
cadeias de caracteres de formato padrão e personalizado. (Para obter mais informações,
consulte Cadeias de caracteres de formato TimeSpan padrão e cadeias de caracteres de
formato TimeSpan personalizadas.) No entanto, apenas cadeias de caracteres de
formato padrão são sensíveis à cultura.

Restaurar formatação de TimeSpan herdada


Em alguns casos, o código que formata TimeSpan valores com êxito no .NET Framework
3.5 e versões anteriores falha no .NET Framework 4. Isso é mais comum no código que
chama um método de elemento TimeSpan_LegacyFormatMode para formatar
um<TimeSpan valor com uma cadeia de caracteres de> formato. O exemplo a seguir
formata com êxito um TimeSpan valor no .NET Framework 3.5 e versões anteriores, mas
lança uma exceção no .NET Framework 4 e versões posteriores. Observe que ele tenta
formatar um valor usando um TimeSpan especificador de formato sem suporte, que é
ignorado no .NET Framework 3.5 e versões anteriores.

C#

ShowFormattingCode();
// Output from .NET Framework 3.5 and earlier versions:
// 12:30:45
// Output from .NET Framework 4:
// Invalid Format

Console.WriteLine("---");

ShowParsingCode();
// Output:
// 000000006 --> 6.00:00:00

void ShowFormattingCode()
{
TimeSpan interval = new TimeSpan(12, 30, 45);
string output;
try
{
output = String.Format("{0:r}", interval);
}
catch (FormatException)
{
output = "Invalid Format";
}
Console.WriteLine(output);
}

void ShowParsingCode()
{
string value = "000000006";
try
{
TimeSpan interval = TimeSpan.Parse(value);
Console.WriteLine("{0} --> {1}", value, interval);
}
catch (FormatException)
{
Console.WriteLine("{0}: Bad Format", value);
}
catch (OverflowException)
{
Console.WriteLine("{0}: Overflow", value);
}
}

Se você não pode modificar o código, você pode restaurar a formatação herdada de
valores de TimeSpan uma das seguintes maneiras:
Criando um arquivo de configuração que contém o <elemento
TimeSpan_LegacyFormatMode>. A definição do enabled atributo desse elemento
restaura a true formatação herdada TimeSpan por aplicativo.

Definindo a opção de compatibilidade "NetFx40_TimeSpanLegacyFormatMode" ao


criar um domínio de aplicativo. Isso permite a formatação herdada TimeSpan por
domínio de aplicativo. O exemplo a seguir cria um domínio de aplicativo que usa
formatação herdada TimeSpan .

C#

using System;

public class Example2


{
public static void Main()
{
AppDomainSetup appSetup = new AppDomainSetup();
appSetup.SetCompatibilitySwitches(new string[] {
"NetFx40_TimeSpanLegacyFormatMode" });
AppDomain legacyDomain = AppDomain.CreateDomain("legacyDomain",
null,
appSetup);
legacyDomain.ExecuteAssembly("ShowTimeSpan.exe");
}
}

Quando o código a seguir é executado no novo domínio do aplicativo, ele reverte


para o comportamento de formatação herdada TimeSpan .

C#

using System;

public class Example3


{
public static void Main()
{
TimeSpan interval = DateTime.Now - DateTime.Now.Date;
string msg = String.Format("Elapsed Time Today: {0:d} hours.",
interval);
Console.WriteLine(msg);
}
}
// The example displays the following output:
// Elapsed Time Today: 01:40:52.2524662 hours.
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.TimeSpan.Parse
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A cadeia de caracteres de entrada para os Parse métodos contém uma especificação de


intervalo de tempo no formato:

[ws][-]{ d | [d.]hh:mm[:ss[.ff]] }[ws]

Os elementos entre colchetes ( [ e ] ) são opcionais. É necessária uma seleção da lista


de alternativas entre chaves ( e ) e } separadas por barras verticais ( { |). A tabela a seguir
descreve cada elemento.

ノ Expandir a tabela

Element Descrição

ws Espaço em branco opcional.

- Um sinal de subtração opcional, que indica um TimeSpan negativo.

d Dias, variando de 0 a 10675199.

. Um símbolo sensível à cultura que separa dias de horas. O formato invariável usa um
caractere de ponto final (".").

hh Horas, variando de 0 a 23.

: O símbolo separador de hora sensível à cultura. O formato invariante usa um caractere


de dois pontos (":").

mm Minutos, variando de 0 a 59.

ss Segundos opcionais, variando de 0 a 59.

. Um símbolo sensível à cultura que separa segundos de frações de um segundo. O


formato invariável usa um caractere de ponto final (".").

ff Segundos fracionários opcionais, consistindo em um a sete dígitos decimais.

Se a cadeia de caracteres de entrada não for apenas um valor de dia, ela deverá incluir
um componente de horas e minutos; outros componentes são opcionais. Se eles
estiverem presentes, os valores de cada componente de tempo devem estar dentro de
um intervalo especificado. Por exemplo, o valor de hh, o componente horas, deve estar
entre 0 e 23. Devido a isso, passar "23:00:00" para o Parse método retorna um intervalo
de tempo de 23 horas. Por outro lado, passar "24:00:00" retorna um intervalo de tempo
de 24 dias. Como "24" está fora do intervalo do componente horas, ele é interpretado
como o componente dias.

Os componentes da cadeia de caracteres de entrada devem especificar coletivamente


um intervalo de tempo maior ou igual a e menor ou igual a
TimeSpan.MinValueTimeSpan.MaxValue.

O Parse(String) método tenta analisar a cadeia de caracteres de entrada usando cada


um dos formatos específicos de cultura para a cultura atual.

Notas aos chamadores


Quando um componente de intervalo de tempo na cadeia de caracteres a ser analisada
contém mais de sete dígitos, as operações de análise no .NET Framework 3.5 e versões
anteriores podem se comportar de forma diferente das operações de análise no .NET
Framework 4 e versões posteriores. Em alguns casos, a análise de operações bem-
sucedidas no .NET Framework 3.5 e versões anteriores pode falhar e lançar um
OverflowException no .NET Framework 4 e posterior. Em outros casos, as operações de
análise que lançam um no .NET Framework 3.5 e versões anteriores podem falhar e
lançar um FormatExceptionOverflowException no .NET Framework 4 e posterior. O
exemplo a seguir ilustra ambos os cenários.

C#

string[] values = { "000000006", "12.12:12:12.12345678" };


foreach (string value in values)
{
try {
TimeSpan interval = TimeSpan.Parse(value);
Console.WriteLine("{0} --> {1}", value, interval);
}
catch (FormatException) {
Console.WriteLine("{0}: Bad Format", value);
}
catch (OverflowException) {
Console.WriteLine("{0}: Overflow", value);
}
}

// Output from .NET Framework 3.5 and earlier versions:


// 000000006 --> 6.00:00:00
// 12.12:12:12.12345678: Bad Format
// Output from .NET Framework 4 and later versions or .NET Core:
// 000000006: Overflow
// 12.12:12:12.12345678: Overflow

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Métodos System.TimeSpan.TryParse
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Método
TryParse(System.String,System.TimeSpan@)
O TimeSpan.TryParse(String, TimeSpan) método é como o TimeSpan.Parse(String)
método, exceto que ele não lança uma exceção se a conversão falhar.

O s parâmetro contém uma especificação de intervalo de tempo no formato:

[ws][-]{ d | d.hh:mm[:ss[.ff]] | hh:mm[:ss[.ff]] }[ws]

Os elementos entre colchetes ([ e ]) são opcionais. É necessária uma seleção da lista de


alternativas entre chaves ({ e }) e separadas por barras verticais (|). A tabela a seguir
descreve cada elemento.

ノ Expandir a tabela

Element Descrição

ws Espaço em branco opcional.

- Um sinal de subtração opcional, que indica um TimeSpan negativo.

d Dias, variando de 0 a 10675199.

. Um símbolo sensível à cultura que separa dias de horas. O formato invariável usa um
caractere de ponto final (".").

hh Horas, variando de 0 a 23.

: O símbolo separador de hora sensível à cultura. O formato invariante usa um caractere


de dois pontos (":").

mm Minutos, variando de 0 a 59.

ss Segundos opcionais, variando de 0 a 59.

. Um símbolo sensível à cultura que separa segundos de frações de um segundo. O


formato invariável usa um caractere de ponto final (".").
Element Descrição

ff Segundos fracionários opcionais, consistindo em um a sete dígitos decimais.

Os componentes do devem especificar coletivamente um intervalo de s tempo maior


ou igual a e menor ou igual a TimeSpan.MinValueTimeSpan.MaxValue.

O Parse(String) método tenta analisar s usando cada um dos formatos específicos de


cultura para a cultura atual.

Método TryParse(String, IFormatProvider,


TimeSpan)
O TryParse(String, IFormatProvider, TimeSpan) método é como o Parse(String,
IFormatProvider) método, exceto que ele não lança uma exceção se a conversão falhar.

O input parâmetro contém uma especificação de intervalo de tempo no formato:

[ws][-]{ d | d.hh:mm[:ss[.ff]] | hh:mm[:ss[.ff]] }[ws]

Os elementos entre colchetes ([ e ]) são opcionais. É necessária uma seleção da lista de


alternativas entre chaves ({ e }) e separadas por barras verticais (|). A tabela a seguir
descreve cada elemento.

ノ Expandir a tabela

Element Descrição

ws Espaço em branco opcional.

- Um sinal de subtração opcional, que indica um TimeSpan negativo.

d Dias, variando de 0 a 10675199.

. Um símbolo sensível à cultura que separa dias de horas. O formato invariável usa um
caractere de ponto final (".").

hh Horas, variando de 0 a 23.

: O símbolo separador de hora sensível à cultura. O formato invariante usa um caractere


de dois pontos (":").

mm Minutos, variando de 0 a 59.

ss Segundos opcionais, variando de 0 a 59.


Element Descrição

. Um símbolo sensível à cultura que separa segundos de frações de um segundo. O


formato invariável usa um caractere de ponto final (".").

ff Segundos fracionários opcionais, consistindo em um a sete dígitos decimais.

Os componentes do devem especificar coletivamente um intervalo de input tempo


maior ou igual a e menor ou igual a TimeSpan.MinValueTimeSpan.MaxValue.

O TryParse(String, IFormatProvider, TimeSpan) método tenta analisar input usando cada


um dos formatos específicos de cultura para a cultura especificada por formatProvider .

O formatProvider parâmetro é uma IFormatProvider implementação que fornece


informações específicas da cultura sobre o formato da cadeia de caracteres retornada. O
parâmetro formatProvider pode ser um dos seguintes:

Um objeto CultureInfo que representa a cultura cujas convenções de formatação


devem ser refletidas na cadeia de caracteres retornada. O objeto
DateTimeFormatInfo retornado pela propriedade CultureInfo.DateTimeFormat
define a formatação da cadeia de caracteres retornada.
Um objeto DateTimeFormatInfo que define a formatação da cadeia de caracteres
retornada.
Um objeto personalizado que implementa a interface IFormatProvider. O método
IFormatProvider.GetFormat retorna um objeto DateTimeFormatInfo que fornece
informações de formatação.

Caso formatProvider seja null , o objeto DateTimeFormatInfo que está associado à


cultura atual é usado.

Notas aos chamadores


Em alguns casos, quando um componente de intervalo de tempo na cadeia de
caracteres a ser analisada contém mais de sete dígitos, as operações de análise bem-
sucedidas e retornadas true no .NET Framework 3.5 e versões anteriores podem falhar
e retornar false no .NET Framework 4 e versões posteriores. O exemplo a seguir ilustra
esse cenário:

C#

string value = "000000006";


TimeSpan interval;
if (TimeSpan.TryParse(value, out interval))
Console.WriteLine("{0} --> {1}", value, interval);
else
Console.WriteLine("Unable to parse '{0}'", value);

// Output from .NET Framework 3.5 and earlier versions:


// 000000006 --> 6.00:00:00
// Output from .NET Framework 4:
// Unable to parse //000000006//

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Estender metadados usando atributos
Artigo • 27/01/2024

O Common Language Runtime permite adicionar declarações descritivas parecidas com


palavras, chamadas atributos, para anotar elementos de programação como tipos,
campos, métodos e propriedades. Quando você compila seu código para o runtime, ele
é convertido em MSIL (Microsoft Intermediate Language) e colocado dentro de um
arquivo PE (executável portátil) com metadados gerados pelo compilador. Os atributos
permitem colocar informações descritivas extras em metadados que podem ser
extraídos usando serviços de reflexão de runtime. O compilador cria atributos quando
você declara instâncias de classes especiais que derivam de System.Attribute.

O .NET usa atributos por vários motivos e para solucionar vários problemas. Os atributos
descrevem como serializar dados, especificar características que são usadas para impor
segurança e limitar otimizações pelo compilador JIT (Just-In-Time) para que o código
permaneça fácil de depurar. Os atributos também podem registrar o nome de um
arquivo ou o autor do código, ou controlar a visibilidade de controles e membros
durante o desenvolvimento de formulários.

Artigos relacionados
ノ Expandir a tabela

Título Descrição

Aplicando atributos Descreve como aplicar um atributo a um elemento do código.

Escrevendo atributos Descreve como criar classes de atributos personalizados.


personalizados

Recuperando informações Descreve como recuperar atributos personalizados para o


armazenadas em atributos código que é carregado no contexto de execução.

Metadados e componentes Fornece uma visão geral dos metadados e descreve como
autodescritivos eles são implementados em um arquivo PE (executável
portátil) do .NET.

Como carregar assemblies no Explica como recuperar informações de atributos


contexto de somente reflexão personalizados no contexto somente reflexão.

Referência
System.Attribute

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Aplicar atributos
Artigo • 12/03/2024

Use o processo a seguir para aplicar um atributo a um elemento do código.

1. Defina um novo atributo ou use um atributo do .NET existente.

2. Aplique o atributo ao elemento de código, colocando-o imediatamente antes do


elemento.

Cada linguagem tem sua própria sintaxe de atributo. Em C++ e C#, o atributo é
delimitado por colchetes e separado do elemento por um espaço em branco, que
pode incluir uma quebra de linha. No Visual Basic, o atributo é delimitado por
colchetes angulares e deve estar na mesma linha lógica. O caractere de
continuação de linha pode ser usado se você quiser uma quebra de linha.

3. Especifique parâmetros posicionais e parâmetros nomeados para o atributo.

Parâmetros posicionais são necessários e devem vir antes de quaisquer parâmetros


nomeados. Eles correspondem aos parâmetros de um dos constructos do atributo.
Parâmetros nomeados são opcionais e correspondem às propriedades do atributo
de leitura/gravação. Em C++ e C#, especifique name=value para cada parâmetro
opcional, em que name é o nome da propriedade. No Visual Basic, especifique
name:=value .

O atributo é emitido em metadados quando você compila o código e está disponível


para o Common Language Runtime e todas as ferramentas ou aplicativos
personalizados por meio dos serviços de reflexão do tempo de execução.

Por convenção, todos os nomes de atributos terminam com o sufixo "Attribute". No


entanto, várias linguagens que têm como destino o runtime, como Visual Basic e C#,
não exigem que você especifique o nome completo de um atributo. Por exemplo, se
quiser inicializar System.ObsoleteAttribute, você só precisa fazer referência a ele como
Obsoleto.

Aplicar um atributo a um método


O exemplo de código a seguir mostra como usar System.ObsoleteAttribute, que marca
o código como obsoleto. A cadeia de caracteres "Will be removed in next version" é
passada para o atributo. Esse atributo faz com que o compilador que exibe a cadeia de
caracteres transmitida emita um aviso quando o código que descreve o atributo for
chamado.

C#

public class Example


{
// Specify attributes between square brackets in C#.
// This attribute is applied only to the Add method.
[Obsolete("Will be removed in next version.")]
public static int Add(int a, int b)
{
return (a + b);
}
}

class Test
{
public static void Main()
{
// This generates a compile-time warning.
int i = Example.Add(2, 2);
}
}

Aplicar atributos no nível de assembly


Se você quiser aplicar um atributo no nível de assembly, use a palavra-chave assembly
( Assembly no Visual Basic). O código a seguir mostra o AssemblyTitleAttribute aplicado
no nível de assembly.

C#

using System.Reflection;
[assembly:AssemblyTitle("My Assembly")]

Quando esse atributo é aplicado, a cadeia de caracteres "My Assembly" é colocada no


manifesto do assembly na parte de metadados do arquivo. Você pode exibir o atributo
usando o Desmontador de MSIL (Ildasm.exe) ou criando um programa personalizado
para recuperar o atributo.

Confira também
Atributos
Recuperando informações armazenadas em atributos
Conceitos
Atributos (C#)
Visão geral de atributos (Visual Basic)

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Escrever atributos personalizados
Artigo • 10/05/2023

Para criar atributos personalizados, não é preciso aprender muitos conceitos novos. Se
estiver familiarizado com a programação orientada a objeto e souber como criar classes,
você já domina a maior parte do conhecimento necessário. Os atributos personalizados
são classes tradicionais que derivam direta ou indiretamente da classe System.Attribute.
Como acontece nas classes tradicionais, os atributos personalizados contêm métodos
que armazenam e recuperam dados.

As principais etapas para criar corretamente classes de atributos personalizados são as


seguintes:

Aplicar o AttributeUsageAttribute

Declarar a classe de atributos

Declarar constructos

Declarar propriedades

Esta seção descreve cada uma dessas etapas e oferece um exemplo de atributo
personalizado no fim.

Aplicar o AttributeUsageAttribute
A declaração de um atributo personalizado começa com o atributo
System.AttributeUsageAttribute, que define algumas das principais características da
classe de atributos. Por exemplo, é possível especificar se o atributo pode ser herdado
por outras classes ou a quais elementos o atributo pode ser aplicado. O fragmento de
código a seguir demonstra como usar o AttributeUsageAttribute:

C#

[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple =


true)]

O AttributeUsageAttribute possui três membros que são importantes para a criação de


atributos personalizados: AttributeTargets, Inherited e AllowMultiple.

Membro de AttributeTargets
No exemplo anterior, AttributeTargets.All é especificado, indicando que esse atributo
pode ser aplicado a todos os elementos do programa. Como alternativa, você pode
especificar AttributeTargets.Class, que indica que o atributo pode ser aplicado somente
a uma classe, ou AttributeTargets.Method, que indica que o atributo pode ser aplicado a
um único método. Todos os elementos do programa podem ser marcados para serem
descritos dessa maneira por um atributo personalizado.

Você também pode passar vários valores de AttributeTargets. O fragmento de código a


seguir especifica que um atributo personalizado pode ser aplicado a qualquer classe ou
método:

C#

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]

Propriedade Inherited
A propriedade AttributeUsageAttribute.Inherited indica se o atributo pode ser herdado
pelas classes derivadas das classes às quais o atributo é aplicado. Essa propriedade
aceita um sinalizador true (o padrão) ou false . No exemplo a seguir, MyAttribute tem
um valor padrão Inherited de true , enquanto YourAttribute tem um valor Inherited de
false :

C#

// This defaults to Inherited = true.


public class MyAttribute : Attribute
{
//...
}

[AttributeUsage(AttributeTargets.Method, Inherited = false)]


public class YourAttribute : Attribute
{
//...
}

Em seguida, os dois atributos são aplicados a um método na classe base MyClass :

C#

public class MyClass


{
[MyAttribute]
[YourAttribute]
public virtual void MyMethod()
{
//...
}
}

Por fim, a classe YourClass é herdada da classe base MyClass . O método MyMethod
mostra MyAttribute , mas não mostra YourAttribute :

C#

public class YourClass : MyClass


{
// MyMethod will have MyAttribute but not YourAttribute.
public override void MyMethod()
{
//...
}
}

Propriedade AllowMultiple
A propriedade AttributeUsageAttribute.AllowMultiple indica se várias instâncias do
atributo podem existir em um elemento. Se definido como true , várias instâncias serão
permitidas. Se definido como false (o padrão), apenas uma instância será permitida.

No exemplo a seguir, MyAttribute tem um valor padrão AllowMultiple de false ,


enquanto YourAttribute tem um valor de true :

C#

//This defaults to AllowMultiple = false.


public class MyAttribute : Attribute
{
}

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]


public class YourAttribute : Attribute
{
}

Quando várias instâncias desses atributos são aplicadas, MyAttribute produz um erro
do compilador. O exemplo de código a seguir mostra o uso válido de YourAttribute e o
uso inválido de MyAttribute :

C#
public class MyClass
{
// This produces an error.
// Duplicates are not allowed.
[MyAttribute]
[MyAttribute]
public void MyMethod()
{
//...
}

// This is valid.
[YourAttribute]
[YourAttribute]
public void YourMethod()
{
//...
}
}

Se as propriedades AllowMultiple e Inherited estiverem definidas como true , uma


classe herdada de outra classe poderá herdar um atributo e ter outra instância do
mesmo atributo aplicada à mesma classe filho. Se AllowMultiple estiver definida como
false , os valores de todos os atributos na classe pai serão substituídos pelas novas

instâncias do mesmo atributo na classe filho.

Declarar a classe de atributos


Depois de aplicar o AttributeUsageAttribute, comece a definir as especificações do seu
atributo. A declaração de uma classe de atributos é semelhante à declaração de uma
classe tradicional, como demonstrado pelo código a seguir:

C#

[AttributeUsage(AttributeTargets.Method)]
public class MyAttribute : Attribute
{
// . . .
}

Esta definição de atributo demonstra os seguintes pontos:

As classes de atributos devem ser declaradas como classes públicas.

Por convenção, o nome da classe de atributos termina com a palavra Attribute.


Embora não seja necessária, essa convenção é recomendada para facilitar a leitura.
Quando o atributo é aplicado, a inclusão da palavra Attribute torna-se opcional.

Todas as classes de atributos devem ser herdadas diretamente ou indiretamente


da classe System.Attribute.

No Microsoft Visual Basic, todas as classes de atributos personalizados devem ter


o atributo System.AttributeUsageAttribute.

Declarar constructos
Assim como as classes tradicionais, os atributos são inicializados com construtores. O
fragmento de código a seguir ilustra um constructo de atributo típico. Esse construtor
público aceita um parâmetro e define uma variável de membro igual ao seu valor.

C#

public MyAttribute(bool myvalue)


{
this.myvalue = myvalue;
}

Você pode sobrecarregar o constructo para acomodar diferentes combinações de


valores. Se também definir uma propriedade para sua classe de atributos
personalizados, você poderá usar uma combinação de parâmetros nomeados e
posicionais ao inicializar o atributo. Normalmente, você define todos os parâmetros
necessários como posicionais e todos os parâmetros opcionais como nomeados. Nesse
caso, o atributo não pode ser inicializado sem o parâmetro necessário. Todos os outros
parâmetros são opcionais.

7 Observação

No Visual Basic, os construtores de uma classe de atributos não devem usar um


argumento ParamArray .

O exemplo de código a seguir mostra como um atributo que usa o constructo anterior
pode ser aplicado usando parâmetros necessários e opcionais. Ele pressupõe que o
atributo tem um valor booliano necessário e uma cadeia de caracteres opcional.

C#

// One required (positional) and one optional (named) parameter are applied.
[MyAttribute(false, OptionalParameter = "optional data")]
public class SomeClass
{
//...
}
// One required (positional) parameter is applied.
[MyAttribute(false)]
public class SomeOtherClass
{
//...
}

Declarar propriedades
Se quiser definir um parâmetro nomeado ou disponibilizar uma maneira fácil de
retornar os valores armazenados pelo seu atributo, declare uma propriedade. As
propriedades de atributos devem ser declaradas como entidades públicas com uma
descrição do tipo de dados que será retornado. Defina a variável que conterá o valor de
sua propriedade e irá associá-la aos métodos get e set . O exemplo de código a seguir
demonstra como implementar uma propriedade no seu atributo:

C#

public bool MyProperty


{
get {return this.myvalue;}
set {this.myvalue = value;}
}

Exemplo de Atributo personalizado


Esta seção incorpora as informações anteriores e mostra como criar um atributo que
documenta informações sobre o autor de uma seção de código. O atributo neste
exemplo armazena o nome e o nível do programador, e se o código foi revisado. Ele usa
três variáveis privadas para armazenar os valores reais que serão salvos. Cada variável é
representada por uma propriedade pública que obtém e define os valores. Por fim, o
constructo é definido com dois parâmetros necessários:

C#

[AttributeUsage(AttributeTargets.All)]
public class DeveloperAttribute : Attribute
{
// Private fields.
private string name;
private string level;
private bool reviewed;
// This constructor defines two required parameters: name and level.

public DeveloperAttribute(string name, string level)


{
this.name = name;
this.level = level;
this.reviewed = false;
}

// Define Name property.


// This is a read-only attribute.

public virtual string Name


{
get {return name;}
}

// Define Level property.


// This is a read-only attribute.

public virtual string Level


{
get {return level;}
}

// Define Reviewed property.


// This is a read/write attribute.

public virtual bool Reviewed


{
get {return reviewed;}
set {reviewed = value;}
}
}

Você pode aplicar esse atributo usando o nome completo, DeveloperAttribute , ou


usando o nome abreviado, Developer , em uma das seguintes maneiras:

C#

[Developer("Joan Smith", "1")]

-or-

[Developer("Joan Smith", "1", Reviewed = true)]

O primeiro exemplo mostra o atributo aplicado apenas com os parâmetros nomeados


necessários. O segundo exemplo mostra o atributo aplicado com os parâmetros
obrigatórios e opcionais.
Confira também
System.Attribute
System.AttributeUsageAttribute
Atributos
Tipos de parâmetro de atributo
Recuperando informações armazenadas
em atributos
Artigo • 08/06/2023

Recuperar um atributo personalizado é um processo simples. Primeiro, declare uma


instância do atributo que você deseja recuperar. Em seguida, use o método
Attribute.GetCustomAttribute para inicializar o novo atributo para o valor do atributo
que você deseja recuperar. Após a inicialização do novo atributo, você pode usar suas
propriedades para obter os valores.

) Importante

Este artigo descreve como recuperar atributos para o código carregado no


contexto da execução. Para recuperar atributos do código carregado no contexto
somente reflexão, use a classe CustomAttributeData, conforme mostrado em
Como carregar assemblies no contexto somente reflexão.

Esta seção descreve as seguintes maneiras de recuperar os atributos:

Recuperar uma única instância de um atributo

Recuperar várias instâncias de um atributo aplicado ao mesmo escopo

Recuperar várias instâncias de um atributo aplicado a escopos diferentes

Recuperar uma única instância de um atributo


No exemplo a seguir, o DeveloperAttribute (descrito na seção anterior) é aplicado à
classe MainApp no nível da classe. O método GetAttribute usa GetCustomAttribute para
recuperar os valores armazenados em DeveloperAttribute no nível da classe antes de
exibi-los no console.

C#

using System;
using System.Reflection;
using CustomCodeAttributes;

[Developer("Joan Smith", "42", Reviewed = true)]


class MainApp
{
public static void Main()
{
// Call function to get and display the attribute.
GetAttribute(typeof(MainApp));
}

public static void GetAttribute(Type t)


{
// Get instance of the attribute.
DeveloperAttribute MyAttribute =
(DeveloperAttribute) Attribute.GetCustomAttribute(t, typeof
(DeveloperAttribute));

if (MyAttribute == null)
{
Console.WriteLine("The attribute was not found.");
}
else
{
// Get the Name value.
Console.WriteLine("The Name Attribute is: {0}." ,
MyAttribute.Name);
// Get the Level value.
Console.WriteLine("The Level Attribute is: {0}." ,
MyAttribute.Level);
// Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}." ,
MyAttribute.Reviewed);
}
}
}

A execução do programa anterior exibe o seguinte texto:

Console

The Name Attribute is: Joan Smith.


The Level Attribute is: 42.
The Reviewed Attribute is: True.

Se o atributo não for encontrado, o método GetCustomAttribute inicializará MyAttribute


com um valor nulo. Este exemplo verifica em MyAttribute a existência dessa instância e
notifica o usuário caso o atributo não seja encontrado. Se DeveloperAttribute não for
encontrado no escopo da classe, o console exibirá a seguinte mensagem:

Console

The attribute was not found.


O exemplo anterior supõe que a definição de atributo está no namespace atual.
Lembre-se de importar o namespace no qual a definição do atributo reside, se não
estiver no namespace atual.

Recuperar várias instâncias de um atributo


aplicado ao mesmo escopo
No exemplo anterior, a classe a ser inspecionada e o atributo específico a ser localizado
são passados para o método GetCustomAttribute. Esse código funcionará bem se
apenas uma instância de um atributo for aplicada no nível de classe. No entanto, se
várias instâncias de um atributo forem aplicadas no mesmo nível de classe, o método
GetCustomAttribute não recuperará todas as informações. Em casos em que várias

instâncias do mesmo atributo são aplicadas ao mesmo escopo, você pode usar o
método Attribute.GetCustomAttributes para colocar todas as instâncias de um atributo
em uma matriz. Por exemplo, se duas instâncias de DeveloperAttribute forem aplicadas
no nível de classe da mesma classe, o método GetAttribute poderá ser modificado para
exibir as informações encontradas nos dois atributos. Lembre-se de aplicar vários
atributos no mesmo nível. O atributo deve ser definido com a propriedade
AllowMultiple definida como true na classe AttributeUsageAttribute.

O exemplo de código a seguir mostra como usar o método GetCustomAttributes para


criar uma matriz que faz referência a todas as instâncias de DeveloperAttribute em
qualquer classe específica. Em seguida, o código gera os valores de todos os atributos
para o console.

C#

public static void GetAttribute(Type t)


{
DeveloperAttribute[] MyAttributes =
(DeveloperAttribute[]) Attribute.GetCustomAttributes(t, typeof
(DeveloperAttribute));

if (MyAttributes.Length == 0)
{
Console.WriteLine("The attribute was not found.");
}
else
{
for (int i = 0 ; i < MyAttributes.Length ; i++)
{
// Get the Name value.
Console.WriteLine("The Name Attribute is: {0}." ,
MyAttributes[i].Name);
// Get the Level value.
Console.WriteLine("The Level Attribute is: {0}." ,
MyAttributes[i].Level);
// Get the Reviewed value.
Console.WriteLine("The Reviewed Attribute is: {0}.",
MyAttributes[i].Reviewed);
}
}
}

Se nenhum atributo for encontrado, esse código alertará o usuário. Caso contrário, as
informações contidas nas duas instâncias de DeveloperAttribute serão exibidas.

Recuperar várias instâncias de um atributo


aplicado a escopos diferentes
Os métodos GetCustomAttributes e GetCustomAttribute não pesquisam uma classe
inteira e retornam todas as instâncias de um atributo nessa classe. Em vez disso, eles
pesquisam somente um método ou membro especificado por vez. Se você tiver uma
classe com o mesmo atributo aplicado a cada membro e quiser recuperar os valores em
todos os atributos aplicados a esses membros, forneça cada método ou membro
individualmente para GetCustomAttributes e GetCustomAttribute .

O seguinte exemplo de código usa uma classe como um parâmetro e procura pelo
DeveloperAttribute (definido anteriormente) no nível de classe e em cada método

individual dessa classe:

C#

public static void GetAttribute(Type t)


{
DeveloperAttribute att;

// Get the class-level attributes.

// Put the instance of the attribute on the class level in the att
object.
att = (DeveloperAttribute) Attribute.GetCustomAttribute (t, typeof
(DeveloperAttribute));

if (att == null)
{
Console.WriteLine("No attribute in class {0}.\n", t.ToString());
}
else
{
Console.WriteLine("The Name Attribute on the class level is: {0}.",
att.Name);
Console.WriteLine("The Level Attribute on the class level is: {0}.",
att.Level);
Console.WriteLine("The Reviewed Attribute on the class level is:
{0}.\n", att.Reviewed);
}

// Get the method-level attributes.

// Get all methods in this class, and put them


// in an array of System.Reflection.MemberInfo objects.
MemberInfo[] MyMemberInfo = t.GetMethods();

// Loop through all methods in this class that are in the


// MyMemberInfo array.
for (int i = 0; i < MyMemberInfo.Length; i++)
{
att = (DeveloperAttribute)
Attribute.GetCustomAttribute(MyMemberInfo[i], typeof (DeveloperAttribute));
if (att == null)
{
Console.WriteLine("No attribute in member function {0}.\n" ,
MyMemberInfo[i].ToString());
}
else
{
Console.WriteLine("The Name Attribute for the {0} member is:
{1}.",
MyMemberInfo[i].ToString(), att.Name);
Console.WriteLine("The Level Attribute for the {0} member is:
{1}.",
MyMemberInfo[i].ToString(), att.Level);
Console.WriteLine("The Reviewed Attribute for the {0} member is:
{1}.\n",
MyMemberInfo[i].ToString(), att.Reviewed);
}
}
}

Se nenhuma instância do DeveloperAttribute for encontrada no nível de método ou


nível de classe, o método GetAttribute notificará o usuário de que nenhum atributo foi
encontrado e exibirá o nome do método ou classe que contém o atributo. Se um
atributo for encontrado, o console exibirá os campos Name , Level e Reviewed .

Você pode usar os membros da classe Type para obter os métodos e membros
individuais na classe passada. Primeiro, esse exemplo consulta o objeto Type para obter
informações de atributo para o nível de classe. Em seguida, ele usa Type.GetMethods
para colocar instâncias de todos os métodos em uma matriz de objetos
System.Reflection.MemberInfo para recuperar informações de atributo para o nível de
método. Você também pode usar o método Type.GetProperties para verificar os
atributos no nível de propriedade, ou Type.GetConstructors para verificar os atributos
no nível do construtor.

Confira também
System.Type
Attribute.GetCustomAttribute
Attribute.GetCustomAttributes
Atributos
Tipos relacionados a memória e
extensão
Artigo • 27/01/2024

Do .NET Core 2.1 em diante, o .NET inclui uma variedade de tipos interrelacionados que
representam uma região contígua, fortemente tipada de memória arbitrária. Estão
incluídos:

System.Span<T>, um tipo usado para acessar uma região contígua da memória.


Uma instância Span<T> pode ser sustentada por uma matriz do tipo T , uma
String, um buffer alocado com stackalloc ou um ponteiro para memória não
gerenciada. Como ela deve ser alocada na pilha, tem várias restrições. Por
exemplo, um campo em uma classe não pode ser do tipo Span<T>, nem a
extensão pode ser usada em operações assíncronas.

System.ReadOnlySpan<T>, uma versão imutável da estrutura Span<T>.

System.Memory<T>, um wrapper sobre uma região contígua de memória. Uma


instância Memory<T> pode ser apoiada por uma matriz do tipo T ou um String,
ou um gerenciador de memória. Como ela pode ser armazenada no heap
gerenciado, Memory<T> não tem nenhuma das limitações de Span<T>.

System.ReadOnlyMemory<T>, uma versão imutável da estrutura Memory<T>.

System.Buffers.MemoryPool<T>, que aloca blocos de memória fortemente tipados


de um pool de memória para um proprietário. Instâncias IMemoryOwner<T>
podem ser alocadas do pool chamando MemoryPool<T>.Rent e lançadas de volta
ao pool chamando MemoryPool<T>.Dispose().

System.Buffers.IMemoryOwner<T>, que representa o proprietário de um bloco de


memória e controla o gerenciamento do tempo de vida.

MemoryManager<T>, uma classe base abstrata que pode ser usada para substituir
a implementação de Memory<T>, de modo que Memory<T> possa ser sustentada
por tipos adicionais, como identificadores seguros. MemoryManager<T> destina-
se a cenários avançados.

ArraySegment<T>, um wrapper para determinado número de elementos de


matriz, começando em um índice específico.

System.MemoryExtensions, uma coleção de métodos de extensão para converter


cadeias de caracteres, matrizes e segmentos de matriz para blocos Memory<T>.
System.Span<T>, System.Memory<T> e seus equivalentes somente leitura são
projetados para permitir a criação de algoritmos que evitam copiar memória ou alocar
no heap gerenciado mais do que o necessário. Criá-los (por meio de Slice ou seus
construtores) não envolve a duplicação dos buffers subjacentes: somente as referências
e os deslocamentos relevantes, que representam a "exibição" da memória encapsulada,
são atualizados.

7 Observação

Para estruturas anteriores, Span<T> e Memory<T> estão disponíveis no pacote do


System.Memory NuGet .

Para obter mais informações, consulte o namespace de System.Buffers.

Como trabalhando com memória e extensão


Como os tipos relacionados a memória e extensão normalmente são usados para
armazenar dados em um pipeline de processamento, é importante que os
desenvolvedores sigam um conjunto de melhores práticas ao usar Span<T>,
Memory<T> e tipos relacionados. Essas melhores práticas estão documentadas em
Diretrizes de uso de Memória<T> e Extensão<T>.

Confira também
System.Memory<T>
System.ReadOnlyMemory<T>
System.Span<T>
System.ReadOnlySpan<T>
System.Buffers

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Diretrizes de uso de Memory<T> e
Span<T>
Artigo • 14/03/2023

O .NET Core inclui vários tipos que representam uma região contígua arbitrária de
memória. O .NET Core 2.0 introduziu Span<T> e ReadOnlySpan<T>, que são buffers de
memória leves que encapsulam referências à memória gerenciada ou não gerenciada.
Como esses tipos só podem ser armazenados na pilha, eles são inadequados para vários
cenários, incluindo chamadas de método assíncronas. O .NET Core 2.1 adiciona vários
outros tipos, inclusive Memory<T>, ReadOnlyMemory<T>, IMemoryOwner<T> e
MemoryPool<T>. Assim como Span<T>, Memory<T> e os tipos relacionados podem
ter suporte de memória gerenciada e não gerenciada. Diferentemente de Span<T>,
Memory<T> pode ser armazenado no heap gerenciado.

Tanto Span<T> como Memory<T> são wrappers sobre buffers de dados estruturados
que podem ser usados em pipelines. Ou seja, eles são projetados para que alguns ou
todos os dados possam ser passados com eficiência para os componentes no pipeline
que pode processá-los e, opcionalmente, modificar o buffer. Como Memory<T> e os
tipos relacionados podem ser acessados por vários componentes ou threads, é
importante que os desenvolvedores sigam algumas diretrizes de uso padrão para criar
um código robusto.

Gerenciamento de proprietários, consumidores


e vida útil
Como os buffers podem ser passados entre APIs e, às vezes, ser acessados de vários
threads, é importante levar em consideração o gerenciamento da vida útil. Os três
principais conceitos são:

Propriedade. O proprietário de uma instância de buffer é responsável pelo


gerenciamento da vida útil, inclusive pela destruição do buffer quando ele não está
mais em uso. Todos os buffers pertencem a um único proprietário. Geralmente, o
proprietário é o componente que criou o buffer ou que recebeu o buffer de
fábrica. É possível também transferir a propriedade. O Componente-A poderá
ceder o controle do buffer ao Componente-B, passando o Componente-A a não
poder mais usar o buffer. A partir daí, o Componente-B se tornará responsável por
destruir o buffer quando ele não estiver mais em uso.
Consumo. O consumidor de uma instância do buffer pode usar essa instância,
lendo e possivelmente gravando nela. Os buffers podem ter um consumidor por
vez, a menos que algum mecanismo de sincronização externo seja fornecido. O
consumidor ativo de um buffer não é necessariamente o proprietário do buffer.

Concessão. A concessão é o período de tempo em que um componente específico


pode ser o consumidor do buffer.

O exemplo de pseudocódigo a seguir ilustra esses três conceitos. Buffer no


pseudocódigo representa um buffer Memory<T> ou Span<T> do tipo Char. O método
Main cria uma instância para o buffer, chama o método WriteInt32ToBuffer para gravar
a representação da cadeia de caracteres de um número inteiro no buffer e, em seguida,
chama o método DisplayBufferToConsole para exibir o valor do buffer.

C#

using System;

class Program
{
// Write 'value' as a human-readable string to the output buffer.
void WriteInt32ToBuffer(int value, Buffer buffer);

// Display the contents of the buffer to the console.


void DisplayBufferToConsole(Buffer buffer);

// Application code
static void Main()
{
var buffer = CreateBuffer();
try
{
int value = Int32.Parse(Console.ReadLine());
WriteInt32ToBuffer(value, buffer);
DisplayBufferToConsole(buffer);
}
finally
{
buffer.Destroy();
}
}
}

O método Main cria o buffer e, portanto, ele é o respectivo proprietário. Dessa forma,
Main é responsável por destruir o buffer quando ele não está mais em uso. O
pseudocódigo ilustra isso chamando um método Destroy no buffer. (Nem Memory<T>
nem Span<T> tem realmente um método Destroy . Você verá exemplos de código reais
mais adiante neste artigo.)
O buffer tem dois consumidores: WriteInt32ToBuffer e DisplayBufferToConsole . Há
apenas um consumidor por vez (primeiro WriteInt32ToBuffer , depois
DisplayBufferToConsole ), e nenhum dos consumidores é proprietário do buffer. Ainda,

"consumidor" neste contexto não implica uma exibição somente leitura do buffer. Os
consumidores podem modificar o conteúdo do buffer, assim como WriteInt32ToBuffer
o faz, se for fornecida uma exibição de leitura/gravação do buffer.

O método WriteInt32ToBuffer tem uma concessão (pode consumir) o buffer entre o


início da chamada do método e a hora em que o método é retornado. Da mesma
maneira, DisplayBufferToConsole tem uma concessão no buffer enquanto ele está em
execução, e a concessão é liberada quando o método é desenrolado. Não há APIs para
gerenciamento de concessão. Uma "concessão" é uma questão conceitual.

Memory<T> e o modelo
proprietário/consumidor
Conforme a seção Gerenciamento de proprietários, consumidores e vida útil descreve,
um buffer tem sempre um proprietário. O .NET Core tem suporte para dois modelos de
propriedade:

Um modelo com suporte para propriedade única. Um buffer tem um único


proprietário por toda a vida útil.

Um modelo com suporte para transferência de propriedade. É possível transferir a


propriedade de um buffer do proprietário original (o respectivo criador) para outro
componente que se torna responsável pelo gerenciamento da vida útil do buffer.
Esse proprietário pode, por sua vez, transferir a propriedade para outro
componente, e assim por diante.

Use a interface System.Buffers.IMemoryOwner<T> para gerenciar explicitamente a


propriedade de um buffer. IMemoryOwner<T> tem suporte para os dois modelos de
propriedade. O componente que possui uma referência de IMemoryOwner<T> possui o
buffer. O exemplo a seguir usa uma instância de IMemoryOwner<T> para refletir a
propriedade de um buffer de Memory<T>.

C#

using System;
using System.Buffers;

class Example
{
static void Main()
{
IMemoryOwner<char> owner = MemoryPool<char>.Shared.Rent();

Console.Write("Enter a number: ");


try {
var value = Int32.Parse(Console.ReadLine());

var memory = owner.Memory;

WriteInt32ToBuffer(value, memory);

DisplayBufferToConsole(owner.Memory.Slice(0,
value.ToString().Length));
}
catch (FormatException) {
Console.WriteLine("You did not enter a valid number.");
}
catch (OverflowException) {
Console.WriteLine($"You entered a number less than
{Int32.MinValue:N0} or greater than {Int32.MaxValue:N0}.");
}
finally {
owner?.Dispose();
}
}

static void WriteInt32ToBuffer(int value, Memory<char> buffer)


{
var strValue = value.ToString();

var span = buffer.Span;


for (int ctr = 0; ctr < strValue.Length; ctr++)
span[ctr] = strValue[ctr];
}

static void DisplayBufferToConsole(Memory<char> buffer) =>


Console.WriteLine($"Contents of the buffer: '{buffer}'");
}

Podemos também escrever este exemplo com using:

C#

using System;
using System.Buffers;

class Example
{
static void Main()
{
using (IMemoryOwner<char> owner = MemoryPool<char>.Shared.Rent())
{
Console.Write("Enter a number: ");
try {
var value = Int32.Parse(Console.ReadLine());

var memory = owner.Memory;


WriteInt32ToBuffer(value, memory);
DisplayBufferToConsole(memory.Slice(0,
value.ToString().Length));
}
catch (FormatException) {
Console.WriteLine("You did not enter a valid number.");
}
catch (OverflowException) {
Console.WriteLine($"You entered a number less than
{Int32.MinValue:N0} or greater than {Int32.MaxValue:N0}.");
}
}
}

static void WriteInt32ToBuffer(int value, Memory<char> buffer)


{
var strValue = value.ToString();

var span = buffer.Slice(0, strValue.Length).Span;


strValue.AsSpan().CopyTo(span);
}

static void DisplayBufferToConsole(Memory<char> buffer) =>


Console.WriteLine($"Contents of the buffer: '{buffer}'");
}

Neste código:

O método Main contém a referência à instância de IMemoryOwner<T>, portanto,


o método Main é o proprietário do buffer.

Os métodos WriteInt32ToBuffer e DisplayBufferToConsole aceitam Memory<T>


como uma API pública. Portanto, eles são consumidores do buffer, e o consomem
somente um de cada vez.

Embora o método WriteInt32ToBuffer seja destinado a gravar um valor no buffer, isso


não se aplica ao método DisplayBufferToConsole . Para refletir isso, ele poderia aceitar
um argumento de tipo ReadOnlyMemory<T>. Para obter mais informações sobre
ReadOnlyMemory<T>, consulte a Regra nº 2: usar ReadOnlySpan<T> ou
ReadOnlyMemory<T> se o buffer for somente leitura.

Instâncias de Memory<T> "sem proprietário"


É possível criar uma instância de Memory<T> sem usar IMemoryOwner<T>. Nesse caso,
a propriedade do buffer é implícita, em vez de explícita, e tem suporte apenas para o
modelo de proprietário único. É possível fazer isso da seguinte maneira:

Chamar um dos construtores de Memory<T> diretamente, passando um T[] ,


assim como no exemplo a seguir.

Chamar o método de extensão String.AsMemory para criar uma instância de


ReadOnlyMemory<char> .

C#

using System;

class Example
{
static void Main()
{
Memory<char> memory = new char[64];

Console.Write("Enter a number: ");


var value = Int32.Parse(Console.ReadLine());

WriteInt32ToBuffer(value, memory);
DisplayBufferToConsole(memory);
}

static void WriteInt32ToBuffer(int value, Memory<char> buffer)


{
var strValue = value.ToString();
strValue.AsSpan().CopyTo(buffer.Slice(0, strValue.Length).Span);
}

static void DisplayBufferToConsole(Memory<char> buffer) =>


Console.WriteLine($"Contents of the buffer: '{buffer}'");
}

O método que inicialmente cria a instância de Memory<T> é o proprietário implícito do


buffer. Não é possível transferir a propriedade para nenhum outro componente porque
não há instâncias de IMemoryOwner<T> para facilitar a transferência. Como alternativa,
imagine que o coletor de lixo do runtime possui o buffer, e que todos os métodos
apenas o consomem.

Diretrizes de uso
Como um bloco de memória pertence, mas se destina a ser passado para vários
componentes, alguns dos quais podem operar em um determinado bloco de memória
simultaneamente, é importante estabelecer diretrizes para o uso de Memory<T> e
Span<T>. Diretrizes são necessárias porque:

É possível que um componente retenha uma referência a um bloco de memória


depois que o respectivo proprietário o liberar.

É possível que um componente opere em um buffer, ao mesmo tempo em que


outro componente esteja operando nele, corrompendo os dados no buffer
durante o processo.

Embora a natureza alocada na pilha do Span<T> otimize o desempenho e torne


Span<T> o tipo preferencial para a operação em um bloco de memória, ele
também submete Span<T> para algumas restrições importantes. É importante
saber quando usar Span<T> e quando usar Memory<T>.

A seguir estão nossas recomendações para usar com êxito o Memory<T> e os tipos
relacionados. A orientação que se aplica a Memory<T> e a Span<T> também se aplica
a ReadOnlyMemory<T> e a ReadOnlySpan<T>, a menos que seja explicitamente
definido de outra forma.

Regra n° 1: no caso de uma API síncrona, usar Span<T> em vez de Memory <T>
como parâmetro, sempre que possível.

Span<T> é mais versátil do que Memory<T> e pode representar uma variedade maior
de buffers de memória contíguos. Span<T> também oferece um melhor desempenho
que Memory<T>. Por fim, é possível usar a propriedade Memory<T>.Span para
converter uma instância de Memory<T> em Span<T>, embora a conversão de Span<T>
em Memory<T> seja inviável. Portanto, se os chamadores tiverem uma instância de
Memory<T>, eles poderão chamar seus métodos com os parâmetros Span<T> de
qualquer maneira.

Usando um parâmetro de tipo Span<T> em vez de o tipo Memory<T>, você pode


também escrever uma implementação correta do método de consumo. Você receberá
automaticamente verificações de tempo de compilação para garantir que não esteja
tentando acessar o buffer, além da concessão do método (saiba mais sobre isso a
seguir).

Às vezes, você terá que usar um parâmetro Memory<T> em vez de um parâmetro


Span<T>, mesmo que esteja totalmente síncrono. Talvez uma API da qual você dependa
aceite apenas argumentos Memory<T>. Isso é possível, mas conheça as vantagens e
desvantagens envolvidas com o uso de Memory<T> de maneira síncrona.

Regra n° 2: usar ReadOnlySpan<T> ou ReadOnlyMemory<T>, se o buffer for somente


leitura.
Nos exemplos anteriores, o método DisplayBufferToConsole apenas lê no buffer, sem
modificar o respectivo conteúdo. A assinatura do método deve ser alterada para a
seguinte.

C#

void DisplayBufferToConsole(ReadOnlyMemory<char> buffer);

Na verdade, se combinarmos esta regra com a Regra 1, poderemos fazer ainda melhor e
reescrever a assinatura do método da seguinte forma:

C#

void DisplayBufferToConsole(ReadOnlySpan<char> buffer);

O método DisplayBufferToConsole agora funciona praticamente com todos os tipos de


buffer possíveis: T[] , armazenamento alocado com stackalloc, e assim por diante. Você
pode, inclusive, passar um String diretamente nele! Para obter mais informações,
consulte o problema do GitHub dotnet/docs #25551 .

Regra n° 3: se o método aceitar o Memory<T> e retornar void , não usar a instância


de Memory<T>, quando o método for retornado.

Isso está relacionado ao conceito de "concessão" mencionado anteriormente. A


concessão de um retorno de void do método na instância de Memory<T> começa
quando o método é inserido e termina quando o método é encerrado. Considere o
exemplo a seguir, que chama Log em um loop com base na entrada do console.

C#

using System;
using System.Buffers;

public class Example


{
// implementation provided by third party
static extern void Log(ReadOnlyMemory<char> message);

// user code
public static void Main()
{
using (var owner = MemoryPool<char>.Shared.Rent())
{
var memory = owner.Memory;
var span = memory.Span;
while (true)
{
int value = Int32.Parse(Console.ReadLine());
if (value < 0)
return;

int numCharsWritten = ToBuffer(value, span);


Log(memory.Slice(0, numCharsWritten));
}
}
}

private static int ToBuffer(int value, Span<char> span)


{
string strValue = value.ToString();
int length = strValue.Length;
strValue.AsSpan().CopyTo(span.Slice(0, length));
return length;
}
}

Se Log for um método totalmente síncrono, esse código se comportará como esperado
porque há apenas um consumidor ativo da instância de memória a qualquer momento.
Mas imagine que Log tenha essa implementação.

C#

// !!! INCORRECT IMPLEMENTATION !!!


static void Log(ReadOnlyMemory<char> message)
{
// Run in background so that we don't block the main thread while
performing IO.
Task.Run(() =>
{
StreamWriter sw = File.AppendText(@".\input-numbers.dat");
sw.WriteLine(message);
});
}

Nesta implementação, Log viola a respectiva concessão porque ela ainda tenta usar a
instância de Memory<T> em segundo plano, após o método original ter retornado. O
método Main pode alterar o buffer enquanto Log tenta ler nele, o que pode resultar em
dados corrompidos.

Há várias maneiras de resolver isso:

O método Log pode retornar uma classe Task em vez de void , assim como faz a
seguinte implementação do método Log .

C#
// An acceptable implementation.
static Task Log(ReadOnlyMemory<char> message)
{
// Run in the background so that we don't block the main thread
while performing IO.
return Task.Run(() => {
StreamWriter sw = File.AppendText(@".\input-numbers.dat");
sw.WriteLine(message);
sw.Flush();
});
}

É possível implementar Log da seguinte maneira:

C#

// An acceptable implementation.
static void Log(ReadOnlyMemory<char> message)
{
string defensiveCopy = message.ToString();
// Run in the background so that we don't block the main thread
while performing IO.
Task.Run(() => {
StreamWriter sw = File.AppendText(@".\input-numbers.dat");
sw.WriteLine(defensiveCopy);
sw.Flush();
});
}

Regra n° 4: se o método aceitar o Memory<T> e retornar uma classe Task, não usar a
instância de Memory<T> quando a classe Task passar para um estado terminal.

Esta é apenas a variante assíncrona da Regra 3. O método Log do exemplo anterior


pode ser escrito da seguinte forma para cumprir esta regra:

C#

// An acceptable implementation.
static void Log(ReadOnlyMemory<char> message)
{
// Run in the background so that we don't block the main thread while
performing IO.
Task.Run(() => {
string defensiveCopy = message.ToString();
StreamWriter sw = File.AppendText(@".\input-numbers.dat");
sw.WriteLine(defensiveCopy);
sw.Flush();
});
}
Nesse caso, "estado terminal" significa que a tarefa passa para um estado concluído,
com falha ou cancelado. Em outras palavras, "estado terminal" significa "qualquer coisa
que cause atraso de lançamento ou continuação de execução".

Essa orientação se aplica a métodos que retornam Task, Task<TResult>,


ValueTask<TResult> ou outros tipos semelhantes.

Regra n° 5: se o construtor aceitar o Memory<T> como parâmetro, os métodos da


instância no objeto construído serão considerados consumidores da instância de
Memory<T>.

Considere o seguinte exemplo:

C#

class OddValueExtractor
{
public OddValueExtractor(ReadOnlyMemory<int> input);
public bool TryReadNextOddValue(out int value);
}

void PrintAllOddValues(ReadOnlyMemory<int> input)


{
var extractor = new OddValueExtractor(input);
while (extractor.TryReadNextOddValue(out int value))
{
Console.WriteLine(value);
}
}

Nesse caso, o construtor OddValueExtractor aceita um ReadOnlyMemory<int> como


parâmetro, para que o próprio construtor seja um consumidor da instância de
ReadOnlyMemory<int> , e todos os métodos da instância no valor retornado também

sejam consumidores da instância original de ReadOnlyMemory<int> . Isso significa que


TryReadNextOddValue consome a instância de ReadOnlyMemory<int> , mesmo que a
instância não seja passada diretamente para o método TryReadNextOddValue .

Regra n° 6: quando há uma propriedade tipada configurável de Memory<T> (ou um


método de instância equivalente) no tipo, presume-se que os métodos da instância
nesse objeto sejam consumidores da instância de Memory<T>.

Na verdade, esta é apenas uma variante da Regra 5. Esta regra existe porque presume-
se que os setters de propriedade ou métodos equivalentes devam capturar e persistir as
respectivas entradas, de modo que os métodos da instância no mesmo objeto possam
usar o estado capturado.
O exemplo a seguir aciona esta regra:

C#

class Person
{
// Settable property.
public Memory<char> FirstName { get; set; }

// alternatively, equivalent "setter" method


public SetFirstName(Memory<char> value);

// alternatively, a public settable field


public Memory<char> FirstName;
}

Regra n° 7: quando há uma referência de IMemoryOwner<T>, é necessário descartá-


la ou transferir a propriedade (mas não ambos), em algum momento.

Como uma instância de Memory<T> pode ter suporte de memória gerenciada ou não
gerenciada, o proprietário deve chamar Dispose ou IMemoryOwner<T> quando
concluir o trabalho realizado na instância de Memory<T>. Como alternativa, o
proprietário pode transferir a propriedade da instância de IMemoryOwner<T> para um
componente diferente, até que o componente receptor se torne responsável por
chamar Dispose no tempo adequado (saiba mais sobre isso a seguir).

Uma falha ao chamar o método Dispose ou uma instância de IMemoryOwner<T> pode


causar perdas de memória não gerenciada ou outra degradação do desempenho.

Esta regra também se aplica ao código que chama métodos de fábrica, como
MemoryPool<T>.Rent. O chamador se torna proprietário do IMemoryOwner<T>
retornado e fica responsável pelo descarte da instância após concluída.

Regra n° 8: se tiver um parâmetro IMemoryOwner<T> na superfície da API, então


você estará aceitando a propriedade dessa instância.

Aceitar uma instância desse tipo indica que o componente pretende se apropriar dessa
instância. O componente se torna responsável pelo descarte adequado de acordo com a
Regra 7.

Os componentes que transferem a propriedade da instância de IMemoryOwner<T>


para um componente diferente não devem mais usar essa instância após a conclusão da
chamada do método.

) Importante
Se o construtor aceitar IMemoryOwner<T> como parâmetro, o respectivo tipo
deverá implementar IDisposable, e o método Dispose deverá chamar Dispose no
objeto IMemoryOwner<T>.

Regra n° 9: se estiver encapsulando um método de P/Invoke síncrono, a API deverá


aceitar Span<T> como parâmetro.

De acordo com a Regra 1, Span<T> geralmente é o tipo correto a ser usado para APIs
síncronas. Você pode fixar instâncias de Span<T> por meio da palavra-chave fixed,
como no exemplo a seguir.

C#

using System.Runtime.InteropServices;

[DllImport(...)]
private static extern unsafe int ExportedMethod(byte* pbData, int cbData);

public unsafe int ManagedWrapper(Span<byte> data)


{
fixed (byte* pbData = &MemoryMarshal.GetReference(data))
{
int retVal = ExportedMethod(pbData, data.Length);

/* error checking retVal goes here */

return retVal;
}
}

No exemplo anterior, pbData poderá ser nulo, se o alcance da entrada estiver vazio. Se o
método exportado exigir que pbData seja não nulo, mesmo que cbData seja 0, o
método poderá ser implementado da seguinte maneira:

C#

public unsafe int ManagedWrapper(Span<byte> data)


{
fixed (byte* pbData = &MemoryMarshal.GetReference(data))
{
byte dummy = 0;
int retVal = ExportedMethod((pbData != null) ? pbData : &dummy,
data.Length);

/* error checking retVal goes here */

return retVal;
}
}

Regra n° 10: se estiver encapsulando um método de P/Invoke assíncrono, a API deverá


aceitar Memory<T> como parâmetro.

Como não é possível usar a palavra-chave fixed em operações assíncronas, use o


método Memory<T>.Pin para fixar instâncias de Memory<T>, independentemente do
tipo de memória contígua que a instância representar. O exemplo a seguir mostra como
usar esta API para executar uma chamada de P/Invoke assíncrona.

C#

using System.Runtime.InteropServices;

[UnmanagedFunctionPointer(...)]
private delegate void OnCompletedCallback(IntPtr state, int result);

[DllImport(...)]
private static extern unsafe int ExportedAsyncMethod(byte* pbData, int
cbData, IntPtr pState, IntPtr lpfnOnCompletedCallback);

private static readonly IntPtr _callbackPtr =


GetCompletionCallbackPointer();

public unsafe Task<int> ManagedWrapperAsync(Memory<byte> data)


{
// setup
var tcs = new TaskCompletionSource<int>();
var state = new MyCompletedCallbackState
{
Tcs = tcs
};
var pState = (IntPtr)GCHandle.Alloc(state);

var memoryHandle = data.Pin();


state.MemoryHandle = memoryHandle;

// make the call


int result;
try
{
result = ExportedAsyncMethod((byte*)memoryHandle.Pointer,
data.Length, pState, _callbackPtr);
}
catch
{
((GCHandle)pState).Free(); // cleanup since callback won't be
invoked
memoryHandle.Dispose();
throw;
}
if (result != PENDING)
{
// Operation completed synchronously; invoke callback manually
// for result processing and cleanup.
MyCompletedCallbackImplementation(pState, result);
}

return tcs.Task;
}

private static void MyCompletedCallbackImplementation(IntPtr state, int


result)
{
GCHandle handle = (GCHandle)state;
var actualState = (MyCompletedCallbackState)(handle.Target);
handle.Free();
actualState.MemoryHandle.Dispose();

/* error checking result goes here */

if (error)
{
actualState.Tcs.SetException(...);
}
else
{
actualState.Tcs.SetResult(result);
}
}

private static IntPtr GetCompletionCallbackPointer()


{
OnCompletedCallback callback = MyCompletedCallbackImplementation;
GCHandle.Alloc(callback); // keep alive for lifetime of application
return Marshal.GetFunctionPointerForDelegate(callback);
}

private class MyCompletedCallbackState


{
public TaskCompletionSource<int> Tcs;
public MemoryHandle MemoryHandle;
}

Confira também
System.Memory<T>
System.Buffers.IMemoryOwner<T>
System.Span<T>
Usar tipos numéricos acelerados por
SIMD
Artigo • 10/05/2023

O SIMD (instrução única, vários dados) fornece suporte a hardware para executar uma
operação em vários dados, em paralelo, usando uma só instrução. No .NET, há um
conjunto de tipos acelerados por SIMD no namespace System.Numerics. Operações
SIMD podem ser paralelizadas no nível de hardware. Isso aumenta a taxa de
transferência dos cálculos vetorizadas, que são comuns em aplicativos matemáticos,
científicos e gráficos.

Tipos acelerados por SIMD do .NET


Os tipos acelerados por SIMD do .NET incluem os seguintes tipos:

Os tipos Vector2, Vector3 e Vector4, que representam vetores com 2, 3 e 4 valores


de Single.

Dois tipos de matriz, Matrix3x2, que representa uma matriz 3x2, e Matrix4x4, que
representa uma matriz 4x4 de valores Single.

O tipo Plane representa um plano no espaço tridimensional usando valores Single.

O tipo Quaternion, que representa um vetor usado para codificar rotações físicas
tridimensionais usando valores Single.

O tipo Vector<T>, que representa um vetor de um tipo numérico especificado e


fornece um amplo conjunto de operadores que se beneficiam de suporte a SIMD.
A contagem de uma instância Vector<T> é fixa para o tempo de vida de um
aplicativo, mas seu valor Vector<T>.Count depende da CPU do computador que
executa o código.

7 Observação

O tipo Vector<T> não está incluído no .NET Framework. Você deve instalar o
pacote System.Numerics.Vectors do NuGet para obter acesso a esse tipo.

Os tipos acelerados por SIMD são implementados de modo que possam ser usados
com hardware não acelerados por SIMD ou compiladores JIT. Para aproveitar as
instruções SIMD, os aplicativos de 64 bits deverão ser executados pelo runtime que
utiliza o compilador RyuJIT. Um compilador RyuJIT é incluído no .NET Core e no .NET
Framework 4.6 e posteriores. O suporte a SIMD só é fornecido ao direcionar
processadores de 64 bits.

Como usar o SIMD?


Antes de executar algoritmos SIMD personalizados, é possível verificar se o computador
host dá suporte a SIMD usando Vector.IsHardwareAccelerated, o que retorna um
Boolean. Isso não garante que a aceleração de SIMD esteja habilitada para um tipo
específico, mas é um indicador de que ela é compatível com alguns tipos.

Vetores simples
Os tipos mais primitivos acelerados por SIMD no .NET são tipos Vector2, Vector3 e
Vector4, que representam vetores com valores Single 2, 3 e 4. O exemplo a seguir usa
Vector2 para adicionar dois vetores.

C#

var v1 = new Vector2(0.1f, 0.2f);


var v2 = new Vector2(1.1f, 2.2f);
var vResult = v1 + v2;

Também é possível usar vetores .NET para calcular outras propriedades matemáticas de
vetores, como Dot product , Transform , Clamp etc.

C#

var v1 = new Vector2(0.1f, 0.2f);


var v2 = new Vector2(1.1f, 2.2f);
var vResult1 = Vector2.Dot(v1, v2);
var vResult2 = Vector2.Distance(v1, v2);
var vResult3 = Vector2.Clamp(v1, Vector2.Zero, Vector2.One);

Matriz
Matrix3x2, que representa uma matriz 3x2, e Matrix4x4, que representa uma matriz 4x4.
Pode ser usado para cálculos relacionados à matriz. O exemplo a seguir demonstra a
multiplicação de uma matriz para sua matriz de transposição correspondente usando
SIMD.

C#
var m1 = new Matrix4x4(
1.1f, 1.2f, 1.3f, 1.4f,
2.1f, 2.2f, 3.3f, 4.4f,
3.1f, 3.2f, 3.3f, 3.4f,
4.1f, 4.2f, 4.3f, 4.4f);

var m2 = Matrix4x4.Transpose(m1);
var mResult = Matrix4x4.Multiply(m1, m2);

Vector<T>
O Vector<T> permite usar vetores mais longos. A contagem de uma instância
Vector<T> é corrigida, mas seu valor Vector<T>.Count depende da CPU do computador
que executa o código.

O exemplo a seguir demonstra como calcular a soma em termos de elemento de duas


matrizes usando Vector<T>.

C#

double[] Sum(double[] left, double[] right)


{
if (left is null)
{
throw new ArgumentNullException(nameof(left));
}

if (right is null)
{
throw new ArgumentNullException(nameof(right));
}

if (left.Length != right.Length)
{
throw new ArgumentException($"{nameof(left)} and {nameof(right)} are
not the same length");
}

int length = left.Length;


double[] result = new double[length];

// Get the number of elements that can't be processed in the vector


// NOTE: Vector<T>.Count is a JIT time constant and will get optimized
accordingly
int remaining = length % Vector<double>.Count;

for (int i = 0; i < length - remaining; i += Vector<double>.Count)


{
var v1 = new Vector<double>(left, i);
var v2 = new Vector<double>(right, i);
(v1 + v2).CopyTo(result, i);
}

for (int i = length - remaining; i < length; i++)


{
result[i] = left[i] + right[i];
}

return result;
}

Comentários
É mais provável que o SIMD remova um gargalo e exponha o próximo, por exemplo, a
taxa de transferência de memória. Em geral, o benefício de desempenho do uso do
SIMD varia conforme o cenário específico e, em alguns casos, pode até mesmo ter um
desempenho pior do que o código equivalente não SIMD mais simples.
Tuplas de valor
Artigo • 10/01/2024

Uma tupla de valor é uma estrutura de dados que tem um número específico e uma
sequência de valores. O .NET fornece os seguintes tipos de tupla de valor interno:

A estrutura ValueTuple<T1> representa uma tupla de valor que tem um elemento.


A estrutura ValueTuple<T1,T2> representa uma tupla de valor que tem dois
elementos.-
A estrutura ValueTuple<T1,T2,T3> representa uma tupla de valor que tem três
elementos.
A estrutura ValueTuple<T1,T2,T3,T4> representa uma tupla de valor que tem
quatro elementos.
A estrutura ValueTuple<T1,T2,T3,T4,T5> representa uma tupla de valor que tem
cinco elementos.
A estrutura ValueTuple<T1,T2,T3,T4,T5,T6> representa uma tupla de valor que tem
seis elementos.
A estrutura ValueTuple<T1,T2,T3,T4,T5,T6,T7> representa uma tupla de valor que
tem sete elementos.
A estrutura ValueTuple<T1,T2,T3,T4,T5,T6,T7,TRest> representa uma tupla de valor
que tem oito ou mais elementos.

Os tipos de tupla de valor diferem dos tipos de tupla (como Tuple<T1,T2>) da seguinte
maneira:

São estruturas (tipos de valor) em vez de classes (tipos de referência).


Membros como Item1 e Item2 são campos em vez de propriedades.
Seus campos são mutáveis em vez de somente leitura.

Os tipos de tupla de valor fornecem a implementação de runtime que dá suporte a


tuplas em C# e tuplas de struct em F#. Além de criar uma instância ValueTuple<T1,T2>
usando a sintaxe de linguagem, você pode chamar o método de fábrica Create.

Confira também
Tipos de tupla – (referência do C#)

6 Colaborar conosco no Comentários do .NET


GitHub
A fonte deste conteúdo pode O .NET é um projeto código aberto.
ser encontrada no GitHub, onde Selecione um link para fornecer
você também pode criar e comentários:
revisar problemas e solicitações
de pull. Para obter mais  Abrir um problema de
informações, confira o nosso documentação
guia para colaboradores.
 Fornecer comentários sobre o
produto
Visão geral das bibliotecas de runtime
Artigo • 02/02/2024

O runtime .NET tem um conjunto padrão expansivo de bibliotecas de classes,


conhecidas como bibliotecas de runtime, bibliotecas de estrutura, ou BCL (biblioteca de
classes base). Além disso, há extensões para as bibliotecas de runtime, fornecidas em
pacotes NuGet.

Essas bibliotecas fornecem implementações para muitos algoritmos, funcionalidades do


utilitário e tipos gerais e específicos do aplicativo.

Bibliotecas de runtime
As bibliotecas fornecem a funcionalidade de utilitário e tipos básicos e são a base de
todas as outras bibliotecas de classes do .NET. Um exemplo é a classe System.String,
que fornece APIs para trabalhar com cadeias de caracteres. Outro exemplo são as
bibliotecas de serialização.

Extensões para as bibliotecas de runtime


Algumas bibliotecas são fornecidas em pacotes NuGet em vez de incluídas na estrutura
compartilhada do runtime. Por exemplo:

ノ Expandir a tabela

Conteúdo conceitual Pacote NuGet

Configuration Microsoft.Extensions.Configuration

Injeção de dependência Microsoft.Extensions.DependencyInjection

Globbing de arquivo Microsoft.Extensions.FileSystemGlobbing

Host Genérico Microsoft.Extensions.Hosting

HTTP †Microsoft.Extensions.Http

Localização Microsoft.Extensions.Localization

Logging Microsoft.Extensions.Logging

† Para algumas estruturas de destino, incluindo net6.0 , essas bibliotecas fazem parte da
estrutura compartilhada e não precisam ser instaladas separadamente.
Confira também
Introdução ao .NET
Instalar o SDK ou o runtime do .NET
Selecionar o SDK do .NET instalado ou a versão de runtime a ser usada
Publicar aplicativos dependentes de estrutura

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Visão geral: como formatar números,
datas, enumerações e outros tipos no
.NET
Artigo • 10/05/2023

Formatação é o processo de converter uma instância de uma classe ou estrutura, ou um


valor de enumeração, em uma representação de cadeia de caracteres. A finalidade é
exibir a cadeia de caracteres resultante para os usuários ou desserializá-la
posteriormente para restaurar o tipo de dados original. Este artigo apresenta os
mecanismos de formatação que o .NET oferece.

7 Observação

A análise é o inverso da formatação. Uma operação de análise cria uma instância de


um tipo de dados com base em na representação de sua cadeia de caracteres. Para
obter mais informações, consulte Analisar cadeias de caracteres. Para saber mais
sobre serialização e desserialização, confira Serialização no .NET.

O mecanismo básico de formatação é a implementação padrão do método


Object.ToString, que é abordado na seção Formatação padrão usando o método
ToString, mais adiante neste tópico. No entanto, o .NET oferece várias maneiras de
modificar e estender o suporte à formatação padrão. Elas incluem o seguinte:

Substituir o método Object.ToString para definir uma representação de cadeia de


caracteres personalizada do valor de um objeto. Para obter mais informações, veja
a seção Substituir o método ToString mais adiante neste tópico.

Definir especificadores de formato que permitem que a representação de cadeia


de caracteres do valor de um objeto assuma várias formas. Por exemplo, o
especificador de formato "X" na instrução a seguir converte um inteiro na
representação de cadeia de caracteres de um valor hexadecimal.

C#

int integerValue = 60312;


Console.WriteLine(integerValue.ToString("X")); // Displays EB98.

Para obter mais informações sobre especificadores de formato, veja a seção


Cadeias de caracteres de formato e método ToString.
Usar provedores de formato para implementar as convenções de formatação de
uma cultura específica. Por exemplo, a instrução a seguir exibe um valor de moeda
usando as convenções de formatação da cultura en-US.

C#

double cost = 1632.54;


Console.WriteLine(cost.ToString("C",
new System.Globalization.CultureInfo("en-US")));
// The example displays the following output:
// $1,632.54

Para obter mais informações sobre a formatação com provedores de formato,


confira a seção Provedores de formato.

A implementação da interface IFormattable para dar suporte tanto à conversão de


cadeia de caracteres com a classe Convert quanto à formatação de composição.
Para obter mais informações, consulte a seção Interface IFormattable.

O uso da formatação de composição para inserir a representação de cadeia de


caracteres de um valor em uma cadeia de caracteres maior. Para obter mais
informações, veja a seção Formatação composição.

Usar a interpolação de cadeia de caracteres, uma sintaxe mais legível para inserir a
representação de cadeia de caracteres de um valor em uma cadeia de caracteres
maior. Para saber mais, confira Interpolação de cadeia de caracteres.

Implementando ICustomFormatter e IFormatProvider para fornecer uma solução


completa de formatação personalizada. Para obter mais informações, veja a seção
Formatação personalizada com ICustomFormatter.

As seções a seguir examinam esses métodos para converter um objeto em sua


representação de cadeia de caracteres.

Formatação padrão usando o método ToString


Cada tipo é derivado de System.Object herda automaticamente um método ToString
sem parâmetros, que retorna o nome do tipo por padrão. O exemplo a seguir ilustra o
método ToString padrão. Ele define uma classe chamada Automobile que não tem
nenhuma implementação. Quando a classe é instanciada e seu método ToString é
chamado, ele exibe seu nome de tipo. Observe que o método ToString não é chamado
explicitamente no exemplo. O método Console.WriteLine(Object) chama implicitamente
o método ToString do objeto é passado para ele como um argumento.
C#

using System;

public class Automobile


{
// No implementation. All members are inherited from Object.
}

public class Example9


{
public static void Main()
{
Automobile firstAuto = new Automobile();
Console.WriteLine(firstAuto);
}
}
// The example displays the following output:
// Automobile

2 Aviso

A partir do Windows 8.1, o Windows Runtime inclui uma interface IStringable com
um único método, IStringable.ToString, que fornece suporte à formatação padrão.
No entanto, recomendamos que tipos gerenciados não implementem a interface
IStringable . Para obter mais informações, veja a seção "Windows Runtime e a

Interface IStringable " na página de referência Object.ToString.

Já que todos os tipos, com a exceção das interfaces, são derivados de Object, essa
funcionalidade é fornecida automaticamente para suas estruturas ou classes
personalizadas. No entanto, a funcionalidade oferecida pelo método ToString padrão é
limitada: embora ele identifique o tipo, não fornece nenhuma informação sobre uma
instância do tipo. Para fornecer uma representação de cadeia de caracteres de um
objeto que fornece informações sobre o objeto, você deve substituir o método
ToString .

7 Observação

As estruturas herdam de ValueType, que por sua vez é derivado de Object. Embora
ValueType substitua Object.ToString, sua implementação é idêntica.

Substituir o método ToString


A exibição do nome de um tipo é geralmente de uso limitado e não permite que os
consumidores dos seus tipos diferenciem uma instância da outra. No entanto, você
pode substituir o método ToString para fornecer uma representação mais útil do valor
de um objeto. O exemplo a seguir define um objeto Temperature e substitui seu método
ToString para exibir a temperatura em graus Celsius.

C#

public class Temperature


{
private decimal temp;

public Temperature(decimal temperature)


{
this.temp = temperature;
}

public override string ToString()


{
return this.temp.ToString("N1") + "°C";
}
}

public class Example12


{
public static void Main()
{
Temperature currentTemperature = new Temperature(23.6m);
Console.WriteLine($"The current temperature is
{currentTemperature}");
}
}
// The example displays the following output:
// The current temperature is 23.6°C.

No .NET, o método ToString de cada tipo de valor primitivo foi substituído para exibir o
valor do objeto em vez de seu nome. A tabela a seguir mostra a substituição de cada
tipo primitivo. Observe que a maioria dos métodos substituídos chama outra
sobrecarga do método ToString e passa-a ao especificador de formato "G", que define
o formato geral de seu tipo, além de um objeto IFormatProvider que representa a
cultura atual.

Tipo Substituição de ToString

Boolean Retorna Boolean.TrueString ou Boolean.FalseString.

Byte Chama Byte.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Byte para a cultura atual.
Tipo Substituição de ToString

Char Retorna o caractere como uma cadeia de caracteres.

DateTime Chama DateTime.ToString("G", DatetimeFormatInfo.CurrentInfo) para formatar o


valor de data e hora para a cultura atual.

Decimal Chama Decimal.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Decimal para a cultura atual.

Double Chama Double.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Double para a cultura atual.

Int16 Chama Int16.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Int16 para a cultura atual.

Int32 Chama Int32.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Int32 para a cultura atual.

Int64 Chama Int64.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Int64 para a cultura atual.

SByte Chama SByte.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


SByte para a cultura atual.

Single Chama Single.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


Single para a cultura atual.

UInt16 Chama UInt16.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


UInt16 para a cultura atual.

UInt32 Chama UInt32.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


UInt32 para a cultura atual.

UInt64 Chama UInt64.ToString("G", NumberFormatInfo.CurrentInfo) para formatar o valor


UInt64 para a cultura atual.

As cadeias de caractere de formato e do


método ToString
Contar com o método padrão ToString ou substituir ToString é apropriado quando um
objeto tem uma única representação de cadeia de caracteres. No entanto, o valor de um
objeto normalmente tem várias representações. Por exemplo, uma temperatura pode
ser expressa em graus Fahrenheit, graus Celsius ou kelvins. De forma similar, o valor de
inteiro 10 pode ser representado de várias maneiras, incluindo 10, 10,0, 1.0e01 ou US
$10,00.
Para permitir que um único valor tenha várias representações de cadeia de caracteres, o
.NET usa cadeias de caracteres de formato. Uma cadeia de caracteres de formato é uma
cadeia de caracteres que contém um ou mais especificadores de formato predefinidos,
que são caracteres únicos ou grupos de caracteres que definem como o método
ToString deve formatar sua saída. A cadeia de caracteres de formato é passada como

um parâmetro para o método ToString do objeto e determina como a representação


de cadeia de caracteres do valor do objeto deve ser exibida.

Todos os tipos numéricos, de data e hora e tipos de enumeração no .NET dão suporte a
um conjunto predefinido de especificadores de formato. Você também pode usar
cadeias de caracteres de formato para definir várias representações de cadeia de
caracteres de seus tipos de dados definidos pelo aplicativo.

Cadeias de caracteres de formato padrão


Uma cadeia de caracteres de formato padrão contém um único especificador de
formato, que é um caractere alfabético que define a representação de cadeia de
caracteres do objeto ao qual ela é aplicada, junto com um especificador de precisão
opcional que afeta a quantos dígitos serão exibidos na cadeia de caracteres de
resultado. Se o especificador de precisão for omitido ou não houver suporte a ele, um
especificador de formato padrão será equivalente a uma cadeia de caracteres de
formato padrão.

O .NET define um conjunto de especificadores de formato padrão para todos os tipos


numéricos, todos os tipos de data e hora e todos os tipos de enumeração. Por exemplo,
cada uma dessas categorias dá suporte a um especificador de formato padrão "G", que
define uma representação de cadeia de caracteres geral de um valor desse mesmo tipo.

Cadeias de caracteres de formato padrão para tipos de enumeração controlam


diretamente a representação de cadeia de caracteres de um valor. As cadeias de
caracteres de formato passadas para o método ToString de um valor de enumeração
determinam se o valor é exibido usando seu nome de cadeia de caracteres (os
especificadores de formato "G" e "F"), seu valor integral subjacente (o especificador de
formato "D") ou seu valor hexadecimal (o especificador de formato "X"). O exemplo a
seguir ilustra o uso de cadeias de caracteres de formato padrão para formatar um valor
de enumeração DayOfWeek.

C#

DayOfWeek thisDay = DayOfWeek.Monday;


string[] formatStrings = {"G", "F", "D", "X"};

foreach (string formatString in formatStrings)


Console.WriteLine(thisDay.ToString(formatString));
// The example displays the following output:
// Monday
// Monday
// 1
// 00000001

Para obter informações sobre cadeias de caracteres de formato de enumeração, veja


Cadeias de caracteres de formato de enumeração.

Cadeias de caracteres de formato padrão para tipos numéricos geralmente definem


uma cadeia de caracteres de resultado cuja aparência exata é controlada por um ou
mais valores de propriedade. Por exemplo, o especificador de formato "C" formata um
número como um valor de moeda. Quando você chama o método ToString com o
especificador de formato "C" como o único parâmetro, os seguintes valores de
propriedade do objeto NumberFormatInfo da cultura atual serão usados para definir a
representação de cadeia de caracteres do valor numérico:

A propriedade CurrencySymbol, que especifica o símbolo da moeda da cultura


atual.

A propriedade CurrencyNegativePattern ou CurrencyPositivePattern, que retorna


um inteiro que determina o seguinte:

O posicionamento do símbolo da moeda.

Se valores negativos são indicados por um sinal de negativo à esquerda, um


sinal de negativo à direita ou parênteses.

Se um espaço é ou não exibido entre o valor numérico e o símbolo da moeda.

A propriedade CurrencyDecimalDigits, que define o número de dígitos fracionários


na cadeia de caracteres de resultado.

O propriedade CurrencyDecimalSeparator, que define o símbolo do separador


decimal na cadeia de caracteres de resultado.

A propriedade CurrencyGroupSeparator, que define o símbolo de separador de


grupo.

A propriedade CurrencyGroupSizes, que define o número de dígitos em cada


grupo à esquerda da vírgula decimal.

A propriedade NegativeSign, que determinará o sinal de negativo usado na cadeia


de caracteres de resultado se parênteses não forem usados para indicar valores
negativos.
Além disso, cadeias de caracteres de formato numérico podem incluir um especificador
de precisão. O significado desse especificador depende da cadeia de caracteres de
formato com o qual ele é usado, mas ele normalmente indica o número total de dígitos
ou o número de dígitos fracionários que devem aparecer na cadeia de caracteres de
resultado. Por exemplo, o exemplo a seguir usa a cadeia de caracteres numérica padrão
"X4" e um especificador de precisão para criar um valor de cadeia de caracteres com
quatro dígitos hexadecimais.

C#

byte[] byteValues = { 12, 163, 255 };


foreach (byte byteValue in byteValues)
Console.WriteLine(byteValue.ToString("X4"));
// The example displays the following output:
// 000C
// 00A3
// 00FF

Para obter mais informações sobre cadeias de caracteres de formatação numérica de


padrão, veja Cadeias de caracteres de formato numérico padrão.

Cadeias de caracteres de formato padrão para valores de data e hora são aliases para
cadeias de caracteres de formato personalizado armazenadas por uma propriedade
DateTimeFormatInfo particular. Por exemplo, chamar o método ToString de um valor
de data e hora com o especificador de formato "D" exibe a data e hora usando a cadeia
de caracteres de formato personalizado armazenada na propriedade
DateTimeFormatInfo.LongDatePattern da cultura atual. (Para obter mais informações
sobre cadeias de caracteres de formato personalizado, consulte a próxima seção.) O
exemplo a seguir ilustra essa relação.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
DateTime date1 = new DateTime(2009, 6, 30);
Console.WriteLine("D Format Specifier: {0:D}", date1);
string longPattern =
CultureInfo.CurrentCulture.DateTimeFormat.LongDatePattern;
Console.WriteLine("'{0}' custom format string: {1}",
longPattern, date1.ToString(longPattern));
}
}
// The example displays the following output when run on a system whose
// current culture is en-US:
// D Format Specifier: Tuesday, June 30, 2009
// 'dddd, MMMM dd, yyyy' custom format string: Tuesday, June 30, 2009

Para obter mais informações sobre o padrão de data e cadeias de caracteres de formato
de hora, veja Cadeias de caracteres de formato de data e hora padrão.

Você também pode usar cadeias de caracteres de formato padrão para definir a
representação de cadeia de caracteres de um objeto definido pelo aplicativo que é
produzido pelo método ToString(String) do objeto. Você pode definir os
especificadores de formato padrão específicos que dão suporte a seu objeto e você
pode determinar se eles diferenciam ou não maiúsculas de minúsculas. A
implementação do ToString(String) método deve dar suporte ao seguinte:

Um especificador de formato "G" que representa um formato comum ou habitual


do objeto. A sobrecarga sem parâmetros do método ToString de seu objeto deve
chamar sua sobrecarga ToString(String) e passar cadeia de caracteres de formato
padrão "G".

Suporte para um especificador de formato que é igual a uma referência nula


( Nothing em Visual Basic). Um especificador de formato igual a uma referência
nula deve ser considerado equivalente ao especificador de formato "G".

Por exemplo, um Temperature classe interna pode armazenar a temperatura em graus


Celsius e usar especificadores de formato para representar o valor do objeto
Temperature em graus Fahrenheit, graus Celsius e kelvins. O exemplo a seguir ilustra

esse cenário.

C#

using System;

public class Temperature


{
private decimal m_Temp;

public Temperature(decimal temperature)


{
this.m_Temp = temperature;
}

public decimal Celsius


{
get { return this.m_Temp; }
}
public decimal Kelvin
{
get { return this.m_Temp + 273.15m; }
}

public decimal Fahrenheit


{
get { return Math.Round(((decimal) (this.m_Temp * 9 / 5 + 32)), 2); }
}

public override string ToString()


{
return this.ToString("C");
}

public string ToString(string format)


{
// Handle null or empty string.
if (String.IsNullOrEmpty(format)) format = "C";
// Remove spaces and convert to uppercase.
format = format.Trim().ToUpperInvariant();

// Convert temperature to Fahrenheit and return string.


switch (format)
{
// Convert temperature to Fahrenheit and return string.
case "F":
return this.Fahrenheit.ToString("N2") + " °F";
// Convert temperature to Kelvin and return string.
case "K":
return this.Kelvin.ToString("N2") + " K";
// return temperature in Celsius.
case "G":
case "C":
return this.Celsius.ToString("N2") + " °C";
default:
throw new FormatException(String.Format("The '{0}' format string
is not supported.", format));
}
}
}

public class Example1


{
public static void Main()
{
Temperature temp1 = new Temperature(0m);
Console.WriteLine(temp1.ToString());
Console.WriteLine(temp1.ToString("G"));
Console.WriteLine(temp1.ToString("C"));
Console.WriteLine(temp1.ToString("F"));
Console.WriteLine(temp1.ToString("K"));

Temperature temp2 = new Temperature(-40m);


Console.WriteLine(temp2.ToString());
Console.WriteLine(temp2.ToString("G"));
Console.WriteLine(temp2.ToString("C"));
Console.WriteLine(temp2.ToString("F"));
Console.WriteLine(temp2.ToString("K"));

Temperature temp3 = new Temperature(16m);


Console.WriteLine(temp3.ToString());
Console.WriteLine(temp3.ToString("G"));
Console.WriteLine(temp3.ToString("C"));
Console.WriteLine(temp3.ToString("F"));
Console.WriteLine(temp3.ToString("K"));

Console.WriteLine(String.Format("The temperature is now {0:F}.",


temp3));
}
}
// The example displays the following output:
// 0.00 °C
// 0.00 °C
// 0.00 °C
// 32.00 °F
// 273.15 K
// -40.00 °C
// -40.00 °C
// -40.00 °C
// -40.00 °F
// 233.15 K
// 16.00 °C
// 16.00 °C
// 16.00 °C
// 60.80 °F
// 289.15 K
// The temperature is now 16.00 °C.

Cadeias de formato personalizadas


Além de cadeias de caracteres de formato padrão, o .NET define cadeias de caracteres
de formato personalizado para valores numéricos e valores de data e hora. Uma cadeia
de caracteres de formato personalizado consiste em um ou mais especificadores de
formato personalizado que definem a representação de cadeia de caracteres de um
valor. Por exemplo, a cadeia de caracteres de formato de data e hora personalizada
"aaaa/mm/dd hh:mm:ss.ffff t zzz" converte uma data em sua representação de cadeia de
caracteres no formato "2008/11/15 07:45:00.0000 P -08:00" para a cultura en-US. Da
mesma forma, a cadeia de caracteres de formato personalizado "0000" converte o valor
inteiro 12 em "0012". Para obter uma lista completa de cadeias de caracteres de formato
personalizado, veja Cadeias de caracteres de formato de data e hora personalizado e
Cadeias de caracteres de formato numérico personalizado.
Se uma cadeia de caracteres de formato consiste em um único especificador de formato
personalizado, o especificador de formato deve ser precedido pelo símbolo de
porcentagem (%) para evitar confusão com um especificador de formato padrão. O
exemplo a seguir usa o especificador de formato personalizado "M" para exibir um
número de um ou dois dígitos do mês de uma data específica.

C#

DateTime date1 = new DateTime(2009, 9, 8);


Console.WriteLine(date1.ToString("%M")); // Displays 9

Muitas cadeias de caracteres de formato padrão para valores de data e hora são aliases
para cadeias de caracteres de formato personalizado que são definidas pelas
propriedades do objeto DateTimeFormatInfo. Cadeias de caracteres de formato
personalizado também oferecem flexibilidade considerável no fornecimento de
formação definida pelo aplicativo para valores numéricos ou valores de data e hora.
Você pode definir suas próprias cadeias de caracteres de resultado personalizadas para
valores numéricos e valores de data e hora combinando vários especificadores de
formato personalizado em uma única cadeia de caracteres de formato personalizado. O
exemplo a seguir define uma cadeia de caracteres de formato personalizado que exibe
o dia da semana entre parênteses após o nome do mês, o dia e o ano.

C#

string customFormat = "MMMM dd, yyyy (dddd)";


DateTime date1 = new DateTime(2009, 8, 28);
Console.WriteLine(date1.ToString(customFormat));
// The example displays the following output if run on a system
// whose language is English:
// August 28, 2009 (Friday)

O exemplo a seguir define uma cadeia de caracteres de formato personalizado que


exibe um valor Int64 como um número de telefone de sete dígitos padrão dos EUA,
junto com seu código de área.

C#

using System;

public class Example17


{
public static void Main()
{
long number = 8009999999;
string fmt = "000-000-0000";
Console.WriteLine(number.ToString(fmt));
}
}
// The example displays the following output:
// 800-999-9999

Embora as cadeias de caracteres de formato padrão geralmente tratem da maioria das


necessidades de formatação para os tipos definidos pelo aplicativo, você também pode
definir especificadores de formato personalizado para formatar seus tipos.

Cadeias de caracteres de formato e tipos do .NET


Todos os tipos numéricos (ou seja, os tipos Byte, Decimal, Double, Int16, Int32, Int64,
SByte, Single, UInt16, UInt32, UInt64 e BigInteger), bem como o DateTime,
DateTimeOffset, TimeSpan, Guid, e todos os tipos de enumeração, suportam a
formatação com cadeias de caracteres de formato. Para obter informações sobre as
cadeias de caracteres de formato específicas às quais cada tipo dá suporte, veja os
seguintes tópicos:

Título Definição

Cadeias de Caracteres de Descreve cadeias de caracteres de formato padrão que criam


Formato Numérico representações de cadeia de caracteres de valores numéricos
Padrão frequentemente usadas.

Cadeias de caracteres de Descreve cadeias de caracteres de formato personalizado que criam


formato numérico formatos específicos de aplicativo para valores numéricos.
personalizado

Cadeias de caracteres de Descreve cadeias de caracteres de formato padrão que criam


formato de data e hora representações de cadeia de caracteres de valores DateTime e
padrão DateTimeOffset frequentemente usadas.

Cadeias de caracteres de Descreve cadeias de caracteres de formato personalizado que criam


formato de data e hora formatos específicos de aplicativo para valores DateTime e
personalizado DateTimeOffset.

Cadeias de caracteres de Descreve cadeias de caracteres de formato padrão que criam


formato TimeSpan representações de intervalos de tempo frequentemente usadas.
padrão

Cadeias de caracteres de Descreve cadeias de caracteres de formato personalizado que criam


formato TimeSpan formatos específicos de aplicativo para intervalos de tempo.
personalizado

Cadeias de Caracteres de Descreve cadeias de caracteres de formato padrão que são usadas
Formato de Enumeração para criar representações de cadeia de caracteres de valores de
enumeração.
Título Definição

Guid.ToString(String) Descreve cadeias de caracteres de formato padrão para valores Guid.

Formatação que faz distinção entre culturas


com provedores de formato
Embora os especificadores de formato permitam personalizar a formatação de objetos,
a produção de uma representação de cadeia de caracteres de objetos significativa
geralmente requer informações de formatação adicionais. Por exemplo, formatar um
número como um valor de moeda usando a cadeia de caracteres de formato padrão "C"
ou então uma cadeia de caracteres de formato personalizado como "$ #,#.00" requer
que, no mínimo, informações sobre o símbolo correto da moeda, o separador de grupo
e o separador decimal estejam disponíveis para inclusão na cadeia de caracteres
formatada. No .NET, essas informações de formatação adicionais são disponibilizadas
por meio da interface IFormatProvider, que é fornecida como um parâmetro para um ou
mais sobrecargas do método ToString de tipos numéricos e tipos de data e hora.
Implementações IFormatProvider são usadas em .NET para dar suporte à formatação
específica à cultura. O exemplo a seguir ilustra como a representação de cadeia de
caracteres de um objeto é alterado quando ele é formatado com três objetos
IFormatProvider que representam diferentes culturas.

C#

using System;
using System.Globalization;

public class Example18


{
public static void Main()
{
decimal value = 1603.42m;
Console.WriteLine(value.ToString("C3", new CultureInfo("en-US")));
Console.WriteLine(value.ToString("C3", new CultureInfo("fr-FR")));
Console.WriteLine(value.ToString("C3", new CultureInfo("de-DE")));
}
}
// The example displays the following output:
// $1,603.420
// 1 603,420 €
// 1.603,420 €

A interface IFormatProvider inclui um método, GetFormat(Type), que tem um único


parâmetro que especifica o tipo de objeto que fornece informações de formatação. Se o
método puder fornecer um objeto desse tipo, ele retornará esse objeto. Caso contrário,
ele retornará uma referência nula ( Nothing em Visual Basic).

IFormatProvider.GetFormat é um método de retorno de chamada. Quando você chama


uma sobrecarga do método ToString que inclui um parâmetro IFormatProvider, ela
chama o método GetFormat daquele objeto IFormatProvider. O método GetFormat é
responsável por retornar um objeto que fornece as informações de formatação
necessárias, conforme especificadas pelo seu parâmetro formatType para o método
ToString .

Um número de métodos de conversão de cadeia de caracteres ou formatação inclui um


parâmetro de tipo IFormatProvider, mas em muitos casos o valor do parâmetro é
ignorado quando o método é chamado. A tabela a seguir lista alguns dos métodos de
formatação que usam o parâmetro e o tipo do objeto Type que passam para o método
IFormatProvider.GetFormat.

Método Tipo de parâmetro formatType

ToString método de tipos numéricos System.Globalization.NumberFormatInfo

ToString método de tipos de data e hora System.Globalization.DateTimeFormatInfo

String.Format System.ICustomFormatter

StringBuilder.AppendFormat System.ICustomFormatter

7 Observação

Os métodos ToString dos tipos numéricos e dos tipos de data e hora estão
sobrecarregados, e somente algumas das sobrecargas incluem um parâmetro
IFormatProvider. Se um método não tiver um parâmetro do tipo IFormatProvider,
o objeto retornado pela propriedade CultureInfo.CurrentCulture será passado. Por
exemplo, uma chamada para o método Int32.ToString() padrão acaba resultando
em uma chamada de método como esta: Int32.ToString("G",
System.Globalization.CultureInfo.CurrentCulture) .

O .NET fornece três classes que implementam IFormatProvider:

DateTimeFormatInfo, uma classe que fornece informações de formatação para


valores de data e hora para uma cultura específica. Sua implementação de
IFormatProvider.GetFormat retorna uma instância de si mesma.
NumberFormatInfo, uma classe que fornece informações de formatação numérica
para uma cultura específica. Sua implementação de IFormatProvider.GetFormat
retorna uma instância de si mesma.

CultureInfo. Sua implementação de IFormatProvider.GetFormat pode retornar um


objeto DateTimeFormatInfo para fornecer informações de formatação numérica ou
um objeto NumberFormatInfo para fornecer informações de formatação para
valores de data e hora.

Você também pode implementar seu próprio provedor de formato para substituir
qualquer uma dessas classes. No entanto, o método GetFormat da implementação deve
retornar um objeto do tipo listado na tabela anterior se ele precisar fornecer
informações para o método ToString .

Formatação de valores numéricos que leva em conta a


cultura
Por padrão, a formatação de valores numéricos leva em conta a cultura. Se você não
especificar uma cultura quando chamar um método de formatação, as convenções de
formatação da cultura atual serão usadas. Isso é ilustrado no exemplo a seguir, que
altera a cultura atual quatro vezes e, em seguida, chama o método
Decimal.ToString(String). Em cada caso, a cadeia de caracteres de resultado reflete as
convenções de formatação da cultura atual. Isso ocorre porque os métodos ToString e
ToString(String) encapsulam chamadas para o método ToString(String,
IFormatProvider) de cada tipo numérico.

C#

using System.Globalization;

public class Example6


{
public static void Main()
{
string[] cultureNames = { "en-US", "fr-FR", "es-MX", "de-DE" };
Decimal value = 1043.17m;

foreach (var cultureName in cultureNames) {


// Change the current culture.
CultureInfo.CurrentCulture =
CultureInfo.CreateSpecificCulture(cultureName);
Console.WriteLine($"The current culture is
{CultureInfo.CurrentCulture.Name}");
Console.WriteLine(value.ToString("C2"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// The current culture is en-US
// $1,043.17
//
// The current culture is fr-FR
// 1 043,17 €
//
// The current culture is es-MX
// $1,043.17
//
// The current culture is de-DE
// 1.043,17 €

Você também pode formatar um valor numérico para uma cultura específica chamando
uma sobrecarga ToString que tenha um parâmetro de provider e passando-o a um
dos dois elementos a seguir:

Um objeto CultureInfo que representa a cultura cujas convenções de formatação


devem ser usadas. Seu método CultureInfo.GetFormat retorna o valor da
propriedade CultureInfo.NumberFormat, que é o objeto NumberFormatInfo que
fornece informações de formatação específicas da cultura para valores numéricos.

Um objeto NumberFormatInfo que define as convenções de formatação


específicas da cultura a serem usadas. Seu método GetFormat retorna uma
instância de si mesmo.

O exemplo a seguir usa objetos NumberFormatInfo que representam as culturas


americana (Estados Unidos) e britânica (Reino Unido), além das culturas neutras francesa
e russa, para formatar um número de ponto flutuante.

C#

using System.Globalization;

public class Example7


{
public static void Main()
{
double value = 1043.62957;
string[] cultureNames = { "en-US", "en-GB", "ru", "fr" };

foreach (string? name in cultureNames)


{
NumberFormatInfo nfi =
CultureInfo.CreateSpecificCulture(name).NumberFormat;
Console.WriteLine("{0,-6} {1}", name + ":", value.ToString("N3",
nfi));
}
}
}
// The example displays the following output:
// en-US: 1,043.630
// en-GB: 1,043.630
// ru: 1 043,630
// fr: 1 043,630

Formatação de valores de data e hora que leva em conta


a cultura
Por padrão, a formatação de valores de data e hora leva em conta a cultura. Se você não
especificar uma cultura quando chamar um método de formatação, as convenções de
formatação da cultura atual serão usadas. Isso é ilustrado no exemplo a seguir, que
altera a cultura atual quatro vezes e, em seguida, chama o método
DateTime.ToString(String). Em cada caso, a cadeia de caracteres de resultado reflete as
convenções de formatação da cultura atual. Isso ocorre porque os métodos
DateTime.ToString(), DateTime.ToString(String), DateTimeOffset.ToString() e
DateTimeOffset.ToString(String) encapsulam chamadas para os métodos
DateTime.ToString(String, IFormatProvider) e DateTimeOffset.ToString(String,
IFormatProvider).

C#

using System.Globalization;

public class Example4


{
public static void Main()
{
string[] cultureNames = { "en-US", "fr-FR", "es-MX", "de-DE" };
DateTime dateToFormat = new DateTime(2012, 5, 28, 11, 30, 0);

foreach (var cultureName in cultureNames) {


// Change the current culture.
CultureInfo.CurrentCulture =
CultureInfo.CreateSpecificCulture(cultureName);
Console.WriteLine($"The current culture is
{CultureInfo.CurrentCulture.Name}");
Console.WriteLine(dateToFormat.ToString("F"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// The current culture is en-US
// Monday, May 28, 2012 11:30:00 AM
//
// The current culture is fr-FR
// lundi 28 mai 2012 11:30:00
//
// The current culture is es-MX
// lunes, 28 de mayo de 2012 11:30:00 a.m.
//
// The current culture is de-DE
// Montag, 28. Mai 2012 11:30:00

Você também pode formatar um valor de data e hora para uma cultura específica
chamando uma sobrecarga DateTime.ToString ou DateTimeOffset.ToString que tenha
um parâmetro provider e passando-o a um dos dois elementos a seguir:

Um objeto CultureInfo que representa a cultura cujas convenções de formatação


devem ser usadas. Seu método CultureInfo.GetFormat retorna o valor da
propriedade CultureInfo.DateTimeFormat, que é o objeto DateTimeFormatInfo que
fornece informações de formatação específicas da cultura para valores de data e
hora.

Um objeto DateTimeFormatInfo que define as convenções de formatação


específicas da cultura a serem usadas. Seu método GetFormat retorna uma
instância de si mesmo.

O exemplo a seguir usa objetos DateTimeFormatInfo que representam as culturas


americana (Estados Unidos) e britânica (Reino Unido), além das culturas neutras francesa
e russa para formatar uma data.

C#

using System.Globalization;

public class Example5


{
public static void Main()
{
DateTime dat1 = new(2012, 5, 28, 11, 30, 0);
string[] cultureNames = { "en-US", "en-GB", "ru", "fr" };

foreach (var name in cultureNames) {


DateTimeFormatInfo dtfi =
CultureInfo.CreateSpecificCulture(name).DateTimeFormat;
Console.WriteLine($"{name}: {dat1.ToString(dtfi)}");
}
}
}
// The example displays the following output:
// en-US: 5/28/2012 11:30:00 AM
// en-GB: 28/05/2012 11:30:00
// ru: 28.05.2012 11:30:00
// fr: 28/05/2012 11:30:00

A interface IFormattable
Normalmente, os tipos que sobrecarregam o método ToString com uma cadeia de
caracteres de formato e um parâmetro IFormatProvider também implementam a
interface IFormattable. Essa interface tem um único membro,
IFormattable.ToString(String, IFormatProvider), que inclui como parâmetros uma cadeia
de caracteres de formato e um provedor de formato.

Implementar a interface IFormattable para a classe definida pelo aplicativo oferece duas
vantagens:

Suporte para conversão de cadeia de caracteres pela classe Convert. As chamadas


para os métodos Convert.ToString(Object) e Convert.ToString(Object,
IFormatProvider) chamam sua implementação IFormattable automaticamente.

Suporte à formatação composição. Se um item de formato que inclui uma cadeia


de caracteres de formato for usado para formatar seu tipo personalizado, o
Common Language Runtime chamará automaticamente a implementação
IFormattable e passará a ele a cadeia de caracteres de formato. Para obter mais
informações sobre formatação de composição com métodos como String.Format
ou Console.WriteLine, veja a seção Formatação de composição.

O exemplo a seguir define uma classe Temperature que implementa a interface


IFormattable. Ela dá suporte aos especificadores de formato "C" ou "G" para exibir a
temperatura em graus Celsius, o especificador de formato "F" para exibir a temperatura
em Fahrenheit e o especificador de formato "K" para exibir a temperatura em Kelvin.

C#

using System;
using System.Globalization;

namespace HotAndCold
{

public class Temperature : IFormattable


{
private decimal m_Temp;

public Temperature(decimal temperature)


{
this.m_Temp = temperature;
}

public decimal Celsius


{
get { return this.m_Temp; }
}

public decimal Kelvin


{
get { return this.m_Temp + 273.15m; }
}

public decimal Fahrenheit


{
get { return Math.Round((decimal)this.m_Temp * 9 / 5 + 32, 2); }
}

public override string ToString()


{
return this.ToString("G", null);
}

public string ToString(string format)


{
return this.ToString(format, null);
}

public string ToString(string format, IFormatProvider provider)


{
// Handle null or empty arguments.
if (String.IsNullOrEmpty(format))
format = "G";
// Remove any white space and covert to uppercase.
format = format.Trim().ToUpperInvariant();

if (provider == null)
provider = NumberFormatInfo.CurrentInfo;

switch (format)
{
// Convert temperature to Fahrenheit and return string.
case "F":
return this.Fahrenheit.ToString("N2", provider) + "°F";
// Convert temperature to Kelvin and return string.
case "K":
return this.Kelvin.ToString("N2", provider) + "K";
// Return temperature in Celsius.
case "C":
case "G":
return this.Celsius.ToString("N2", provider) + "°C";
default:
throw new FormatException(String.Format("The '{0}'
format string is not supported.", format));
}
}
}

O exemplo a seguir instancia um objeto Temperature . Depois, ele chama o método


ToString e usa várias cadeias de caracteres de formato de composição para obter
diferentes representações de cadeia de caracteres de um objeto Temperature . Cada uma
dessas chamadas de método, por sua vez, chama a implementação IFormattable da
classe Temperature .

C#

public class Example11


{
public static void Main()
{
CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("en-US");
Temperature temp = new Temperature(22m);
Console.WriteLine(Convert.ToString(temp, new CultureInfo("ja-JP")));
Console.WriteLine("Temperature: {0:K}", temp);
Console.WriteLine("Temperature: {0:F}", temp);
Console.WriteLine(String.Format(new CultureInfo("fr-FR"),
"Temperature: {0:F}", temp));
}
}
// The example displays the following output:
// 22.00°C
// Temperature: 295.15K
// Temperature: 71.60°F
// Temperature: 71,60°F

Formatação de composição
Alguns métodos, tais como String.Format e StringBuilder.AppendFormat, dão suporte à
formatação de composição. Uma cadeia de caracteres de formato de composição é um
tipo de modelo que retorna uma única cadeia de caracteres que incorpora a
representação de cadeia de caracteres de zero, um ou mais objetos. Cada objeto é
representado na cadeia de caracteres de formato de composição por um item de
formato indexado. O índice do item de formato corresponde à posição do objeto que
ele representa na lista de parâmetros do método. Os índices são baseados em zero. Por
exemplo, na seguinte chamada para o método String.Format, o primeiro item de
formato, {0:D} , é substituído pela representação de cadeia de caracteres de thatDate ; o
segundo item de formato, {1} , é substituído pela representação de cadeia de caracteres
de item1 e, por fim, o terceiro item de formato, {2:C2} , é substituído pela
representação de cadeia de caracteres de item1.Value .
C#

result = String.Format("On {0:d}, the inventory of {1} was worth {2:C2}.",


thatDate, item1, item1.Value);
Console.WriteLine(result);
// The example displays output like the following if run on a system
// whose current culture is en-US:
// On 5/1/2009, the inventory of WidgetA was worth $107.44.

Além de substituir um item de formato pela representação de cadeia de caracteres de


seu objeto correspondente, os itens de formato também permitem que você controle o
seguinte:

O modo específico em que um objeto é representado como uma cadeia de


caracteres, se o objeto implementa a interface IFormattable e se dá suporte a
cadeias de caracteres de formato. Você pode fazer isso seguindo o índice do item
de formato com um : (dois-pontos) seguido por uma cadeia de caracteres de
formato válido. O exemplo anterior já fez isso ao formatar um valor de data com a
cadeia de caracteres de formato (por exemplo, {0:d} ) "d" (padrão de data
abreviada) e formatando um valor numérico com a cadeia de caracteres de
formato "C2" (por exemplo, {2:C2} ) para representar o número como um valor de
moeda com dois dígitos decimais fracionários.

A largura do campo que contém a representação de cadeia de caracteres do


objeto e o alinhamento da representação de cadeia de caracteres nesse campo.
Você pode fazer isso seguindo o índice do item de formato com uma , (vírgula)
seguida da largura do campo. A cadeia de caracteres será alinhada à direita no
campo se a largura do campo for um valor positivo ou à esquerda se esse valor for
negativo. O exemplo a seguir alinha os valores de data à esquerda em um campo
de 20 caracteres e alinha valores decimais com um dígito fracionário à direita em
um campo de 11 caracteres.

C#

DateTime startDate = new DateTime(2015, 8, 28, 6, 0, 0);


decimal[] temps = { 73.452m, 68.98m, 72.6m, 69.24563m,
74.1m, 72.156m, 72.228m };
Console.WriteLine("{0,-20} {1,11}\n", "Date", "Temperature");
for (int ctr = 0; ctr < temps.Length; ctr++)
Console.WriteLine("{0,-20:g} {1,11:N1}", startDate.AddDays(ctr),
temps[ctr]);

// The example displays the following output:


// Date Temperature
//
// 8/28/2015 6:00 AM 73.5
// 8/29/2015 6:00 AM 69.0
// 8/30/2015 6:00 AM 72.6
// 8/31/2015 6:00 AM 69.2
// 9/1/2015 6:00 AM 74.1
// 9/2/2015 6:00 AM 72.2
// 9/3/2015 6:00 AM 72.2

Observe que, se o componente de cadeia de caracteres de alinhamento e o


componente de cadeia de caracteres de formato estiverem presentes, o primeiro
precederá o último (por exemplo, {0,-20:g} .

Para obter mais informações sobre formatação de composição, veja Formatação de


composição.

Formatação personalizada com


ICustomFormatter
Dois métodos de formatação de composição String.Format(IFormatProvider, String,
Object[]) e StringBuilder.AppendFormat(IFormatProvider, String, Object[]), incluem um
parâmetro de provedor de formato que dá suporte à formatação personalizada. Quando
um desses métodos de formatação é chamado, ele passa um objeto Type que
representa uma interface ICustomFormatter para o método GetFormat do provedor de
formato. O método GetFormat, em seguida, será responsável por retornar a
implementação ICustomFormatter que oferece formatação personalizada.

A interface ICustomFormatter tem um único método, Format(String, Object,


IFormatProvider), que é chamado automaticamente por um método de formatação de
composição, uma vez para cada item de formato em uma cadeia de caracteres de
formato de composição. O método Format(String, Object, IFormatProvider) tem três
parâmetros: uma cadeia de caracteres de formato, que representa o argumento
formatString em um item de formato, um objeto a ser formatado e um objeto

IFormatProvider que oferece serviços de formatação. Normalmente, a classe que


implementa ICustomFormatter também implementa IFormatProvider, portanto este
último parâmetro é uma referência para a própria classe de formatação personalizada. O
método retorna uma representação de cadeia de caracteres formatada personalizada do
objeto a ser formatado. Se o método não for capaz de formatar o objeto, ele deverá
retornar uma referência nula ( Nothing em Visual Basic).

O exemplo a seguir fornece uma implementação ICustomFormatter chamada


ByteByByteFormatter que exibe valores inteiros como uma sequência de valores
hexadecimais de dois dígitos seguidos por um espaço.
C#

public class ByteByByteFormatter : IFormatProvider, ICustomFormatter


{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(string format, object arg,


IFormatProvider formatProvider)
{
if (! formatProvider.Equals(this)) return null;

// Handle only hexadecimal format string.


if (! format.StartsWith("X")) return null;

byte[] bytes;
string output = null;

// Handle only integral types.


if (arg is Byte)
bytes = BitConverter.GetBytes((Byte) arg);
else if (arg is Int16)
bytes = BitConverter.GetBytes((Int16) arg);
else if (arg is Int32)
bytes = BitConverter.GetBytes((Int32) arg);
else if (arg is Int64)
bytes = BitConverter.GetBytes((Int64) arg);
else if (arg is SByte)
bytes = BitConverter.GetBytes((SByte) arg);
else if (arg is UInt16)
bytes = BitConverter.GetBytes((UInt16) arg);
else if (arg is UInt32)
bytes = BitConverter.GetBytes((UInt32) arg);
else if (arg is UInt64)
bytes = BitConverter.GetBytes((UInt64) arg);
else
return null;

for (int ctr = bytes.Length - 1; ctr >= 0; ctr--)


output += String.Format("{0:X2} ", bytes[ctr]);

return output.Trim();
}
}

O exemplo a seguir usa a classe ByteByByteFormatter para formatar valores inteiros.


Observe que o método ICustomFormatter.Format é chamado mais de uma vez na
segunda chamada de método String.Format(IFormatProvider, String, Object[]) e que o
provedor padrão NumberFormatInfo é usado na terceira chamada do método porque o
método . O método ByteByByteFormatter.Format não reconhece a cadeia de caracteres
de formato "N0" e retorna uma referência nula ( Nothing no Visual Basic).

C#

public class Example10


{
public static void Main()
{
long value = 3210662321;
byte value1 = 214;
byte value2 = 19;

Console.WriteLine(String.Format(new ByteByByteFormatter(), "{0:X}",


value));
Console.WriteLine(String.Format(new ByteByByteFormatter(), "{0:X} And
{1:X} = {2:X} ({2:000})",
value1, value2, value1 & value2));
Console.WriteLine(String.Format(new ByteByByteFormatter(), "
{0,10:N0}", value));
}
}
// The example displays the following output:
// 00 00 00 00 BF 5E D1 B1
// 00 D6 And 00 13 = 00 12 (018)
// 3,210,662,321

Confira também
Título Definição

Cadeias de Caracteres Descreve cadeias de caracteres de formato padrão que criam


de Formato Numérico representações de cadeia de caracteres de valores numéricos
Padrão frequentemente usadas.

Cadeias de caracteres Descreve cadeias de caracteres de formato personalizado que criam


de formato numérico formatos específicos de aplicativo para valores numéricos.
personalizado

Cadeias de caracteres Descreve cadeias de caracteres de formato padrão que criam


de formato de data e representações de cadeia de caracteres de valores DateTime
hora padrão frequentemente usadas.

Cadeias de caracteres Descreve cadeias de caracteres de formato personalizado que criam


de formato de data e formatos específicos de aplicativo para valores DateTime.
hora personalizado
Título Definição

Cadeias de caracteres Descreve cadeias de caracteres de formato padrão que criam


de formato TimeSpan representações de intervalos de tempo frequentemente usadas.
padrão

Cadeias de caracteres Descreve cadeias de caracteres de formato personalizado que criam


de formato TimeSpan formatos específicos de aplicativo para intervalos de tempo.
personalizado

Cadeias de Caracteres Descreve cadeias de caracteres de formato padrão que são usadas para
de Formato de criar representações de cadeia de caracteres de valores de
Enumeração enumeração.

Formatação de Descreve como inserir um ou mais valores formatados em uma cadeia


composição de caracteres. A cadeia de caracteres pode posteriormente ser exibida
no console ou gravada em um fluxo.

Analisando cadeias de Descreve como inicializar objetos para os valores descritos pelas
caracteres representações de cadeia de caracteres desses objetos. A análise é a
operação inversa da formatação.

Referência
System.IFormattable
System.IFormatProvider
System.ICustomFormatter
Cadeias de caracteres de formato
numérico padrão
Artigo • 05/06/2023

As cadeias de caracteres de formato numérico padrão são usadas para formatar tipos
numéricos comuns. Uma cadeia de caracteres de formato numérico padrão assume o
formato [format specifier][precision specifier] , em que:

O especificador de formato é um só caractere alfabético que especifica o tipo de


formato de número, por exemplo, moeda ou percentual. Qualquer cadeia de
caracteres de formato numérico que contém mais de um caractere alfabético,
incluindo espaços em branco, é interpretada como uma cadeia de caracteres de
formato numérico personalizado. Para mais informações, confira Cadeias de
caracteres de formato numérico personalizado.

O especificador de precisão é um inteiro opcional que afeta o número de dígitos na


cadeia de caracteres resultante. No .NET 7 e versões posteriores, o valor máximo
de precisão é 999.999.999. No .NET 6, o valor máximo de precisão é
Int32.MaxValue. Nas versões anteriores do .NET, a precisão pode variar de 0 a 99.
O especificador de precisão controla o número de dígitos na representação de
cadeia de caracteres de um número. Ele não arredonda o número em si. Para
executar uma operação de arredondamento, use o método Math.Ceiling,
Math.Floor ou Math.Round.

Quando o especificador de precisão controla o número de dígitos fracionários na


cadeia de caracteres de resultado, ela reflete um número que será arredondado
para um resultado representável mais próximo do resultado infinitamente preciso.
Se houver dois resultados representáveis igualmente próximos:
No .NET Framework e .NET Core até o .NET Core 2.0, o runtime selecionará o
resultado com o dígito menos significativo maior (ou seja, usando
MidpointRounding.AwayFromZero).
No .NET Core 2.1 e versões posteriores, o runtime selecionará o resultado com
um dígito até menos significativo (ou seja, usando MidpointRounding.ToEven).

7 Observação

O especificador de precisão determina o número de dígitos na cadeia de


caracteres de resultado. Para acrescentar espaços à direita ou à esquerda em
uma cadeia de caracteres de resultado, use o recurso formatação de
composição e defina um componente de alinhamento no item de formato.
As cadeias de caractere de formato numérico padrão têm suporte de:

Algumas sobrecargas do método ToString de todos os tipos numéricos. Por


exemplo, você pode fornecer uma cadeia de caracteres de formato numérico para
os métodos Int32.ToString(String) e Int32.ToString(String, IFormatProvider).

O método TryFormat de todos os tipos numéricos, por exemplo,


Int32.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider) e
Single.TryFormat(Span<Char>, Int32, ReadOnlySpan<Char>, IFormatProvider).

O recurso de formatação composta do .NET, o qual é usado por alguns métodos


Write e WriteLine das classes Console e StreamWriter, pelo método String.Format

e pelo método StringBuilder.AppendFormat. O recurso de formato de composição


permite a inclusão da representação da cadeia de caracteres de vários itens de
dados em uma única cadeia de caracteres, para especificar a largura do campo e
alinhar os números em um campo. Para obter mais informações, veja Formatação
de composição.

Cadeias de caracteres interpoladas em C# e Visual Basic, que fornecem uma


sintaxe simplificada quando comparada a cadeias de caracteres de formato
composto.

 Dica

Baixe o Utilitário de Formatação, um aplicativo do Windows Forms do .NET Core


que permite aplicar cadeias de caracteres de formato a valores numéricos ou de
data e hora e exibir a cadeia de caracteres de resultado. O código-fonte está
disponível para o C# e o Visual Basic.

Especificadores de formato padrão


A tabela a seguir descreve os especificadores de formato numérico padrão e exibe
exemplos de saídas produzidas por cada especificador de formato. Confira a seção
Notas para obter informações adicionais sobre como usar cadeias de caracteres de
formato numérico padrão e a seção Exemplo de código para obter uma ilustração
abrangente de seu uso.

O resultado de uma cadeia de caracteres formatada para uma cultura específica


pode ser diferente dos exemplos a seguir. As configurações do sistema operacional,
as configurações do usuário, as variáveis de ambiente e a versão do .NET que você
está usando podem afetar o formato. Por exemplo, começando com o .NET 5, o
.NET tenta unificar formatos culturais entre plataformas. Para mais informações,
confira Globalização do .NET e ICU.

Especificador Nome Descrição Exemplos


de formato

"B" ou "b" Binário Resultado: uma cadeia de caracteres 42 ("B")


binária. -> 101010

Compatível com: somente tipos integrais 255 ("b16")


(.NET 8+). -> 0000000011111111

Especificador de precisão: número de


dígitos na cadeia de caracteres de
resultado.

Mais informações: Especificador de


formato binário ("B").

"C" ou "c" Moeda Resultado: um valor de moeda. 123.456 ("C", en-US)


-> $123.46
Compatível com: todos os tipos
numéricos. 123.456 ("C", fr-FR)
-> 123,46 €
Especificador de precisão: número de
casas decimais. 123.456 ("C", ja-JP)
-> ¥123
Especificador de precisão padrão: definido
por -123.456 ("C3", en-US)
NumberFormatInfo.CurrencyDecimalDigits. -> ($123.456)

Para saber mais: especificador de formato -123.456 ("C3", fr-FR)


de moeda ("C"). -> -123,456 €

-123.456 ("C3", ja-JP)


-> -¥123.456
Especificador Nome Descrição Exemplos
de formato

"D" ou "d" Decimal Resultado: dígitos inteiros com sinal 1234 ("D")
negativo opcional. -> 1234

Compatível com: somente tipos integrais. -1234 ("D6")


-> -001234
Especificador de precisão: número mínimo
de dígitos.

Especificador de precisão padrão: número


mínimo de dígitos necessários.

Para saber mais: especificador de formato


decimal ("D").

"E" ou "e" Exponencial Resultado: notação Exponencial. 1052.0329112756 ("E",


(científica) en-US)
Compatível com: todos os tipos -> 1.052033E+003
numéricos.
1052.0329112756 ("e",
Especificador de precisão: número de fr-FR)
casas decimais. -> 1,052033e+003

Especificador de precisão padrão: 6. -1052.0329112756


("e2", en-US)
Para saber mais: especificador de formato -> -1.05e+003
exponencial ("E").
-1052.0329112756
("E2", fr-FR)
-> -1,05E+003
Especificador Nome Descrição Exemplos
de formato

"F" ou "f" Ponto fixo Resultado: dígitos integrais e decimais 1234.567 ("F", en-US)
com sinal negativo opcional. -> 1234.57

Compatível com: todos os tipos 1234.567 ("F", de-DE)


numéricos. -> 1234,57

Especificador de precisão: número de 1234 ("F1", en-US)


casas decimais. -> 1234.0

Especificador de precisão padrão: definido 1234 ("F1", de-DE)


por -> 1234,0
NumberFormatInfo.NumberDecimalDigits.
-1234.56 ("F4", en-US)
Para saber mais: especificador de formato -> -1234.5600
de ponto fixo ("F").
-1234.56 ("F4", de-DE)
-> -1234,5600

"G" ou "g" Geral Resultado: a mais compacta entre notação -123.456 ("G", en-US)
de ponto fixo ou científica. -> -123.456

Compatível com: todos os tipos -123.456 ("G", sv-SE)


numéricos. -> -123,456

Especificador de precisão: número de 123.4546 ("G4", en-US)


dígitos significativos. -> 123.5

Especificador de precisão padrão: depende 123.4546 ("G4", sv-SE)


do tipo numérico. -> 123,5

Para saber mais: especificador de formato -1.234567890e-25 ("G",


geral ("G"). en-US)
-> -1.23456789E-25

-1.234567890e-25 ("G",
sv-SE)
-> -1,23456789E-25
Especificador Nome Descrição Exemplos
de formato

"N" ou "n" Número Resultado: dígitos integrais e decimais, 1234.567 ("N", en-US)
separadores de grupo e um separador -> 1,234.57
decimal com sinal negativo opcional.
1234.567 ("N", ru-RU)
Compatível com: todos os tipos -> 1 234,57
numéricos.
1234 ("N1", en-US)
Especificador de precisão: número de -> 1,234.0
casas decimais desejadas.
1234 ("N1", ru-RU)
Especificador de precisão padrão: definido -> 1 234,0
por
NumberFormatInfo.NumberDecimalDigits. -1234.56 ("N3", en-US)
-> -1,234.560
Para saber mais: especificador de formato
numérico ("N"). -1234.56 ("N3", ru-RU)
-> -1 234,560

"P" ou "p" Porcentagem Resultado: número multiplicado por 100 e 1 ("P", en-US)
exibido com um sinal de porcentagem. -> 100.00 %

Compatível com: todos os tipos 1 ("P", fr-FR)


numéricos. -> 100,00 %

Especificador de precisão: número de -0.39678 ("P1", en-US)


casas decimais desejadas. -> -39.7 %

Especificador de precisão padrão: definido -0.39678 ("P1", fr-FR)


por -> -39,7 %
NumberFormatInfo.PercentDecimalDigits.

Para saber mais: especificador de formato


de porcentagem ("P").
Especificador Nome Descrição Exemplos
de formato

"R" ou "r" Ida e volta Resultado: uma cadeia de caracteres que 123456789.12345678
pode ir e voltar para um número idêntico. ("R")
->
Compatível com: Single, Double e 123456789.12345678
BigInteger.
-1234567890.12345678
Observação: recomendado apenas para o ("R")
tipo BigInteger. Para os tipos Double, use ->
"G17"; para os tipos Single, use "G9". -1234567890.1234567
Especificador de precisão: ignorado.

Para saber mais: especificador de formato


de ida e volta ("R").

"X" ou "x" Hexadecimal Resultado: uma cadeia de caracteres 255 ("X")


hexadecimal. -> FF

Compatível com: somente tipos integrais. -1 ("x")


-> ff
Especificador de precisão: número de
dígitos na cadeia de caracteres de 255 ("x4")
resultado. -> 00ff

Para saber mais: especificador de formato -1 ("X4")


hexadecimal ("X"). -> 00FF

Qualquer Especificador Resultado: gera uma FormatException em


outro desconhecido tempo de execução.
caractere
único

Usar cadeias de caracteres de formato


numérico padrão

7 Observação

Os exemplos de C# neste artigo são executados no executador de código


embutido Try.NET e no playground. Clique no botão Executar para executar um
exemplo em uma janela interativa. Ao executar o código, é possível modificá-lo e
executar o código modificado clicando em Executar novamente. O código
modificado será executado na janela interativa ou, se a compilação falhar, a janela
interativa exibirá todos as mensagens de erro do compilador C#.
Uma cadeia de caracteres de formato numérico padrão pode ser usada para definir a
formatação de um valor numérico em uma de seguintes formas:

Ele pode ser passado para o método TryFormat ou uma sobrecarga do método
ToString que tem um parâmetro format . O exemplo a seguir formata um valor
numérico como uma cadeia de caracteres de moeda na cultura atual (nesse caso, a
cultura en-US).

C#

decimal value = 123.456m;


Console.WriteLine(value.ToString("C2"));
// Displays $123.46

Ele pode ser fornecido como o argumento formatString em um item de formato


usado com métodos como String.Format, Console.WriteLine e
StringBuilder.AppendFormat. Para obter mais informações, veja Formatação de
composição. O exemplo a seguir usa um item de formato para inserir um valor de
moeda em uma cadeia de caracteres.

C#

decimal value = 123.456m;


Console.WriteLine("Your account balance is {0:C2}.", value);
// Displays "Your account balance is $123.46."

Opcionalmente, você pode fornecer um argumento alignment para especificar a


largura do campo numérico e se o valor é alinhado à direita ou à esquerda. O
exemplo a seguir alinha à esquerda um valor de moeda em um campo de 28
caracteres e alinha à direita um valor de moeda em um campo de 14 caracteres.

C#

decimal[] amounts = { 16305.32m, 18794.16m };


Console.WriteLine(" Beginning Balance Ending Balance");
Console.WriteLine(" {0,-28:C2}{1,14:C2}", amounts[0], amounts[1]);
// Displays:
// Beginning Balance Ending Balance
// $16,305.32 $18,794.16

Ele pode ser fornecido como o argumento formatString em um item de expressão


interpolada de uma cadeia de caracteres interpolada. Para mais informações,
confira o artigo Interpolação de cadeia de caracteres na referência C# ou o artigo
Cadeias de caracteres interpoladas na referência do Visual Basic.

As seções a seguir fornecem informações detalhadas sobre cada uma das cadeias de
caracteres de formato numérico padrão.

Especificador de formato binário (B)


O especificador de formato binário ("B") converte um número em uma cadeia de
caracteres de dígitos binários. Esse formato é compatível apenas com tipos integrais e
somente no .NET 8+.

O especificador de precisão indica o número mínimo de dígitos desejados na cadeia de


caracteres resultante. Se necessário, o número é preenchido com zeros à esquerda para
produzir o número de dígitos fornecido pelo especificador de precisão.

A cadeia de caracteres do resultado não é afetada pelas informações de formatação do


objeto NumberFormatInfo atual.

Especificador de formato de moeda (C)


O especificador de formato "C" (ou moeda) converte um número em uma cadeia de
caracteres que representa um valor de moeda. O especificador de precisão indica o
número desejado de casas decimais na cadeia de caracteres resultante. Se o
especificador de precisão for omitido, a precisão padrão será definida pela propriedade
NumberFormatInfo.CurrencyDecimalDigits.

Se o valor a ser formatado tem mais do que o número especificado ou padrão de casas
decimais, o valor fracionário é arredondado na cadeia de caracteres de resultado. Se o
valor à direita do número de casas decimais especificadas for 5 ou mais, o último dígito
da cadeia de caracteres do resultado será arredondado para cima.

A cadeia de caracteres de resultado é afetada pelas informações de formatação do


objeto NumberFormatInfo atual. A tabela a seguir lista as propriedades
NumberFormatInfo que controlam a formatação da cadeia de caracteres retornada.

Propriedade Descrição
NumberFormatInfo

CurrencyPositivePattern Define o posicionamento do símbolo de moeda para valores


positivos.
Propriedade Descrição
NumberFormatInfo

CurrencyNegativePattern Define o posicionamento do símbolo da moeda para valores


negativos e especifica se o sinal de negativo é representado por
parênteses ou pela propriedade NegativeSign.

NegativeSign Define o sinal de negativo usado se CurrencyNegativePattern indicar


que parênteses não são usados.

CurrencySymbol Define o símbolo de moeda.

CurrencyDecimalDigits Define o número padrão de dígitos decimais em um valor de


moeda. Esse valor pode ser substituído usando-se o especificador
de precisão.

CurrencyDecimalSeparator Define a cadeia de caracteres que separa dígitos decimais e


integrais.

CurrencyGroupSeparator Define a cadeia de caracteres que separa grupos de números


integrais.

CurrencyGroupSizes Define o número de dígitos inteiros que aparecem em um grupo.

O seguinte exemplo formata um valor Double com o especificador de formato de


moeda:

C#

double value = 12345.6789;


Console.WriteLine(value.ToString("C", CultureInfo.CurrentCulture));

Console.WriteLine(value.ToString("C3", CultureInfo.CurrentCulture));

Console.WriteLine(value.ToString("C3",
CultureInfo.CreateSpecificCulture("da-DK")));
// The example displays the following output on a system whose
// current culture is English (United States):
// $12,345.68
// $12,345.679
// 12.345,679 kr

Especificador de formato decimal (D)


O especificador de formato “D” (ou decimal) converte um número em uma cadeia de
caracteres de dígitos decimais (0-9), prefixados por um sinal de negativo se o número
for negativo. Esse formato é compatível apenas com tipos integrais.
O especificador de precisão indica o número mínimo de dígitos desejados na cadeia de
caracteres resultante. Se necessário, o número é preenchido com zeros à esquerda para
produzir o número de dígitos fornecido pelo especificador de precisão. Quando
nenhum especificador de precisão é especificado, o padrão é o valor mínimo necessário
para representar o inteiro sem zeros à esquerda.

A cadeia de caracteres de resultado é afetada pelas informações de formatação do


objeto NumberFormatInfo atual. Conforme mostrado na tabela a seguir, uma única
propriedade afeta a formatação da cadeia de caracteres de resultado.

Propriedade Descrição
NumberFormatInfo

NegativeSign Define a cadeia de caracteres que indica que um número é


negativo.

O exemplo a seguir formata um valor Int32 com o especificador de formato decimal.

C#

int value;

value = 12345;
Console.WriteLine(value.ToString("D"));
// Displays 12345
Console.WriteLine(value.ToString("D8"));
// Displays 00012345

value = -12345;
Console.WriteLine(value.ToString("D"));
// Displays -12345
Console.WriteLine(value.ToString("D8"));
// Displays -00012345

Especificador de formato exponencial (E)


O especificador do formato do exponencial ("E") converte um número em uma cadeia
de caracteres no formato "-d.ddd…E+ddd" ou "-d.ddd…e+ddd", em que cada 'd' indica
um dígito (0-9). A cadeia de caracteres é iniciada com um sinal de negativo se o número
é negativo. Exatamente um dígito sempre precede o ponto decimal.

O especificador de precisão indica o número desejado de dígitos após o ponto decimal.


Quando o especificador de precisão é omitido, um padrão de seis dígitos após o ponto
decimal é usado.
A caixa do especificador de formato indica se o expoente é prefixado com um "E" ou um
"e". O expoente sempre consistem em um sinal de positivo ou negativo e um mínimo de
três dígitos. O expoente é preenchido com zeros para atender a esse mínimo, se
necessário.

A cadeia de caracteres de resultado é afetada pelas informações de formatação do


objeto NumberFormatInfo atual. A tabela a seguir lista as propriedades
NumberFormatInfo que controlam a formatação da cadeia de caracteres retornada.

Propriedade Descrição
NumberFormatInfo

NegativeSign Define a cadeia de caracteres que indica que o número é negativo


para o coeficiente e o expoente.

NumberDecimalSeparator Define a cadeia de caracteres que separa o dígito integral dos dígitos
decimais no coeficiente.

PositiveSign Define a cadeia de caracteres que indica que um expoente é positivo.

O seguinte exemplo formata um valor Double com o especificador de formato


exponencial:

C#

double value = 12345.6789;


Console.WriteLine(value.ToString("E", CultureInfo.InvariantCulture));
// Displays 1.234568E+004

Console.WriteLine(value.ToString("E10", CultureInfo.InvariantCulture));
// Displays 1.2345678900E+004

Console.WriteLine(value.ToString("e4", CultureInfo.InvariantCulture));
// Displays 1.2346e+004

Console.WriteLine(value.ToString("E",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 1,234568E+004

Especificador de formato de ponto fixo (F)


O especificador de formato de ponto fixo ("F") converte um número em uma cadeia de
caracteres no formato "-ddd.ddd…", em que cada "d" indica um dígito (0-9). A cadeia de
caracteres é iniciada com um sinal de negativo se o número é negativo.
O especificador de precisão indica o número de casas decimais desejadas. Quando o
especificador de precisão é omitido, a propriedade
NumberFormatInfo.NumberDecimalDigits atual fornece a precisão numérica.

A cadeia de caracteres de resultado é afetada pelas informações de formatação do


objeto NumberFormatInfo atual. A tabela a seguir lista as propriedades do objeto
NumberFormatInfo que controlam a formatação da cadeia de caracteres de resultado.

Propriedade Descrição
NumberFormatInfo

NegativeSign Define a cadeia de caracteres que indica que um número é negativo.

NumberDecimalSeparator Define a cadeia de caracteres que separa dígitos integrais de dígitos


decimais.

NumberDecimalDigits Define o número padrão de dígitos decimais. Esse valor pode ser
substituído usando-se o especificador de precisão.

O seguinte exemplo formata um valor Double e um valor Int32 com o especificador de


formato de ponto fixo:

C#

int integerNumber;
integerNumber = 17843;
Console.WriteLine(integerNumber.ToString("F",
CultureInfo.InvariantCulture));
// Displays 17843.00

integerNumber = -29541;
Console.WriteLine(integerNumber.ToString("F3",
CultureInfo.InvariantCulture));
// Displays -29541.000

double doubleNumber;
doubleNumber = 18934.1879;
Console.WriteLine(doubleNumber.ToString("F", CultureInfo.InvariantCulture));
// Displays 18934.19

Console.WriteLine(doubleNumber.ToString("F0",
CultureInfo.InvariantCulture));
// Displays 18934

doubleNumber = -1898300.1987;
Console.WriteLine(doubleNumber.ToString("F1",
CultureInfo.InvariantCulture));
// Displays -1898300.2

Console.WriteLine(doubleNumber.ToString("F3",
CultureInfo.CreateSpecificCulture("es-ES")));
// Displays -1898300,199

Especificador de formato geral (G)


O especificador de formato geral ("G") converte um número para a notação mais
compacta entre ponto fixo ou científica, dependendo do tipo do número e se um
especificador de precisão está presente. O especificador de precisão define o número
máximo de dígitos significativos que podem aparecer na cadeia de caracteres de
resultado. Quando o especificador de precisão é omitido ou zero, o tipo do número
determina a precisão padrão, conforme indicado na tabela a seguir.

Tipo Precisão padrão


numérico

Byte ou 3 dígitos
SByte

Int16 ou 5 dígitos
UInt16

Int32 ou 10 dígitos
UInt32

Int64 19 dígitos

UInt64 20 dígitos

BigInteger Ilimitado (igual a "R")

Half Menor número com viagem de ida e volta de dígitos para representar o número

Single Menor número com viagem de ida e volta de dígitos para representar o número (no
.NET Framework, G7 é o padrão)

Double Menor número com viagem de ida e volta de dígitos para representar o número (no
.NET Framework, G15 é o padrão)

Decimal Menor número com viagem de ida e volta de dígitos para representar o número

A notação de ponto fixo é usada se o expoente que resultaria ao expressar o número na


notação científica é maior do que -5 e menor que o especificador de precisão; caso
contrário, a notação científica é usada. Se necessário, o resultado contém um ponto
decimal, e os zeros à direita após o ponto decimal são omitidos. Se o especificador de
precisão estiver presente e o número de dígitos significativos no resultado exceder a
precisão especificada, o excesso de dígitos à direita será removido por arredondamento.
No entanto, se o número for Decimal e o especificador de precisão for omitido, a
notação de ponto fixo será usada sempre e os zeros à direita serão preservados.

Se a notação científica for usada, o expoente no resultado será prefixado com "E" se o
especificador de formato for "G" ou "e" se o especificador de formato for "g". O
expoente contém um mínimo de dois dígitos. Isso difere do formato de notação
científica que é gerado pelo especificador de formato exponencial, o qual inclui um
mínimo de três dígitos no expoente.

Quando usado com um valor Double, o especificador de formato "G17" garante que o
valor Double original faça a viagem de ida e volta com êxito. Isso ocorre porque Double
é um número de ponto flutuante de precisão dupla em conformidade com IEEE 754
2008 ( binary64 ) que oferece até 17 dígitos significativos de precisão. No .NET
Framework, recomendamos seu uso em vez do especificador de formato "R", pois em
alguns casos "R" não realiza a viagem de ida e volta dos valores de ponto flutuante de
precisão dupla.

Quando usado com um valor Single, o especificador de formato "G9" garante que o
valor Single original faça a viagem de ida e volta com êxito. Isso ocorre porque Single é
um número de ponto flutuante com precisão simples em conformidade com IEEE 754-
2008 ( binary32 ) que fornece até nove dígitos significativos de precisão. Por motivos de
desempenho, recomendamos seu uso em vez do especificador de formato "R".

A cadeia de caracteres de resultado é afetada pelas informações de formatação do


objeto NumberFormatInfo atual. A tabela a seguir lista as propriedades
NumberFormatInfo que controlam a formatação da cadeia de caracteres de resultado.

Propriedade Descrição
NumberFormatInfo

NegativeSign Define a cadeia de caracteres que indica que um número é


negativo.

NumberDecimalSeparator Define a cadeia de caracteres que separa dígitos integrais de


dígitos decimais.

PositiveSign Define a cadeia de caracteres que indica que um expoente é


positivo.

O seguinte exemplo formata valores de ponto flutuante variados com o especificador


de formato geral:

C#
double number;

number = 12345.6789;
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture));
// Displays 12345.6789
Console.WriteLine(number.ToString("G",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 12345,6789

Console.WriteLine(number.ToString("G7", CultureInfo.InvariantCulture));
// Displays 12345.68

number = .0000023;
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture));
// Displays 2.3E-06
Console.WriteLine(number.ToString("G",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 2,3E-06

number = .0023;
Console.WriteLine(number.ToString("G", CultureInfo.InvariantCulture));
// Displays 0.0023

number = 1234;
Console.WriteLine(number.ToString("G2", CultureInfo.InvariantCulture));
// Displays 1.2E+03

number = Math.PI;
Console.WriteLine(number.ToString("G5", CultureInfo.InvariantCulture));
// Displays 3.1416

Especificador de formato numérico (N)


O especificador de formato numérico ("N") converte um número em uma cadeia de
caracteres no formato "-d,ddd,ddd.ddd…", em que "-" indica um símbolo de número
negativo, se necessário, "d" indica um dígito (0-9), "," indica um separador de grupos de
número e "." indica um símbolo de ponto decimal. O especificador de precisão indica o
número desejado de dígitos após o ponto decimal. Se o especificador de precisão for
omitido, o número de casas decimais será definido pela propriedade
NumberFormatInfo.NumberDecimalDigits atual.

A cadeia de caracteres de resultado é afetada pelas informações de formatação do


objeto NumberFormatInfo atual. A tabela a seguir lista as propriedades
NumberFormatInfo que controlam a formatação da cadeia de caracteres de resultado.

Propriedade Descrição
NumberFormatInfo
Propriedade Descrição
NumberFormatInfo

NegativeSign Define a cadeia de caracteres que indica que um número é negativo.

NumberNegativePattern Define o formato dos valores negativos e especifica se o sinal de


negativo é representado por parênteses ou a propriedade
NegativeSign.

NumberGroupSizes Define o número total de dígitos integrais que aparecem entre os


separadores de grupo.

NumberGroupSeparator Define a cadeia de caracteres que separa grupos de números


integrais.

NumberDecimalSeparator Define a cadeia de caracteres que separa dígitos decimais e integrais.

NumberDecimalDigits Define o número padrão de dígitos decimais. Esse valor pode ser
substituído usando um especificador de precisão.

O seguinte exemplo formata valores de ponto flutuante variados com o especificador


de formato numérico:

C#

double dblValue = -12445.6789;


Console.WriteLine(dblValue.ToString("N", CultureInfo.InvariantCulture));
// Displays -12,445.68
Console.WriteLine(dblValue.ToString("N1",
CultureInfo.CreateSpecificCulture("sv-SE")));
// Displays -12 445,7

int intValue = 123456789;


Console.WriteLine(intValue.ToString("N1", CultureInfo.InvariantCulture));
// Displays 123,456,789.0

Especificador de formato percentual (P)


O especificador de formato de porcentagem (“P”) multiplica um número por 100 e o
converte em uma cadeia de caracteres que representa uma porcentagem. O
especificador de precisão indica o número de casas decimais desejadas. Se o
especificador de precisão for omitido, a precisão numérica padrão fornecida pela
propriedade PercentDecimalDigits atual será usada.

A tabela a seguir lista as propriedades NumberFormatInfo que controlam a formatação


da cadeia de caracteres retornada.
Propriedade Descrição
NumberFormatInfo

PercentPositivePattern Define o posicionamento do símbolo de porcentagem para valores


positivos.

PercentNegativePattern Define o posicionamento do símbolo de porcentagem e o símbolo


negativo para valores negativos.

NegativeSign Define a cadeia de caracteres que indica que um número é negativo.

PercentSymbol Define o símbolo de porcentagem.

PercentDecimalDigits Define o número padrão de dígitos decimais em um valor percentual.


Esse valor pode ser substituído usando-se o especificador de precisão.

PercentDecimalSeparator Define a cadeia de caracteres que separa dígitos decimais e integrais.

PercentGroupSeparator Define a cadeia de caracteres que separa grupos de números


integrais.

PercentGroupSizes Define o número de dígitos inteiros que aparecem em um grupo.

O seguinte exemplo formata valores de ponto flutuante variados com o especificador


de formato de porcentagem:

C#

double number = .2468013;


Console.WriteLine(number.ToString("P", CultureInfo.InvariantCulture));
// Displays 24.68 %
Console.WriteLine(number.ToString("P",
CultureInfo.CreateSpecificCulture("hr-HR")));
// Displays 24,68%
Console.WriteLine(number.ToString("P1", CultureInfo.InvariantCulture));
// Displays 24.7 %

Especificador de formato de ida e volta (R)


O especificador de formato de ida e volta ("R") tenta garantir que um valor numérico
convertido em uma cadeia de caracteres seja analisado com o mesmo valor numérico.
Esse formato é compatível apenas com os tipos Half, Single, Double e BigInteger.

No .NET Framework e em versões do .NET Core anteriores à 3.0, o especificador de


formato "R" não consegue obter valores Double de ida e volta com êxito em alguns
casos. Para os valores Double e Single, o especificador de formato "R" oferece um
desempenho relativamente baixo. Em vez disso, recomendamos que você use o
especificador de formato "G17" para os valores Double e o especificador de formato
"G9" para realizar a viagem de ida e volta dos valores Single.

Quando um valor BigInteger é formatado usando esse especificador, sua representação


de cadeia de caracteres contém todos os dígitos significativos no valor de BigInteger.

Embora você possa incluir um especificador de precisão, ele será ignorado. Idas e voltas
têm precedência sobre a precisão quando esse especificador é usado. A cadeia de
caracteres de resultado é afetada pelas informações de formatação do objeto
NumberFormatInfo atual. A tabela a seguir lista as propriedades NumberFormatInfo que
controlam a formatação da cadeia de caracteres de resultado.

Propriedade Descrição
NumberFormatInfo

NegativeSign Define a cadeia de caracteres que indica que um número é


negativo.

NumberDecimalSeparator Define a cadeia de caracteres que separa dígitos integrais de


dígitos decimais.

PositiveSign Define a cadeia de caracteres que indica que um expoente é


positivo.

O exemplo a seguir formata um valor BigInteger com o especificador de formato de ida


e volta.

C#

using System;
using System.Numerics;

public class Example


{
public static void Main()
{
var value = BigInteger.Pow(Int64.MaxValue, 2);
Console.WriteLine(value.ToString("R"));
}
}
// The example displays the following output:
// 85070591730234615847396907784232501249

) Importante

Em alguns casos, os valores Double formatados com a cadeia de caracteres de


formato numérico padrão "R" não realizam a viagem de ida e volta se forem
compilados usando as opções /platform:x64 ou /platform:anycpu e executados
em sistemas de 64 bits. Para saber mais, consulte o seguinte parágrafo.

Para solucionar o problema de valores Double formatados com a cadeia de caracteres


no formato numérico padrão "R" que não conseguem realizar a viagem de ida e volta se
forem compilados usando as opções /platform:x64 ou /platform:anycpu e executados
em sistemas de 64 bits, formate os valores Double usando a cadeia de caracteres de
formato numérico padrão "G17". O seguinte exemplo usa a cadeia de caracteres de
formato "R" com um valor Double que não realiza a viagem de ida e volta com sucesso
e também usa a cadeia de caracteres de formato "G17" para realizar a viagem de ida e
volta do valor original:

C#

Console.WriteLine("Attempting to round-trip a Double with 'R':");


double initialValue = 0.6822871999174;
string valueString = initialValue.ToString("R",
CultureInfo.InvariantCulture);
double roundTripped = double.Parse(valueString,
CultureInfo.InvariantCulture);
Console.WriteLine("{0:R} = {1:R}: {2}\n",
initialValue, roundTripped,
initialValue.Equals(roundTripped));

Console.WriteLine("Attempting to round-trip a Double with 'G17':");


string valueString17 = initialValue.ToString("G17",
CultureInfo.InvariantCulture);
double roundTripped17 = double.Parse(valueString17,
CultureInfo.InvariantCulture);
Console.WriteLine("{0:R} = {1:R}: {2}\n",
initialValue, roundTripped17,
initialValue.Equals(roundTripped17));
// If compiled to an application that targets anycpu or x64 and run on an
x64 system,
// the example displays the following output:
// Attempting to round-trip a Double with 'R':
// 0.6822871999174 = 0.68228719991740006: False
//
// Attempting to round-trip a Double with 'G17':
// 0.6822871999174 = 0.6822871999174: True

Especificador de formato hexadecimal (X)


O especificador de formato hexadecimal ("X") converte um número em uma cadeia de
caracteres de dígitos hexadecimais. A caixa do especificador de formato indica se
caracteres maiúsculos ou minúsculos devem ser usados para os dígitos hexadecimais
maiores que 9. Por exemplo, use "X" para produzir "ABCDEF" e "x" para produzir
"abcdef". Esse formato é compatível apenas com tipos integrais.

O especificador de precisão indica o número mínimo de dígitos desejados na cadeia de


caracteres resultante. Se necessário, o número é preenchido com zeros à esquerda para
produzir o número de dígitos fornecido pelo especificador de precisão.

A cadeia de caracteres do resultado não é afetada pelas informações de formatação do


objeto NumberFormatInfo atual.

O exemplo a seguir formata valores Int32 com o especificador de formato hexadecimal.

C#

int value;

value = 0x2045e;
Console.WriteLine(value.ToString("x"));
// Displays 2045e
Console.WriteLine(value.ToString("X"));
// Displays 2045E
Console.WriteLine(value.ToString("X8"));
// Displays 0002045E

value = 123456789;
Console.WriteLine(value.ToString("X"));
// Displays 75BCD15
Console.WriteLine(value.ToString("X2"));
// Displays 75BCD15

Observações
Esta seção contém informações adicionais sobre como usar cadeias de caracteres de
formato numérico padrão.

Configurações do Painel de Controle


As configurações no item Opções Regionais e de Idioma do Painel de Controle
influenciam a cadeia de caracteres de resultado produzida por uma operação de
formatação. Essas configurações são usadas para inicializar o objeto NumberFormatInfo
associado à cultura atual, a qual fornece os valores usados para determinar a
formatação. Computadores que usam configurações diferentes geram cadeias de
caracteres de resultado diferentes.
Além disso, se o construtor CultureInfo(String) for usado para criar uma instância de um
novo objeto CultureInfo que representa a mesma cultura que a cultura atual do sistema,
quaisquer personalizações estabelecidas pelo item Opções Regionais e de Idioma no
Painel de Controle serão aplicadas ao novo objeto CultureInfo. Você pode usar o
construtor CultureInfo(String, Boolean) para criar um objeto CultureInfo que não reflita
as personalizações de um sistema.

Propriedades de NumberFormatInfo
A formatação é influenciada pelas propriedades do objeto NumberFormatInfo atual, que
é fornecido implicitamente pela cultura atual ou explicitamente pelo parâmetro
IFormatProvider do método que invoca a formatação. Especifique um objeto
NumberFormatInfo ou CultureInfo para esse parâmetro.

7 Observação

Para obter informações sobre como personalizar os padrões ou cadeias de


caracteres usados na formatação de valores numéricos, consulte o tópico de classe
NumberFormatInfo.

Tipos numéricos integrais e de ponto flutuante


Algumas descrições de especificadores de formato numérico padrão referem-se a tipos
inteiros ou de ponto flutuante. Os tipos numéricos integrais são Byte, SByte, Int16, Int32,
Int64, UInt16, UInt32, UInt64 e BigInteger. Os tipos numéricos de ponto flutuante são
Decimal, Half, Single e Double.

Infinitos de ponto flutuante e NaN


Independentemente da cadeia de caracteres de formato, se o valor de um tipo de ponto
flutuante Half, Single ou Double for infinito positivo, infinito negativo ou um não for um
número (NaN), a cadeia de caracteres formatada será o valor das respectivas
propriedades PositiveInfinitySymbol, NegativeInfinitySymbol ou NaNSymbol
especificadas pelo objeto NumberFormatInfo aplicável no momento.

Exemplo de código
O exemplo a seguir formata um inteiro e um valor numérico de ponto flutuante usando
a cultura en-US e todos os especificadores de formato numérico padrão. Este exemplo
usa dois tipos numéricos específicos, (Double e Int32), mas poderia produzir resultados
semelhantes para qualquer um dos tipos base numéricos (Byte, SByte, Int16, Int32,
Int64, UInt16, UInt32, UInt64, BigInteger, Decimal, Half e Single).

C#

// Display string representations of numbers for en-us culture


CultureInfo ci = new CultureInfo("en-us");

// Output floating point values


double floating = 10761.937554;
Console.WriteLine("C: {0}",
floating.ToString("C", ci)); // Displays "C: $10,761.94"
Console.WriteLine("E: {0}",
floating.ToString("E03", ci)); // Displays "E: 1.076E+004"
Console.WriteLine("F: {0}",
floating.ToString("F04", ci)); // Displays "F: 10761.9376"
Console.WriteLine("G: {0}",
floating.ToString("G", ci)); // Displays "G: 10761.937554"
Console.WriteLine("N: {0}",
floating.ToString("N03", ci)); // Displays "N: 10,761.938"
Console.WriteLine("P: {0}",
(floating/10000).ToString("P02", ci)); // Displays "P: 107.62 %"
Console.WriteLine("R: {0}",
floating.ToString("R", ci)); // Displays "R: 10761.937554"
Console.WriteLine();

// Output integral values


int integral = 8395;
Console.WriteLine("C: {0}",
integral.ToString("C", ci)); // Displays "C: $8,395.00"
Console.WriteLine("D: {0}",
integral.ToString("D6", ci)); // Displays "D: 008395"
Console.WriteLine("E: {0}",
integral.ToString("E03", ci)); // Displays "E: 8.395E+003"
Console.WriteLine("F: {0}",
integral.ToString("F01", ci)); // Displays "F: 8395.0"
Console.WriteLine("G: {0}",
integral.ToString("G", ci)); // Displays "G: 8395"
Console.WriteLine("N: {0}",
integral.ToString("N01", ci)); // Displays "N: 8,395.0"
Console.WriteLine("P: {0}",
(integral/10000.0).ToString("P02", ci)); // Displays "P: 83.95 %"
Console.WriteLine("X: 0x{0}",
integral.ToString("X", ci)); // Displays "X: 0x20CB"
Console.WriteLine();

Confira também
NumberFormatInfo
Cadeias de caracteres de formato numérico personalizado
Formatar tipos
Como preencher um número com zeros à esquerda
Formatação de composição
Amostra: Utilitário de Formatação do WinForms do .NET Core (C#)
Amostra: Utilitário de Formatação do WinForms do .NET Core (Visual Basic)
Cadeias de caracteres de formato
numérico personalizado
Artigo • 10/05/2023

Você pode criar uma cadeia de caracteres de formato numérico personalizado, que
consiste em um ou mais especificadores numéricos personalizados, para definir a
formatação de dados numéricos. Uma cadeia de caracteres de formato numérico
personalizado é qualquer cadeia de caracteres que é não uma cadeia de caracteres de
formato numérico padrão.

As cadeias de caracteres de formato numérico personalizado têm suporte de algumas


sobrecargas do método ToString de todos os tipos numéricos. Por exemplo, você pode
fornecer uma cadeia de caracteres de formato numérico para os métodos
ToString(String) e ToString(String, IFormatProvider) do tipo Int32. Cadeias de caracteres
de formato numérico personalizado também têm suporte no recurso de formatação
composta do .NET Framework, o qual é usado por alguns métodos Write e WriteLine
das classes Console e StreamWriter, o método String.Format e o método
StringBuilder.AppendFormat. O recurso de Interpolação de cadeia de caracteres
também é compatível com cadeias de caracteres de formato numérico personalizado.

 Dica

Baixe o Utilitário de Formatação, um aplicativo do Windows Forms do .NET Core


que permite aplicar cadeias de caracteres de formato a valores numéricos ou de
data e hora e exibir a cadeia de caracteres de resultado. O código-fonte está
disponível para o C# e o Visual Basic.

A tabela a seguir descreve os especificadores de formato numérico personalizado e


exibe a saída de exemplo produzida por cada especificador de formato. Consulte a
seção Notas para obter informações adicionais sobre como usar cadeias de caracteres
de formato numérico personalizado e a seção Exemplo para obter uma ilustração
abrangente de seu uso.

Especificador Nome Descrição Exemplos


de formato
Especificador Nome Descrição Exemplos
de formato

"0" Espaço Substitui o zero pelo dígito correspondente, se 1234.5678


reservado houver um presente. Caso contrário, o zero ("00000") ->
de zero aparecerá na cadeia de caracteres de resultado. 01235

Mais informações: Especificador de formato 0.45678


personalizado "0". ("0.00", en-
US) -> 0.46

0.45678
("0.00", fr-FR)
-> 0,46

"#" Espaço Substitui o símbolo "#" pelo dígito 1234.5678


reservado correspondente, se houver um presente. Caso ("#####") ->
de dígito contrário, nenhum dígito aparecerá na cadeia de 1235
caracteres de resultado.
0.45678
Observe que nenhum dígito aparece na cadeia de ("#.##", en-
caracteres de resultado se o dígito na cadeia de US) -> .46
entrada correspondente for um 0 não
significativo. Por exemplo, 0003 ("####") -> 3. 0.45678
("#.##", fr-FR)
Mais informações: Especificador de formato -> ,46
personalizado "#".

"." Ponto Determina a posição do separador decimal na 0.45678


decimal cadeia de caracteres de resultado. ("0.00", en-
US) -> 0.46
Mais informações: O especificador personalizado
".". 0.45678
("0.00", fr-FR)
-> 0,46
Especificador Nome Descrição Exemplos
de formato

"," Separador Funciona tanto como um separador de grupo Especificador


de grupo e quanto como um especificador de escala de separador
escala numérica. Como separador de grupo, insere um de grupo:
numérica caractere separador de grupo localizado entre
cada grupo. Como especificador de escala 2147483647
numérica, divide um número por 1000 para cada ("##,#", en-
vírgula especificada. US) ->
2,147,483,647
Mais informações: Especificador de formato
personalizado ",". 2147483647
("##,#", es-
ES) ->
2.147.483.647

Especificador
de escala:

2147483647
("#,#,,", en-
US) -> 2,147

2147483647
("#,#,,", es-ES)
-> 2.147

"%" Espaço Multiplica um número por 100 e insere um 0.3697


reservado símbolo percentual localizado na cadeia de ("%#0.00",
percentual caracteres de resultado. en-US) ->
%36.97
Mais informações: Especificador de formato
personalizado "%". 0.3697
("%#0.00", el-
GR) ->
%36,97

0.3697 ("##.0
%", en-US) ->
37.0 %

0.3697 ("##.0
%", el-GR) ->
37,0 %
Especificador Nome Descrição Exemplos
de formato

"‰" Espaço Multiplica um número por 1000 e insere um 0.03697


reservado símbolo de por milhar localizado na cadeia de ("#0.00‰",
por milhar caracteres de resultado. en-US) ->
36.97‰
Mais informações: Especificador de formato
personalizado "‰". 0.03697
("#0.00‰",
ru-RU) ->
36,97‰

"E0" Notação Se seguida por pelo menos um 0 (zero), formata o 987654


exponencial resultado usando notação exponencial. A caixa de ("#0.0e0") ->
"E+0" “E” ou “e” indica a caixa do símbolo do expoente 98.8e4
na cadeia de caracteres de resultado. O número
"E-0" de zero depois do caractere “E” ou “e” determina 1503.92311
o número mínimo de dígitos do expoente. Um ("0.0##e+00")
"e0" sinal de positivo (+) indica que um caractere de -> 1.504e+03
sinal sempre precede o expoente. Um sinal de
"e+0" negativo (-) indica que um caractere de sinal 1.8901385E-
precede apenas expoentes negativos. 16
"e-0" ("0.0e+00") -
Mais informações: Especificadores de formato > 1.9e-16
personalizado "E" e "e".

"\" Caractere Faz com que o próximo caractere seja 987654


de escape interpretado como um literal em vez de como um ("\###00\#")
especificador de formato personalizado. -> #987654#

Mais informações: O caractere de escape "\".

'string' Delimitador Indica que os caracteres delimitados devem ser 68 ("# '
de cadeia copiados para a cadeia de caracteres de resultado graus'") -> 68
"string" de sem sofrerem alterações. graus
caracteres
literal Para saber mais: Literais de cadeia de caracteres. 68 ("# '
graus'") -> 68
graus
Especificador Nome Descrição Exemplos
de formato

; Separador Define seções com cadeias de caracteres de 12.345


de seção formato separadas para números positivos, ("#0.0#;
negativos e zero. (#0.0#);-\0-")
-> 12.35
Mais informações: Separador de seção ";".
0 ("#0.0#;
(#0.0#);-\0-")
-> -0-

-12.345
("#0.0#;
(#0.0#);-\0-")
-> (12.35)

12.345
("#0.0#;
(#0.0#)") ->
12.35

0 ("#0.0#;
(#0.0#)") ->
0.0

-12.345
("#0.0#;
(#0.0#)") ->
(12.35)

Outro Todos os O caractere é copiado, inalterado, para a cadeia 68 ("# °") ->
outros de caracteres de resultado. 68 °
caracteres
Para saber mais: Literais de cadeia de caracteres.

As seções a seguir fornecem informações detalhadas sobre cada um dos especificadores


de formato numérico personalizado.

7 Observação

Alguns dos exemplos de C# neste artigo são executados no executador de código


embutido Try.NET e no playground. Clique no botão Executar para executar um
exemplo em uma janela interativa. Ao executar o código, é possível modificá-lo e
executar o código modificado clicando em Executar novamente. O código
modificado será executado na janela interativa ou, se a compilação falhar, a janela
interativa exibirá todos as mensagens de erro do compilador C#.
Especificador personalizado "0"
O especificador de formato personalizado “0 " funciona como um símbolo de espaço
reservado de zero. Se o valor que está sendo formatado tiver um dígito na posição em
que o zero aparece na cadeia de caracteres de formato, esse dígito será copiado para a
cadeia de caracteres de resultado; caso contrário, um zero aparecerá na cadeia de
caracteres de resultado. A posição do zero mais à esquerda antes do ponto decimal e o
zero mais à direita após o ponto decimal determina o intervalo de dígitos que estão
sempre presentes na cadeia de caracteres de resultado.

O especificador "00" faz com que o valor seja arredondado para o dígito mais próximo
anterior ao decimal, no qual o arredondamento para cima sempre é usado. Por
exemplo, a formatação de 34,5 com "00" resultaria no valor 35.

O exemplo a seguir mostra vários valores formatados com cadeias de caracteres de


formato personalizado que incluem espaços reservados de zero.

C#

double value;

value = 123;
Console.WriteLine(value.ToString("00000"));
Console.WriteLine(String.Format("{0:00000}", value));
// Displays 00123

value = 1.2;
Console.WriteLine(value.ToString("0.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.00}", value));
// Displays 1.20

Console.WriteLine(value.ToString("00.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:00.00}", value));
// Displays 01.20

CultureInfo daDK = CultureInfo.CreateSpecificCulture("da-DK");


Console.WriteLine(value.ToString("00.00", daDK));
Console.WriteLine(String.Format(daDK, "{0:00.00}", value));
// Displays 01,20

value = .56;
Console.WriteLine(value.ToString("0.0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.0}", value));
// Displays 0.6
value = 1234567890;
Console.WriteLine(value.ToString("0,0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0}", value));
// Displays 1,234,567,890

CultureInfo elGR = CultureInfo.CreateSpecificCulture("el-GR");


Console.WriteLine(value.ToString("0,0", elGR));
Console.WriteLine(String.Format(elGR, "{0:0,0}", value));
// Displays 1.234.567.890

value = 1234567890.123456;
Console.WriteLine(value.ToString("0,0.0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0.0}", value));
// Displays 1,234,567,890.1

value = 1234.567890;
Console.WriteLine(value.ToString("0,0.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0,0.00}", value));
// Displays 1,234.57

Voltar à tabela

Especificador personalizado "#"


O especificador de formato personalizado “# " funciona como um símbolo de espaço
reservado de dígito. Se o valor que está sendo formatado tem um dígito na posição
onde o símbolo "#" aparece na cadeia de caracteres de formato, esse dígito é copiado
para a cadeia de caracteres de resultado. Caso contrário, nada é armazenado nessa
posição na cadeia de caracteres de resultado.

Observe que esse especificador nunca exibe um zero que não seja um dígito
significativo, mesmo se o zero é o único dígito na cadeia de caracteres. Ele exibirá zero
somente se ele for um dígito significativo no número que está sendo exibido.

A cadeia de caracteres de formato "##" faz com que o valor seja arredondado para o
dígito mais próximo anterior ao decimal, no qual o arredondamento para cima sempre é
usado. Por exemplo, a formatação de 34,5 com "##" resultaria no valor 35.

O exemplo a seguir mostra vários valores formatados com cadeias de caracteres de


formato personalizado que incluem espaços reservados de dígito.

C#

double value;
value = 1.2;
Console.WriteLine(value.ToString("#.##", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#.##}", value));
// Displays 1.2

value = 123;
Console.WriteLine(value.ToString("#####"));
Console.WriteLine(String.Format("{0:#####}", value));
// Displays 123

value = 123456;
Console.WriteLine(value.ToString("[##-##-##]"));
Console.WriteLine(String.Format("{0:[##-##-##]}", value));
// Displays [12-34-56]

value = 1234567890;
Console.WriteLine(value.ToString("#"));
Console.WriteLine(String.Format("{0:#}", value));
// Displays 1234567890

Console.WriteLine(value.ToString("(###) ###-####"));
Console.WriteLine(String.Format("{0:(###) ###-####}", value));
// Displays (123) 456-7890

Para retornar uma cadeia de caracteres de resultado em que dígitos ausentes ou zeros à
esquerda são substituídos por espaços, use o recurso de formatação de composição e
especifique uma largura de campo, como mostra o exemplo a seguir.

C#

using System;

public class SpaceOrDigit


{
public static void Main()
{
Double value = .324;
Console.WriteLine("The value is: '{0,5:#.###}'", value);
}
}
// The example displays the following output if the current culture
// is en-US:
// The value is: ' .324'

Voltar à tabela

Especificador personalizado "."


O especificador de formato personalizado "." insere um separador decimal localizado na
cadeia de caracteres resultante. O primeiro caractere de ponto na cadeia de caracteres
de formato determina o local do separador decimal no valor formatado; quaisquer
caracteres de ponto adicionais são ignorados.

O caractere que é usado como separador decimal na cadeia de caracteres de resultado


é determinado pela propriedade NumberDecimalSeparator do objeto
NumberFormatInfo que controla a formatação.

O exemplo a seguir usa o especificador de formato "." para definir a posição do ponto
decimal em várias cadeias de caracteres de resultados.

C#

double value;

value = 1.2;
Console.WriteLine(value.ToString("0.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.00}", value));
// Displays 1.20

Console.WriteLine(value.ToString("00.00", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:00.00}", value));
// Displays 01.20

Console.WriteLine(value.ToString("00.00",
CultureInfo.CreateSpecificCulture("da-DK")));
Console.WriteLine(String.Format(CultureInfo.CreateSpecificCulture("da-DK"),
"{0:00.00}", value));
// Displays 01,20

value = .086;
Console.WriteLine(value.ToString("#0.##%", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#0.##%}", value));
// Displays 8.6%

value = 86000;
Console.WriteLine(value.ToString("0.###E+0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+0}", value));
// Displays 8.6E+4

Voltar à tabela

Especificador personalizado ","


O caractere "," funciona tanto como um separador de grupo quanto como um
especificador de escala do número.

Separador de grupo: se uma ou mais vírgulas forem especificadas entre dois


espaços reservados de dígito (0 ou #) que formatam os dígitos integrais de um
número, um caractere separador de grupo será inserido entre cada grupo de
números na parte integral da saída.

As propriedades NumberGroupSeparator e NumberGroupSizes do objeto


NumberFormatInfo atual determinam o caractere usado como separador de
número de grupo e o tamanho de cada grupo de números. Por exemplo, se a
cadeia de caracteres "#,#" e a cultura invariável forem usadas para formatar o
número 1000, a saída será "1.000".

Especificador de escala numérica: se uma ou mais vírgulas forem especificadas


imediatamente à esquerda do ponto decimal explícito ou implícito, o número a ser
formatado será dividido por 1000 para cada vírgula. Por exemplo, se a cadeia de
caracteres "0,," for usada para formatar o número 100 milhões, a saída será "100".

Você pode usar os especificadores de separador de grupo e escala numérica na mesma


cadeia de caracteres de formato. Por exemplo, se a cadeia de caracteres "#, 0,," e a
cultura invariante forem usadas para formatar o número um bilhão, a saída será "1.000".

O exemplo a seguir ilustra o uso da vírgula como um separador de grupo.

C#

double value = 1234567890;


Console.WriteLine(value.ToString("#,#", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,#}", value));
// Displays 1,234,567,890

Console.WriteLine(value.ToString("#,##0,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,##0,,}", value));
// Displays 1,235

O exemplo a seguir ilustra o uso da vírgula como um especificador de escala numérica.

C#

double value = 1234567890;


Console.WriteLine(value.ToString("#,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,,}", value));
// Displays 1235
Console.WriteLine(value.ToString("#,,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,,,}", value));
// Displays 1

Console.WriteLine(value.ToString("#,##0,,", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#,##0,,}", value));
// Displays 1,235

Voltar à tabela

Especificador personalizado "%"


Um sinal de porcentagem (%) em uma cadeia de caracteres de formato faz com que um
número seja multiplicado por 100 antes de ser formatado. O símbolo de porcentagem
localizado é inserido no próprio número no local em que o % aparece na cadeia de
caracteres de formato. O caractere de porcentagem usado é definido pela propriedade
PercentSymbol do objeto NumberFormatInfo atual.

O exemplo a seguir define várias cadeias de caracteres de formato personalizado que


incluem o especificador personalizado "%".

C#

double value = .086;


Console.WriteLine(value.ToString("#0.##%", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:#0.##%}", value));
// Displays 8.6%

Voltar à tabela

Especificador personalizado "‰"


Um caractere de por mil sinal (‰ ou \u2030) em uma cadeia de caracteres de formato
faz com que um número seja multiplicado por 1000 antes de ser formatado. O símbolo
de por mil apropriado é inserido na cadeia de caracteres retornada no local em que o
símbolo ‰ aparece na cadeia de caracteres de formato. O caractere de por mil usado é
definido pela propriedade NumberFormatInfo.PerMilleSymbol do objeto que fornece
informações de formatação específicas da cultura.
O exemplo a seguir define uma cadeia de caracteres de formato personalizado que
inclui o especificador personalizado "‰".

C#

double value = .00354;


string perMilleFmt = "#0.## " + '\u2030';
Console.WriteLine(value.ToString(perMilleFmt,
CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:" + perMilleFmt + "}", value));
// Displays 3.54‰

Voltar à tabela

Especificadores personalizados "E" e "e"


Se qualquer uma das cadeias de caracteres "E", " E + ", " E - ", "e", " e + " ou " e - "
estiver presente na cadeia de caracteres de formato e for seguida imediatamente por
pelo menos um caractere zero, então o número será formatado usando notação
científica com um "E" ou "e" inserido entre o número e o expoente. O número de zeros
após o indicador de notação científica determina o número mínimo de dígitos que
serão enviados para o expoente. Os formatos "E+" e "e+" indicam que um caractere de
positivo ou negativo sempre deve preceder o expoente. Os formatos "E", " E-", "e" ou
"e-" indicam que um caractere de sinal só deve preceder os expoentes negativos.

O exemplo a seguir formata vários valores numéricos usando os especificadores para


notação científica.

C#

double value = 86000;


Console.WriteLine(value.ToString("0.###E+0", CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+0}", value));
// Displays 8.6E+4

Console.WriteLine(value.ToString("0.###E+000",
CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E+000}", value));
// Displays 8.6E+004

Console.WriteLine(value.ToString("0.###E-000",
CultureInfo.InvariantCulture));
Console.WriteLine(String.Format(CultureInfo.InvariantCulture,
"{0:0.###E-000}", value));
// Displays 8.6E004

Voltar à tabela

O caractere de escape "\"


Os símbolos “#”, “0 ", “. ”, “,”, “% e "‰" em uma cadeia de caracteres de formato são
interpretados como especificadores de formato e não como caracteres literais.
Dependendo da sua posição em uma cadeia de caracteres de formato personalizado, o
"E" em maiúscula e minúscula, bem como os símbolos + e -, também podem ser
interpretados como especificadores de formato.

Para impedir que um caractere seja interpretado como um especificador de formato,


você pode precedê-lo com uma barra invertida, que é o caractere de escape. O
caractere de escape significa que o próximo caractere é um literal de caractere que deve
ser incluído inalterado na cadeia de caracteres de resultado.

Para incluir uma barra invertida em uma cadeia de caracteres de resultado, você deve
escapá-la com outra barra invertida ( \\ ).

7 Observação

Alguns compiladores, como os compiladores C++ e C#, também podem interpretar


um único caractere de barra invertida como um caractere de escape. Para garantir
que uma cadeia de caracteres seja interpretada corretamente quando formatada,
você poderá usar o caractere literal de cadeia de caracteres textual (o caractere @)
antes da cadeia de caracteres em C# ou adicionar outro caractere de barra
invertida antes de cada barra invertida em C# e em C++. O exemplo de C# a seguir
ilustra ambas as abordagens.

O exemplo a seguir usa o caractere de escape para impedir que a operação de


formatação interprete os caracteres "#", "0", e "\" como caracteres de escape ou
especificadores de formato. Os exemplos em C# usam uma barra invertida adicional
para garantir que uma barra invertida seja interpretada como um caractere literal.

C#

int value = 123;


Console.WriteLine(value.ToString("\\#\\#\\# ##0 dollars and \\0\\0 cents
\\#\\#\\#"));
Console.WriteLine(String.Format("{0:\\#\\#\\# ##0 dollars and \\0\\0 cents
\\#\\#\\#}",
value));
// Displays ### 123 dollars and 00 cents ###

Console.WriteLine(value.ToString(@"\#\#\# ##0 dollars and \0\0 cents


\#\#\#"));
Console.WriteLine(String.Format(@"{0:\#\#\# ##0 dollars and \0\0 cents
\#\#\#}",
value));
// Displays ### 123 dollars and 00 cents ###

Console.WriteLine(value.ToString("\\\\\\\\\\\\ ##0 dollars and \\0\\0 cents


\\\\\\\\\\\\"));
Console.WriteLine(String.Format("{0:\\\\\\\\\\\\ ##0 dollars and \\0\\0
cents \\\\\\\\\\\\}",
value));
// Displays \\\ 123 dollars and 00 cents \\\

Console.WriteLine(value.ToString(@"\\\\\\ ##0 dollars and \0\0 cents


\\\\\\"));
Console.WriteLine(String.Format(@"{0:\\\\\\ ##0 dollars and \0\0 cents
\\\\\\}",
value));
// Displays \\\ 123 dollars and 00 cents \\\

Voltar à tabela

Separador de seção ";"


O ponto-e-vírgula (;) é um especificador de formato condicional que aplica formatação
diferente a um número dependendo se o valor é positivo, negativo ou zero. Para
produzir esse comportamento, uma cadeia de caracteres de formato personalizado
pode conter até três seções separadas por ponto-e-vírgula. Essas seções são descritas
na tabela a seguir.

Número Descrição
de
seções

Uma A cadeia de caracteres de formato aplica-se a todos os valores.


seção

Duas A primeira seção aplica-se a valores positivos e zeros e a segunda seção aplica-se a
seções valores negativos.

Se o número a ser formatado for negativo, mas se tornar zero após o arredondamento
de acordo com o formato na segunda seção, então o zero resultante será formatado
de acordo com a primeira seção.
Número Descrição
de
seções

Trê A primeira seção aplica-se a valores positivos, a segunda seção aplica-se a valores
seções negativos e a terceira seção aplica-se a zeros.

A segunda seção pode ser deixada em branco (ao não ter nada entre os pontos-e-
vírgulas), caso em que a primeira seção se aplica a todos os valores diferentes de zero.

Se o número a ser formatado for não zero, mas se tornar zero após o arredondamento
de acordo com o formato na primeira ou segunda seção, então o zero resultante será
formatado de acordo com a terceira seção.

Separadores de seção ignoram qualquer formatação preexistente associada a um


número quando o valor final é formatado. Por exemplo, valores negativos sempre são
exibidos sem um sinal de negativ o quando os separadores de seção são usados. Se
desejar que o valor final formatado contenha um sinal de negativo, você deverá incluir
explicitamente o sinal de negativo como parte de especificador de formato
personalizado.

O exemplo a seguir utiliza o especificador de formato ";" para formatar números


positivos, negativos e zero de maneira diferente.

C#

double posValue = 1234;


double negValue = -1234;
double zeroValue = 0;

string fmt2 = "##;(##)";


string fmt3 = "##;(##);**Zero**";

Console.WriteLine(posValue.ToString(fmt2));
Console.WriteLine(String.Format("{0:" + fmt2 + "}", posValue));
// Displays 1234

Console.WriteLine(negValue.ToString(fmt2));
Console.WriteLine(String.Format("{0:" + fmt2 + "}", negValue));
// Displays (1234)

Console.WriteLine(zeroValue.ToString(fmt3));
Console.WriteLine(String.Format("{0:" + fmt3 + "}", zeroValue));
// Displays **Zero**

Voltar à tabela
Literais de caracteres
Os especificadores de formato que aparecem em uma cadeia de caracteres de formato
numérica personalizada sempre são interpretados como caracteres de formatação e
nunca como caracteres literais. Isso inclui os seguintes caracteres:

0
#
%

'
\
.
,
E ou e, de acordo com a posição na cadeia de caracteres de formato.

Todos os outros caracteres sempre são interpretados como literais de caracteres e, em


uma operação de formatação, são incluídos na cadeia de caracteres de resultado
inalterada. Em uma operação de análise, eles devem corresponder exatamente aos
caracteres na cadeia de entrada; a comparação diferencia maiúsculas de minúsculas.

O exemplo a seguir ilustra um uso comum de unidades de caractere literal (neste caso,
milhares):

C#

double n = 123.8;
Console.WriteLine($"{n:#,##0.0K}");
// The example displays the following output:
// 123.8K

Há duas maneiras de indicar que os caracteres devem ser interpretados como caracteres
literais e não como caracteres de formatação, para que possam ser incluídos em uma
cadeia de caracteres de resultado ou analisados com êxito em uma cadeia de caracteres
de entrada:

Pelo escape de um caractere de formatação. Para obter mais informações, consulte


O caractere de escape "\".

Colocando toda a cadeia de caracteres literal entre aspas ou apóstrofos.

O exemplo a seguir usa as duas abordagem para incluir caracteres reservados em uma
cadeia de caracteres de formato numérica personalizada.
C#

double n = 9.3;
Console.WriteLine($@"{n:##.0\%}");
Console.WriteLine($@"{n:\'##\'}");
Console.WriteLine($@"{n:\\##\\}");
Console.WriteLine();
Console.WriteLine($"{n:##.0'%'}");
Console.WriteLine($@"{n:'\'##'\'}");
// The example displays the following output:
// 9.3%
// '9'
// \9\
//
// 9.3%
// \9\

Observações

Infinitos de ponto flutuante e NaN


Independentemente da cadeia de caracteres de formato, se o valor de um tipo de ponto
flutuante Half, Single ou Double for infinito positivo, infinito negativo ou não um
número (NaN), a cadeia de caracteres formatada será o valor do respectiva propriedade
PositiveInfinitySymbol, NegativeInfinitySymbol ou NaNSymbol especificada pelo objeto
NumberFormatInfo atualmente aplicável.

Configurações do Painel de Controle


As configurações no item Opções Regionais e de Idioma do Painel de Controle
influenciam a cadeia de caracteres de resultado produzida por uma operação de
formatação. Essas configurações são usadas para inicializar o objeto NumberFormatInfo
associado à cultura atual e a cultura atual fornece valores usados para controlar a
formatação. Computadores que usam configurações diferentes geram cadeias de
caracteres de resultado diferentes.

Além disso, se o constructo CultureInfo(String) for usado para criar uma instância de um
novo objeto CultureInfo que representa a mesma cultura que a cultura atual do sistema,
quaisquer personalizações estabelecidas pelo item Opções Regionais e de Idioma no
Painel de Controle serão aplicadas ao novo objeto CultureInfo. Você pode usar o
construtor CultureInfo(String, Boolean) para criar um objeto CultureInfo que não reflita
as personalizações de um sistema.
Arredondamento e cadeias de caracteres de formato de
ponto fixo
Para cadeias de caracteres de formato de ponto fixo (isto é, cadeias de caracteres de
formato que não contêm caracteres de formato de notação científica), os números são
arredondados para tantas casas decimais quanto houver espaços reservados de dígitos
à direita do ponto decimal. Se a cadeia de caracteres de formato não contiver um ponto
decimal, o número será arredondado para o inteiro mais próximo. Se o número tiver
mais dígitos do que espaços reservados de dígitos à esquerda do ponto decimal, os
dígitos extras serão copiados para a cadeia de caracteres de resultado imediatamente
antes do primeiro espaço reservado de dígito.

Voltar à tabela

Exemplo
O exemplo a seguir demonstra duas cadeias de caracteres de formato numérico
personalizado. Em ambos os casos, o espaço reservado de dígito ( # ) exibe os dados
numéricos, e todos os outros caracteres são copiados para a cadeia de caracteres de
resultado.

C#

double number1 = 1234567890;


string value1 = number1.ToString("(###) ###-####");
Console.WriteLine(value1);

int number2 = 42;


string value2 = number2.ToString("My Number = #");
Console.WriteLine(value2);
// The example displays the following output:
// (123) 456-7890
// My Number = 42

Voltar à tabela

Confira também
System.Globalization.NumberFormatInfo
Formatar tipos
Cadeias de Caracteres de Formato Numérico Padrão
Como preencher um número com zeros à esquerda
Amostra: Utilitário de Formatação do WinForms do .NET Core (C#)
Amostra: Utilitário de Formatação do WinForms do .NET Core (Visual Basic)
Cadeias de caracteres de formato de
data e hora padrão
Artigo • 05/06/2023

Uma cadeia de caracteres de formato de data e hora padrão usa um só caractere como
especificador de formato para definir a representação do texto de um valor de
DateTime ou DateTimeOffset. Qualquer cadeia de caracteres de formato de data e hora
que contenha mais de um caractere, incluindo espaço em branco, é interpretada como
uma cadeia de caracteres de formato de data e hora personalizado. Uma cadeia de
caracteres de formato padrão ou personalizado pode ser usada de duas maneiras:

Para definir a cadeia de caracteres que resulta de uma operação de formatação.

Para definir a representação de texto de um valor de data e hora que possa ser
convertido em valor de DateTime ou DateTimeOffset por uma operação de análise.

 Dica

Baixe o Utilitário de Formatação, um aplicativo do Windows Forms do .NET que


permite aplicar cadeias de caracteres de formato a valores numéricos ou de data e
hora e exibir a cadeia de caracteres de resultado. O código-fonte está disponível
para o C# e o Visual Basic.

7 Observação

Alguns dos exemplos de C# neste artigo são executados no executador de código


embutido Try.NET e no playground. Clique no botão Executar para executar um
exemplo em uma janela interativa. Ao executar o código, é possível modificá-lo e
executar o código modificado clicando em Executar novamente. O código
modificado será executado na janela interativa ou, se a compilação falhar, a janela
interativa exibirá todos as mensagens de erro do compilador C#.

O fuso horário local do executador de código embutido Try.NET e do


playground é o Tempo Universal Coordenado ou UTC. Isso pode afetar o
comportamento e a saída dos exemplos que ilustram os tipos DateTime,
DateTimeOffset e TimeZoneInfo e seus membros.

Tabela de especificadores de formato


A tabela a seguir descreve os especificadores de formato de data e hora padrão. Salvo
indicação em contrário, um determinado especificador de formato de data e hora
padrão produz uma representação de cadeia de caracteres idêntica independente de ela
ser usada com um valor DateTime ou DateTimeOffset. Confira Configurações do Painel
de Controle e Propriedades DateTimeFormatInfo para informações adicionais sobre
como usar cadeias de caracteres de formato de data e hora padrão.

Especificador Descrição Exemplos


de formato

"d" Padrão de data abreviada. 2009-06-15T13:45:30 -> 6/15/2009 (en-


US)
Mais informações:especificador de
formato de data abreviado ("d"). 2009-06-15T13:45:30 -> 15/06/2009 (fr-
FR)

2009-06-15T13:45:30 -> 2009/06/15 (ja-


JP)

"D" Padrão de data completa. 2009-06-15T13:45:30 -> Monday, June 15,


2009 (en-US)
Para saber mais:especificador de
formato de data completa ("D"). 2009-06-15T13:45:30 -> понедельник, 15
июня 2009 г. (ru-RU)

2009-06-15T13:45:30 -> Montag, 15. Juni


2009 (de-DE)

"f" Padrão de data/hora completa 2009-06-15T13:45:30 -> Monday, June 15,


(hora abreviada). 2009 1:45 PM (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> den 15 juni 2009


formato de data completa e hora 13:45 (sv-SE)
abreviada ("f").
2009-06-15T13:45:30 -> Δευτέρα, 15
Ιουνίου 2009 1:45 μμ (el-GR)

"F" Padrão de data/hora completa 2009-06-15T13:45:30 -> Monday, June 15,


(hora completa). 2009 1:45:30 PM (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> den 15 juni 2009


formato de data completa e hora 13:45:30 (sv-SE)
completa ("F").
2009-06-15T13:45:30 -> Δευτέρα, 15
Ιουνίου 2009 1:45:30 μμ (el-GR)
Especificador Descrição Exemplos
de formato

"g" Padrão geral de data/hora (hora 2009-06-15T13:45:30 -> 6/15/2009 1:45


abreviada). PM (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> 15/06/2009 13:45


formato geral de data e hora (es-ES)
abreviada ("g").
2009-06-15T13:45:30 -> 2009/6/15 13:45
(zh-CN)

"G" Padrão geral de data/hora (hora 2009-06-15T13:45:30 -> 6/15/2009 1:45:30


completa). PM (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> 15/06/2009


formato geral de data e hora 13:45:30 (es-ES)
completa ("G").
2009-06-15T13:45:30 -> 2009/6/15
13:45:30 (zh-CN)

"M", "m" Padrão de mês/dia. 2009-06-15T13:45:30 -> June 15 (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> 15. juni (da-DK)


formato de mês ("M", "m").
2009-06-15T13:45:30 -> 15 Juni (id-ID)

"O", "o" padrão de data/hora de ida e volta. Valores DateTime:

Para saber mais: especificador de 2009-06-15T13:45:30 (DateTimeKind.Local)


formato de viagem de ida e volta --> 2009-06-15T13:45:30.0000000-07:00
("O", "o").
2009-06-15T13:45:30 (DateTimeKind.Utc) -
-> 2009-06-15T13:45:30.0000000Z

2009-06-15T13:45:30
(DateTimeKind.Unspecified) --> 2009-06-
15T13:45:30.0000000

Valores DateTimeOffset:

2009-06-15T13:45:30-07:00 --> 2009-06-


15T13:45:30.0000000-07:00

"R", "r" Padrão RFC1123. DateTimeOffset input: 2009-06-


15T13:45:30 -> segunda-feira, 15 de junho
Para saber mais: especificador de de 2009 20:45:30 GMT
formato RFC1123 ("R", "r"). DateTime input: 2009-06-15T13:45:30 ->
segunda-feira, 15 de junho de 2009
13:45:30 GMT
Especificador Descrição Exemplos
de formato

"s" Padrão de data/hora classificável. 2009-06-15T13:45:30 (DateTimeKind.Local)


-> 2009-06-15T13:45:30
Para saber mais: especificador de
formato classificável ("s"). 2009-06-15T13:45:30 (DateTimeKind.Utc) -
> 2009-06-15T13:45:30

"t" Padrão de hora abreviada. 2009-06-15T13:45:30 -> 1:45 PM (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> 13:45 (hr-HR)


formato de hora abreviada ("t").
2009-06-15T13:45:30 -> 01:45 ‫( م‬ar-EG)

"T" Padrão de hora completa. 2009-06-15T13:45:30 -> 1:45:30 PM (en-


US)
Para saber mais: especificador de
formato de hora completa ("T"). 2009-06-15T13:45:30 -> 13:45:30 (hr-HR)

2009-06-15T13:45:30 -> 01:45:30 ‫( م‬ar-EG)

"u" Padrão classificável universal de Com um valor DateTime: 2009-06-


data/hora. 15T13:45:30 -> 2009-06-15 13:45:30Z

Para saber mais: especificador de Com um valor DateTimeOffset: 2009-06-


formato de padrão classificável 15T13:45:30 -> 2009-06-15 20:45:30Z
universal ("u").

"U" Padrão universal de data/hora 2009-06-15T13:45:30 -> Monday, June 15,


completa. 2009 8:45:30 PM (en-US)

Para saber mais: especificador de 2009-06-15T13:45:30 -> den 15 juni 2009


formato de padrão universal 20:45:30 (sv-SE)
completo ("U").
2009-06-15T13:45:30 -> Δευτέρα, 15
Ιουνίου 2009 8:45:30 μμ (el-GR)

"Y", "y" Padrão ano mês. 2009-06-15T13:45:30 -> June 2009 (en-
US)
Para saber mais: especificador de
formato de ano mês ("Y"). 2009-06-15T13:45:30 -> juni 2009 (da-DK)

2009-06-15T13:45:30 -> Juni 2009 (id-ID)

Qualquer Especificador desconhecido. Gera uma FormatException de tempo de


outro execução.
caractere
único
Como funcionam as cadeias de caracteres de
formato padrão
Em uma operação de formatação, uma cadeia de formato padrão é simplesmente um
alias para uma cadeia de caracteres de formato personalizado. A vantagem de usar um
alias para se referir a uma cadeia de caracteres de formato personalizado é que, embora
o alias permaneça invariável, a cadeia de caracteres de formato personalizado em si
pode variar. Isso é importante porque as representações de cadeia de caracteres de
valores de data e hora normalmente variam de acordo com a cultura. Por exemplo, a
cadeia de caracteres de formato padrão "d" indica que um valor de data e hora deve ser
exibido usando um formato de data abreviada. Para a cultura invariável, esse padrão é
"MM/dd/aaaa". Para a cultura fr-FR, ele é " dd/MM/aaaa ". Para a cultura ja-JP, ele é "
aaaa/MM/dd ".

Se uma cadeia de caracteres de formato padrão em uma operação de formatação


aponta para uma cadeia de caracteres de formato personalizado de uma cultura, seu
aplicativo pode definir a cultura específica cujas cadeias de caracteres de formato
personalizado são usadas de uma dessas maneiras:

Você pode usar a cultura padrão (ou atual). O exemplo a seguir exibe uma data
usando o formato de data abreviada da cultura atual. Nesse caso, a cultura atual é
en-US.

C#

// Display using current (en-us) culture's short date format


DateTime thisDate = new DateTime(2008, 3, 15);
Console.WriteLine(thisDate.ToString("d")); // Displays
3/15/2008

Você pode passar um objeto CultureInfo que representa a cultura cuja formatação
deve ser usada para um método que possua um parâmetro IFormatProvider. O
exemplo a seguir exibe uma data usando o formato de data abreviada da cultura
pt-BR.

C#

// Display using pt-BR culture's short date format


DateTime thisDate = new DateTime(2008, 3, 15);
CultureInfo culture = new CultureInfo("pt-BR");
Console.WriteLine(thisDate.ToString("d", culture)); // Displays
15/3/2008
Você pode passar um objeto DateTimeFormatInfo que fornece informações de
formatação para um método que possua um parâmetro IFormatProvider. O
exemplo a seguir exibe uma data usando o formato de data abreviada de um
objeto DateTimeFormatInfo para a cultura hr-HR.

C#

// Display using date format information from hr-HR culture


DateTime thisDate = new DateTime(2008, 3, 15);
DateTimeFormatInfo fmt = (new CultureInfo("hr-HR")).DateTimeFormat;
Console.WriteLine(thisDate.ToString("d", fmt)); // Displays
15.3.2008

7 Observação

Para obter informações sobre como personalizar os padrões ou as cadeias de


caracteres usados na formatação de valores de data e hora, consulte o tópico da
classe NumberFormatInfo.

Em alguns casos, a cadeia de caracteres de formato padrão funciona como uma


abreviação conveniente de uma cadeia de caracteres de formato personalizado maior
que é invariável. Quatro cadeias de caracteres de formato padrão se enquadram nesta
categoria: "O" (ou "o"), "R" (ou "r"), "s" e "u". Estas cadeias de caracteres correspondem
às cadeias de caracteres de formato personalizado definidas pela cultura invariável. Elas
produzem representações de cadeias de caracteres de valores de data e hora que são
feitos para ser idênticos entre culturas. A tabela a seguir fornece informações sobre
essas quatro cadeias de caracteres de formato de data e hora padrão.

Cadeias de caracteres Definidas pela propriedade Cadeia de caracteres de


de formato padrão DateTimeFormatInfo.InvariantInfo formato personalizado

"O" ou "o" Nenhum yyyy'-'MM'-


'dd'T'HH':'mm':'ss'.'fffffffK

"R" ou "r" RFC1123Pattern ddd, dd MMM yyyy


HH':'mm':'ss 'GMT'

"s" SortableDateTimePattern yyyy'-'MM'-


'dd'T'HH':'mm':'ss

"u" UniversalSortableDateTimePattern yyyy'-'MM'-'dd


HH':'mm':'ss'Z'

As cadeias de caracteres de formato padrão também podem ser usadas em operações


de análise com os métodos DateTime.ParseExact ou DateTimeOffset.ParseExact, o que
exige que uma cadeia de caracteres de entrada atenda com exatidão a um padrão
específico para que a operação de análise seja bem-sucedida. Muitas cadeias de
caracteres de formato padrão são mapeadas em muitas cadeias de caracteres de
formato personalizado. Assim, um valor de data e hora poderá ser representado em
vários formatos e a operação de análise ainda terá êxito. Você pode determinar a cadeia
ou as cadeias de caracteres de formato que correspondem a uma cadeia de formato
padrão ao chamar o método DateTimeFormatInfo.GetAllDateTimePatterns(Char). O
exemplo a seguir exibe as cadeias de caracteres de formato personalizado que são
mapeados para a cadeia de caracteres de formato padrão “d” (padrão de data
abreviada).

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
Console.WriteLine("'d' standard format string:");
foreach (var customString in
DateTimeFormatInfo.CurrentInfo.GetAllDateTimePatterns('d'))
Console.WriteLine(" {0}", customString);
}
}
// The example displays the following output:
// 'd' standard format string:
// M/d/yyyy
// M/d/yy
// MM/dd/yy
// MM/dd/yyyy
// yy/MM/dd
// yyyy-MM-dd
// dd-MMM-yy

As seções a seguir descrevem os especificadores de formato padrão para valores


DateTime e DateTimeOffset.

Formatos de data
Esse grupo inclui os seguintes formatos:

Especificador de formato de data abreviada ("d")


Especificador de formato de data completa ("D")
Especificador de formato de data abreviada ("d")
O especificador de formato padrão “d” representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.ShortDatePattern de uma cultura específica. Por exemplo, a cadeia
de caracteres de formato personalizado retornada pela propriedade ShortDatePattern
da cultura invariável é " MM/dd/aaaa ".

A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que controlam a


formatação da cadeia de caracteres retornada.

Propriedade Descrição

ShortDatePattern Define o formato geral da cadeia de caracteres de resultado.

DateSeparator Define a cadeia de caracteres que separa o ano, o mês e os componentes de


dia de uma data.

O exemplo a seguir utiliza o especificador de formato "d" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008,4, 10);


Console.WriteLine(date1.ToString("d", DateTimeFormatInfo.InvariantInfo));
// Displays 04/10/2008
Console.WriteLine(date1.ToString("d",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays 4/10/2008
Console.WriteLine(date1.ToString("d",
CultureInfo.CreateSpecificCulture("en-NZ")));
// Displays 10/04/2008
Console.WriteLine(date1.ToString("d",
CultureInfo.CreateSpecificCulture("de-DE")));
// Displays 10.04.2008

Voltar à tabela

Especificador de formato de data completa ("D")


O especificador de formato padrão “D” representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.LongDatePattern atual. Por exemplo, a cadeia de caracteres de
formato personalizado para a cultura invariável é "dddd, dd MMMM aaaa".
A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que controlam a
formatação da cadeia de caracteres retornada.

Propriedade Descrição

LongDatePattern Define o formato geral da cadeia de caracteres de resultado.

DayNames Define os nomes de dias localizados que podem aparecer na cadeia de


caracteres de resultado.

MonthNames Define os nomes dos meses localizados que podem aparecer na cadeia de
caracteres de resultado.

O exemplo a seguir utiliza o especificador de formato "D" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10);


Console.WriteLine(date1.ToString("D",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Thursday, April 10, 2008
Console.WriteLine(date1.ToString("D",
CultureInfo.CreateSpecificCulture("pt-BR")));
// Displays quinta-feira, 10 de abril de 2008
Console.WriteLine(date1.ToString("D",
CultureInfo.CreateSpecificCulture("es-MX")));
// Displays jueves, 10 de abril de 2008

Voltar à tabela

Formatos de data e hora


Esse grupo inclui os seguintes formatos:

Especificador de formato de data completa e hora abreviada ("f")


Especificador de formato de data completa e hora completa (“F”)
Especificador de formato de data geral e hora abreviada (“g”)
Especificador de formato de data geral e hora completa (“G”)
Especificador de formato de ida e volta ("O", "o")
Especificador de formato RFC1123 ("R", "r")
Especificador de formato classificável ("s")
Especificador de formato classificável universal ("u")
Especificador de formato completo universal ("U")
Especificador de formato de data completa e hora
abreviada ("f")
O especificador de formato padrão "f" representa uma combinação dos padrões de data
completa ("D") e hora abreviada ("t"), separados por um espaço.

A cadeia de caracteres do resultado é afetada pelas informações de formatação de um


objeto DateTimeFormatInfo específico. A tabela a seguir lista as propriedades do objeto
DateTimeFormatInfo que podem controlar a formatação da cadeia de caracteres
retornada. O especificador personalizado de formato retornado pelas propriedades
DateTimeFormatInfo.LongDatePattern e DateTimeFormatInfo.ShortTimePattern de
algumas culturas não pode usar todas as propriedades.

Propriedade Descrição

LongDatePattern Define o formato do componente de data da cadeia de caracteres de


resultado.

ShortTimePattern Define o formato do componente de hora da cadeia de caracteres de


resultado.

DayNames Define os nomes de dias localizados que podem aparecer na cadeia de


caracteres de resultado.

MonthNames Define os nomes dos meses localizados que podem aparecer na cadeia de
caracteres de resultado.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto e


segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes do
meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O exemplo a seguir utiliza o especificador de formato "f" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("f",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Thursday, April 10, 2008 6:30 AM
Console.WriteLine(date1.ToString("f",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays jeudi 10 avril 2008 06:30
Voltar à tabela

Especificador de formato de data completa e hora


completa (“F”)
O especificador de formato padrão “F” representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.FullDateTimePattern atual. Por exemplo, a cadeia de caracteres de
formato personalizado para a cultura invariável é "dddd, dd MMMM aaaa HH:mm:ss".

A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que podem


controlar a formatação da cadeia de caracteres retornada. O especificador de formato
personalizado que é retornado pela propriedade FullDateTimePattern de algumas
culturas não pode usar todas as propriedades.

Propriedade Descrição

FullDateTimePattern Define o formato geral da cadeia de caracteres de resultado.

DayNames Define os nomes de dias localizados que podem aparecer na cadeia de


caracteres de resultado.

MonthNames Define os nomes dos meses localizados que podem aparecer na cadeia de
caracteres de resultado.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto


e segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes
do meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O exemplo a seguir utiliza o especificador de formato "F" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("F",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Thursday, April 10, 2008 6:30:00 AM
Console.WriteLine(date1.ToString("F",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays jeudi 10 avril 2008 06:30:00
Voltar à tabela

Especificador de formato de data geral e hora abreviada


(“g”)
O especificador de formato padrão "g" representa uma combinação dos padrões de
data abreviada ("d") e hora abreviada ("t"), separados por um espaço.

A cadeia de caracteres do resultado é afetada pelas informações de formatação de um


objeto DateTimeFormatInfo específico. A tabela a seguir lista as propriedades do objeto
DateTimeFormatInfo que podem controlar a formatação da cadeia de caracteres
retornada. O especificador personalizado de formato que é retornado pelas
propriedades DateTimeFormatInfo.ShortDatePattern e
DateTimeFormatInfo.ShortTimePattern de algumas culturas não pode usar todas as
propriedades.

Propriedade Descrição

ShortDatePattern Define o formato do componente de data da cadeia de caracteres de


resultado.

ShortTimePattern Define o formato do componente de hora da cadeia de caracteres de


resultado.

DateSeparator Define a cadeia de caracteres que separa o ano, o mês e os componentes de


dia de uma data.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto e


segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes do
meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O exemplo a seguir utiliza o especificador de formato "g" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("g",
DateTimeFormatInfo.InvariantInfo));
// Displays 04/10/2008 06:30
Console.WriteLine(date1.ToString("g",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays 4/10/2008 6:30 AM
Console.WriteLine(date1.ToString("g",
CultureInfo.CreateSpecificCulture("fr-BE")));
// Displays 10/04/2008 6:30

Voltar à tabela

Especificador de formato de data geral e hora completa


(“G”)
O especificador de formato padrão "G" representa uma combinação dos padrões de
data abreviada ("d") e hora completa ("T"), separados por um espaço.

A cadeia de caracteres do resultado é afetada pelas informações de formatação de um


objeto DateTimeFormatInfo específico. A tabela a seguir lista as propriedades do objeto
DateTimeFormatInfo que podem controlar a formatação da cadeia de caracteres
retornada. O especificador personalizado de formato que é retornado pelas
propriedades DateTimeFormatInfo.ShortDatePattern e
DateTimeFormatInfo.LongTimePattern de algumas culturas não pode usar todas as
propriedades.

Propriedade Descrição

ShortDatePattern Define o formato do componente de data da cadeia de caracteres de


resultado.

LongTimePattern Define o formato do componente de hora da cadeia de caracteres de


resultado.

DateSeparator Define a cadeia de caracteres que separa o ano, o mês e os componentes de


dia de uma data.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto e


segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes do
meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O exemplo a seguir utiliza o especificador de formato "G" para exibir um valor de data e
hora.

C#
DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);
Console.WriteLine(date1.ToString("G",
DateTimeFormatInfo.InvariantInfo));
// Displays 04/10/2008 06:30:00
Console.WriteLine(date1.ToString("G",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays 4/10/2008 6:30:00 AM
Console.WriteLine(date1.ToString("G",
CultureInfo.CreateSpecificCulture("nl-BE")));
// Displays 10/04/2008 6:30:00

Voltar à tabela

Especificador de formato de ida e volta ("O", "o")


O especificador de formato padrão "O" ou "o" representa uma cadeia de caracteres de
data e hora personalizada usando um padrão que preserva as informações de fuso
horário e emite uma cadeia de caracteres de resultado compilada com ISO 8601. Para
valores DateTime, este especificador de formato foi projetado para manter valores de
data e hora junto à propriedade DateTime.Kind no texto. A cadeia de caracteres
formatada pode ser analisada de volta usando o método DateTime.Parse(String,
IFormatProvider, DateTimeStyles) ou DateTime.ParseExact quando o parâmetro styles
está definido como DateTimeStyles.RoundtripKind.

O especificador de formato padrão “O” ou “o” corresponde à cadeia de caracteres de


formato personalizado "aaaa'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK" para valores DateTime e
à cadeia de caracteres de formato personalizado "yyyy'-'MM'-
'dd'T'HH':'mm':'ss'.'fffffffzzz" para valores DateTimeOffset. Nesta cadeia de caracteres, os
pares de aspas simples que delimitam caracteres individuais, como hifens, dois-pontos e
a letra "T", indicam que o caractere individual é literal e não pode ser alterado. As aspas
simples em si não aparecem na cadeia de caracteres de saída.

O especificador de formato "O" ou "o" padrão (e a cadeia de caracteres de formato


personalizado "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffffffK") aproveita as três maneiras do ISO
8601 representar as informações de fuso horário para preservar a propriedade Kind dos
valores DateTime:

O componente de fuso horário dos valores de data e hora DateTimeKind.Local é


um deslocamento em relação ao UTC (por exemplo, +01:00, -07:00). Todos os
valores DateTimeOffset também estão representados nesse formato.

O componente de fuso horário dos valores de data e hora DateTimeKind.Utc usa


"Z" (que significa deslocamento zero) para representar o UTC.
Os valores de data e hora DateTimeKind.Unspecified não têm informações de fuso
horário.

Como o especificador de formato padrão "O" ou "o" está de acordo com um padrão
internacional, a operação de formatação ou análise que usa o especificador sempre usa
a cultura invariável e o calendário gregoriano.

As cadeias de caracteres passadas para os métodos Parse , TryParse , ParseExact e


TryParseExact de DateTime e DateTimeOffset podem ser analisadas usando o
especificador de formato "O" ou "o", caso estejam em um desses formatos. No caso de
objetos DateTime, a sobrecarga de análise chamada também inclui um parâmetro
styles com um valor de DateTimeStyles.RoundtripKind. Se chamar um método de

análise com a cadeia de caracteres de formato personalizado correspondente ao


especificador de formato "O" ou "o", você não terá os mesmos resultados de "O" ou "o".
Isso porque os métodos de análise que usam uma cadeia de caracteres de formato
personalizado não podem analisar a representação da cadeia de caracteres de valores
de data e hora que não tenham um componente de fuso horário ou usem "Z" para
indicar o UTC.

O exemplo a seguir usa o especificador de formato "o" para exibir uma série de valores
DateTime e um valor DateTimeOffset em um sistema no fuso horário do Pacífico dos
EUA.

C#

using System;

public class Example


{
public static void Main()
{
DateTime dat = new DateTime(2009, 6, 15, 13, 45, 30,
DateTimeKind.Unspecified);
Console.WriteLine("{0} ({1}) --> {0:O}", dat, dat.Kind);

DateTime uDat = new DateTime(2009, 6, 15, 13, 45, 30,


DateTimeKind.Utc);
Console.WriteLine("{0} ({1}) --> {0:O}", uDat, uDat.Kind);

DateTime lDat = new DateTime(2009, 6, 15, 13, 45, 30,


DateTimeKind.Local);
Console.WriteLine("{0} ({1}) --> {0:O}\n", lDat, lDat.Kind);

DateTimeOffset dto = new DateTimeOffset(lDat);


Console.WriteLine("{0} --> {0:O}", dto);
}
}
// The example displays the following output:
// 6/15/2009 1:45:30 PM (Unspecified) --> 2009-06-15T13:45:30.0000000
// 6/15/2009 1:45:30 PM (Utc) --> 2009-06-15T13:45:30.0000000Z
// 6/15/2009 1:45:30 PM (Local) --> 2009-06-15T13:45:30.0000000-07:00
//
// 6/15/2009 1:45:30 PM -07:00 --> 2009-06-15T13:45:30.0000000-07:00

O exemplo a seguir usa o especificador do formato “o” para criar uma cadeia de
caracteres formatada e, em seguida, restaurar o valor de data e hora original ao chamar
um método Parse de data e hora.

C#

// Round-trip DateTime values.


DateTime originalDate, newDate;
string dateString;
// Round-trip a local time.
originalDate = DateTime.SpecifyKind(new DateTime(2008, 4, 10, 6, 30, 0),
DateTimeKind.Local);
dateString = originalDate.ToString("o");
newDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} {1} to {2} {3}.", originalDate,
originalDate.Kind,
newDate, newDate.Kind);
// Round-trip a UTC time.
originalDate = DateTime.SpecifyKind(new DateTime(2008, 4, 12, 9, 30, 0),
DateTimeKind.Utc);
dateString = originalDate.ToString("o");
newDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} {1} to {2} {3}.", originalDate,
originalDate.Kind,
newDate, newDate.Kind);
// Round-trip time in an unspecified time zone.
originalDate = DateTime.SpecifyKind(new DateTime(2008, 4, 13, 12, 30, 0),
DateTimeKind.Unspecified);
dateString = originalDate.ToString("o");
newDate = DateTime.Parse(dateString, null, DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} {1} to {2} {3}.", originalDate,
originalDate.Kind,
newDate, newDate.Kind);

// Round-trip a DateTimeOffset value.


DateTimeOffset originalDTO = new DateTimeOffset(2008, 4, 12, 9, 30, 0, new
TimeSpan(-8, 0, 0));
dateString = originalDTO.ToString("o");
DateTimeOffset newDTO = DateTimeOffset.Parse(dateString, null,
DateTimeStyles.RoundtripKind);
Console.WriteLine("Round-tripped {0} to {1}.", originalDTO, newDTO);
// The example displays the following output:
// Round-tripped 4/10/2008 6:30:00 AM Local to 4/10/2008 6:30:00 AM
Local.
// Round-tripped 4/12/2008 9:30:00 AM Utc to 4/12/2008 9:30:00 AM Utc.
// Round-tripped 4/13/2008 12:30:00 PM Unspecified to 4/13/2008 12:30:00
PM Unspecified.
// Round-tripped 4/12/2008 9:30:00 AM -08:00 to 4/12/2008 9:30:00 AM
-08:00.

Voltar à tabela

Especificador de formato RFC1123 ("R", "r")


O especificador de formato padrão “R” ou "r" representa uma cadeia de caracteres de
formato de data e hora personalizado definida pela propriedade
DateTimeFormatInfo.RFC1123Pattern. O padrão reflete um padrão definido e a
propriedade é somente leitura. Portanto, ele é sempre o mesmo, independentemente da
cultura usada ou do provedor de formato fornecido. A cadeia de caracteres de formato
personalizado é "ddd, dd MMM aaaa HH':'mm':'ss 'GMT' ". Quando esse especificador
de formato padrão é usado, a operação de formatação ou análise sempre usa a cultura
invariável.

A cadeia de caracteres de resultado é afetada pelas seguintes propriedades do objeto


DateTimeFormatInfo retornado pela propriedade DateTimeFormatInfo.InvariantInfo que
representa a cultura invariável.

Propriedade Descrição

RFC1123Pattern Define o formato da cadeia de caracteres de resultado.

AbbreviatedDayNames Define os nomes de dias abreviados que podem aparecer na cadeia


de caracteres de resultado.

AbbreviatedMonthNames Define os nomes dos meses abreviados que podem aparecer na


cadeia de caracteres de resultado.

Embora o padrão RFC 1123 expresse a hora no formato UTC (Tempo Universal
Coordenado), a operação de formatação não altera o valor do objeto DateTime sendo
formatado. Assim, você deve converter o valor de DateTime em UTC ao chamar o
método DateTime.ToUniversalTime antes de executar a operação de formatação. Em
contraste, os valores de DateTimeOffset executam essa conversão automaticamente;
não há necessidade de chamar o método DateTimeOffset.ToUniversalTime antes da
operação de formatação.

O exemplo a seguir usa o especificador de formato "f" para exibir um valor DateTime e
um valor DateTimeOffset em um sistema no fuso horário Pacífico dos EUA.

C#
DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);
DateTimeOffset dateOffset = new DateTimeOffset(date1,
TimeZoneInfo.Local.GetUtcOffset(date1));
Console.WriteLine(date1.ToUniversalTime().ToString("r"));
// Displays Thu, 10 Apr 2008 13:30:00 GMT
Console.WriteLine(dateOffset.ToUniversalTime().ToString("r"));
// Displays Thu, 10 Apr 2008 13:30:00 GMT

Voltar à tabela

Especificador de formato classificável ("s")


O especificador de formato padrão “s" representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.SortableDateTimePattern. O padrão reflete um padrão definido
(ISO 8601) e a propriedade é somente leitura. Portanto, ele é sempre o mesmo,
independentemente da cultura usada ou do provedor de formato fornecido. A cadeia
de caracteres de formato personalizado é "aaaa'-'MM'-'dd'T'HH':'mm':'ss".

A finalidade do especificador de formato "s" é produzir cadeias de caracteres de


resultado que classificam consistentemente em ordem crescente ou decrescente com
base nos valores de data e hora. Como resultado, embora o especificador de formato
padrão "s" represente um valor de data e hora em um formato consistente, a operação
de formatação não modifica o valor do objeto de data e hora que está sendo formatado
para refletir sua propriedade DateTime.Kind ou seu valor DateTimeOffset.Offset. Por
exemplo, as cadeias de caracteres de resultado produzidas pela formatação de valores
de data e hora 2014-11-15T18:32:17+00:00 e 2014-11-15T18:32:17+08:00 são idênticas.

Quando esse especificador de formato padrão é usado, a operação de formatação ou


análise sempre usa a cultura invariável.

O exemplo a seguir usa o especificador de formato "s" para exibir um valor DateTime e
um valor DateTimeOffset em um sistema no fuso horário Pacífico dos EUA.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("s"));
// Displays 2008-04-10T06:30:00

Voltar à tabela

Especificador de formato classificável universal ("u")


O especificador de formato padrão “u" representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.UniversalSortableDateTimePattern. O padrão reflete um padrão
definido e a propriedade é somente leitura. Portanto, ele é sempre o mesmo,
independentemente da cultura usada ou do provedor de formato fornecido. A cadeia
de caracteres de formato personalizado é "aaaa'-'MM'-'dd HH':'mm':'ss'Z'". Quando esse
especificador de formato padrão é usado, a operação de formatação ou análise sempre
usa a cultura invariável.

Embora a cadeia de caracteres de resultado deva expressar a hora no formato UTC,


nenhuma conversão do valor de DateTime original é executada durante a operação de
formatação. Assim, você deve converter um valor de DateTime em UTC ao chamar o
método DateTime.ToUniversalTime antes de formatá-lo. Em contraste, os valores de
DateTimeOffset executam essa conversão automaticamente; não há necessidade de
chamar o método DateTimeOffset.ToUniversalTime antes da operação de formatação.

O exemplo a seguir utiliza o especificador de formato "u" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToUniversalTime().ToString("u"));
// Displays 2008-04-10 13:30:00Z

Voltar à tabela

Especificador de formato completo universal ("U")


O especificador de formato padrão “U” representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.FullDateTimePattern de uma cultura específica. O padrão é o
mesmo que o padrão "F". Entretanto, o valor de DateTime é automaticamente
convertido em UTC antes de ser formatado.

A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que podem


controlar a formatação da cadeia de caracteres retornada. O especificador de formato
personalizado que é retornado pela propriedade FullDateTimePattern de algumas
culturas não pode usar todas as propriedades.

Propriedade Descrição

FullDateTimePattern Define o formato geral da cadeia de caracteres de resultado.


Propriedade Descrição

DayNames Define os nomes de dias localizados que podem aparecer na cadeia de


caracteres de resultado.

MonthNames Define os nomes dos meses localizados que podem aparecer na cadeia de
caracteres de resultado.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto


e segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes
do meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O especificador de formato "U" não é suportado pelo tipo DateTimeOffset e gera uma
FormatException ao ser usado para formatar um valor DateTimeOffset.

O exemplo a seguir utiliza o especificador de formato "U" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("U",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Thursday, April 10, 2008 1:30:00 PM
Console.WriteLine(date1.ToString("U",
CultureInfo.CreateSpecificCulture("sv-FI")));
// Displays den 10 april 2008 13:30:00

Voltar à tabela

Formatos de hora
Esse grupo inclui os seguintes formatos:

Especificador de formato de hora abreviada ("t")


Especificador de formato de hora completa ("T")

Especificador de formato de hora abreviada ("t")


O especificador de formato padrão “t” representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.ShortTimePattern atual. Por exemplo, a cadeia de caracteres de
formato personalizado para a cultura invariável é "HH:mm".

A cadeia de caracteres do resultado é afetada pelas informações de formatação de um


objeto DateTimeFormatInfo específico. A tabela a seguir lista as propriedades do objeto
DateTimeFormatInfo que podem controlar a formatação da cadeia de caracteres
retornada. O especificador de formato personalizado que é retornado pela propriedade
DateTimeFormatInfo.ShortTimePattern de algumas culturas não pode usar todas as
propriedades.

Propriedade Descrição

ShortTimePattern Define o formato do componente de hora da cadeia de caracteres de


resultado.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto e


segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes do
meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O exemplo a seguir utiliza o especificador de formato "t" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("t",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays 6:30 AM
Console.WriteLine(date1.ToString("t",
CultureInfo.CreateSpecificCulture("es-ES")));
// Displays 6:30

Voltar à tabela

Especificador de formato de hora completa ("T")


O especificador de formato padrão “T” representa uma cadeia de caracteres de formato
de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.LongTimePattern de uma cultura específica. Por exemplo, a cadeia
de caracteres de formato personalizado para a cultura invariável é "HH:mm:ss".
A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que podem
controlar a formatação da cadeia de caracteres retornada. O especificador de formato
personalizado que é retornado pela propriedade DateTimeFormatInfo.LongTimePattern
de algumas culturas não pode usar todas as propriedades.

Propriedade Descrição

LongTimePattern Define o formato do componente de hora da cadeia de caracteres de


resultado.

TimeSeparator Define a cadeia de caracteres que separa os componentes de hora, minuto e


segundo de uma hora.

AMDesignator Define a cadeia de caracteres que indica as horas da meia-noite até antes do
meio-dia em um relógio de 12 horas.

PMDesignator Define a cadeia de caracteres que indica as horas do meio-dia até antes da
meia-noite em um relógio de 12 horas.

O exemplo a seguir utiliza o especificador de formato "T" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("T",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays 6:30:00 AM
Console.WriteLine(date1.ToString("T",
CultureInfo.CreateSpecificCulture("es-ES")));
// Displays 6:30:00

Voltar à tabela

Formatos de data parcial


Esse grupo inclui os seguintes formatos:

Especificador de formato de mês ("M", "m")


Especificador de formato de ano mês ("Y", "y")

Especificador de formato de mês ("M", "m")


O especificador de formato padrão “M” ou "m" representa uma cadeia de caracteres de
formato de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.MonthDayPattern atual. Por exemplo, a cadeia de caracteres de
formato personalizado para o cultura invariável é "MMMM dd".

A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que controlam a


formatação da cadeia de caracteres retornada.

Propriedade Descrição

MonthDayPattern Define o formato geral da cadeia de caracteres de resultado.

MonthNames Define os nomes dos meses localizados que podem aparecer na cadeia de
caracteres de resultado.

O exemplo a seguir utiliza o especificador de formato "m" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("m",
CultureInfo.CreateSpecificCulture("en-us")));
// Displays April 10
Console.WriteLine(date1.ToString("m",
CultureInfo.CreateSpecificCulture("ms-MY")));
// Displays 10 April

Voltar à tabela

Especificador de formato de ano mês ("Y", "y")


O especificador de formato padrão “Y” ou "y" representa uma cadeia de caracteres de
formato de data e hora personalizado que é definida pela propriedade
DateTimeFormatInfo.YearMonthPattern de uma cultura específica. Por exemplo, a cadeia
de caracteres de formato personalizado para o cultura invariável é "aaaa MMMM".

A tabela a seguir lista as propriedades do objeto DateTimeFormatInfo que controlam a


formatação da cadeia de caracteres retornada.

Propriedade Descrição

YearMonthPattern Define o formato geral da cadeia de caracteres de resultado.

MonthNames Define os nomes dos meses localizados que podem aparecer na cadeia de
caracteres de resultado.
O exemplo a seguir utiliza o especificador de formato "y" para exibir um valor de data e
hora.

C#

DateTime date1 = new DateTime(2008, 4, 10, 6, 30, 0);


Console.WriteLine(date1.ToString("Y",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays April, 2008
Console.WriteLine(date1.ToString("y",
CultureInfo.CreateSpecificCulture("af-ZA")));
// Displays April 2008

Voltar à tabela

Configurações do Painel de Controle


No Windows, as configurações no item Opções Regionais e de Idioma do Painel de
Controle influenciam a cadeia de caracteres de resultado produzida por uma operação
de formatação. Essas configurações são usadas para inicializar o objeto
DateTimeFormatInfo associado à cultura atual, a qual fornece os valores usados para
determinar a formatação. Computadores que usam configurações diferentes geram
cadeias de caracteres de resultado diferentes.

Além disso, se o constructo CultureInfo(String) for usado para criar uma instância de um
novo objeto CultureInfo que representa a mesma cultura que a cultura atual do sistema,
quaisquer personalizações estabelecidas pelo item Opções Regionais e de Idioma no
Painel de Controle serão aplicadas ao novo objeto CultureInfo. Você pode usar o
construtor CultureInfo(String, Boolean) para criar um objeto CultureInfo que não reflita
as personalizações de um sistema.

Propriedades DateTimeFormatInfo
A formatação é influenciada pelas propriedades do objeto DateTimeFormatInfo atual,
que é fornecido implicitamente pela cultura atual ou explicitamente pelo parâmetro
IFormatProvider do método que invoca a formatação. Para o parâmetro
IFormatProvider, seu aplicativo deve especificar um objeto CultureInfo, que representa
uma cultura, ou um objeto DateTimeFormatInfo, que representa as convenções de
formatação de data e hora de uma determinada cultura. Muitos dos especificadores de
formato padrão de data e hora são aliases para padrões de formatação definidos pelas
propriedades do objeto DateTimeFormatInfo atual. Seu aplicativo pode alterar o
resultado produzido por alguns especificadores de formato padrão de data e hora
alterando os padrões de formatação de data e hora correspondentes da propriedade
DateTimeFormatInfo.

Confira também
System.DateTime
System.DateTimeOffset
Formatar tipos
Cadeias de caracteres de formato de data e hora personalizado
Amostra: Utilitário de Formatação do WinForms do .NET Core (C#)
Amostra: Utilitário de Formatação do WinForms do .NET Core (Visual Basic)
Cadeias de caracteres de formato de
data e hora personalizado
Artigo • 02/06/2023

Uma cadeia de caracteres de formato de data e hora define a representação de texto de


um valor DateTime ou DateTimeOffset que é resultante de uma operação de
formatação. Ela também pode definir a representação de um valor de data e hora
necessário em uma operação de análise para converter com êxito a cadeia de caracteres
para uma data e hora. Uma cadeia de caracteres de formato personalizado consiste em
um ou mais especificadores de formato de data e hora personalizado. Qualquer cadeia
de caracteres que não é uma cadeia de caracteres de formato de data e hora padrão é
interpretada como uma cadeia de caracteres de formato de data e hora personalizado.

 Dica

Baixe o Utilitário de Formatação, um aplicativo do Windows Forms do .NET Core


que permite aplicar cadeias de caracteres de formato a valores numéricos ou de
data e hora e exibir a cadeia de caracteres de resultado. O código-fonte está
disponível para o C# e o Visual Basic.

As cadeias de caracteres de formato de data e hora personalizado podem ser usadas


tanto com valores DateTime quanto DateTimeOffset.

7 Observação

Alguns dos exemplos de C# neste artigo são executados no executador de código


embutido Try.NET e no playground. Clique no botão Executar para executar um
exemplo em uma janela interativa. Ao executar o código, é possível modificá-lo e
executar o código modificado clicando em Executar novamente. O código
modificado será executado na janela interativa ou, se a compilação falhar, a janela
interativa exibirá todos as mensagens de erro do compilador C#.

O fuso horário local do executador de código embutido Try.NET e do


playground é o Tempo Universal Coordenado ou UTC. Isso pode afetar o
comportamento e a saída dos exemplos que ilustram os tipos DateTime,
DateTimeOffset e TimeZoneInfo e seus membros.

Nas operações de formatação, as cadeias de caracteres de formato de data e hora


personalizado podem ser usadas com o método ToString de uma instância de data e
hora ou com um método que ofereça suporte a formatação de composição. O exemplo
a seguir ilustra ambos os usos.

C#

DateTime thisDate1 = new DateTime(2011, 6, 10);


Console.WriteLine("Today is " + thisDate1.ToString("MMMM dd, yyyy") + ".");

DateTimeOffset thisDate2 = new DateTimeOffset(2011, 6, 10, 15, 24, 16,


TimeSpan.Zero);
Console.WriteLine("The current date and time: {0:MM/dd/yy H:mm:ss zzz}",
thisDate2);
// The example displays the following output:
// Today is June 10, 2011.
// The current date and time: 06/10/11 15:24:16 +00:00

Em operações de análise, as cadeias de caracteres de formato de data e hora


personalizado podem ser usadas com os métodos DateTime.ParseExact,
DateTime.TryParseExact, DateTimeOffset.ParseExact e DateTimeOffset.TryParseExact.
Esses métodos exigem que uma cadeia de caracteres de entrada esteja exatamente de
acordo com um padrão específico para que a operação de análise obtenha êxito. O
exemplo a seguir ilustra uma chamada ao método DateTimeOffset.ParseExact(String,
String, IFormatProvider) para analisar uma data que deve incluir um dia, um mês e um
ano com dois dígitos.

C#

using System;
using System.Globalization;

public class Example1


{
public static void Main()
{
string[] dateValues = { "30-12-2011", "12-30-2011",
"30-12-11", "12-30-11" };
string pattern = "MM-dd-yy";
DateTime parsedDate;

foreach (var dateValue in dateValues)


{
if (DateTime.TryParseExact(dateValue, pattern, null,
DateTimeStyles.None, out parsedDate))
Console.WriteLine("Converted '{0}' to {1:d}.",
dateValue, parsedDate);
else
Console.WriteLine("Unable to convert '{0}' to a date and
time.",
dateValue);
}
}
}
// The example displays the following output:
// Unable to convert '30-12-2011' to a date and time.
// Unable to convert '12-30-2011' to a date and time.
// Unable to convert '30-12-11' to a date and time.
// Converted '12-30-11' to 12/30/2011.

A tabela a seguir descreve os especificadores de formato de data e hora personalizados


e exibe uma cadeia de caracteres de resultado produzida por cada especificador de
formato. Por padrão, as cadeias de caracteres de resultado refletem as convenções de
formatação da cultura en-US. Se um determinado especificador de formato produz uma
cadeia de caracteres de resultado localizada, o exemplo também observa a cultura à
qual a cadeia de caracteres de resultado se aplica. Para obter informações adicionais
sobre como usar cadeias de caracteres de formato data e hora personalizado, confira a
seção Observações.

Especificador Descrição Exemplos


de formato

"d" O dia do mês, de 1 a 31. 2009-06-01T13:45:30 –> 1

Mais informações: Especificador de 2009-06-15T13:45:30 –> 15


formato personalizado "d".

"dd" O dia do mês, de 01 a 31. 2009-06-01T13:45:30 –> 01

Mais informações: Especificador de 2009-06-15T13:45:30 –> 15


formato personalizado "dd".

"ddd" O nome abreviado do dia da semana. 2009-06-15T13:45:30 –> Mon (en-US)

Mais informações: Especificador de 2009-06-15T13:45:30 –> Пн (ru-RU)


formato personalizado "ddd".
2009-06-15T13:45:30 –> lun. (fr-FR)

"dddd" O nome completo do dia da semana. 2009-06-15T13:45:30 –> Monday (en-


US)
Mais informações: Especificador de
formato personalizado "dddd". 2009-06-15T13:45:30 –> понедельник
(ru-RU)

2009-06-15T13:45:30 –> lundi (fr-FR)


Especificador Descrição Exemplos
de formato

"f" Os décimos de segundo em um valor 2009-06-15T13:45:30.6170000 –> 6


de data e hora.
2009-06-15T13:45:30.05 –> 0
Mais informações: Especificador de
formato personalizado "F".

"ff" Os centésimos de segundo em um 2009-06-15T13:45:30.6170000 –> 61


valor de data e hora.
2009-06-15T13:45:30.0050000 –> 00
Mais informações: Especificador de
formato personalizado "ff".

"fff" Os milissegundos em um valor de data 6/15/2009 13:45:30.617 –> 617


e hora.
6/15/2009 13:45:30.0005 –> 000
Mais informações: Especificador de
formato personalizado "fff".

"ffff" Os décimos de milésimos de segundo 2009-06-15T13:45:30.6175000 –> 6175


em um valor de data e hora.
2009-06-15T13:45:30.0000500 –> 0000
Mais informações: Especificador de
formato personalizado "ffff".

"fffff" Os centésimos de milésimos de 2009-06-15T13:45:30.6175400 –>


segundo em um valor de data e hora. 61754

Mais informações: Especificador de 6/15/2009 13:45:30.000005 –> 00000


formato personalizado "fffff".

"ffffff" Os milionésimos de segundo em um 2009-06-15T13:45:30.6175420 –>


valor de data e hora. 617542

Mais informações: Especificador de 2009-06-15T13:45:30.0000005 –>


formato personalizado "ffffff". 000000

"fffffff" Os décimos de milionésimos de 2009-06-15T13:45:30.6175425 –>


segundo em um valor de data e hora. 6175425

Mais informações: Especificador de 2009-06-15T13:45:30.0001150 –>


formato personalizado "fffffff". 0001150

"F" Se diferente de zero, os décimos de 2009-06-15T13:45:30.6170000 –> 6


segundo em um valor de data e hora.
2009-06-15T13:45:30.0500000 –> (no
Mais informações: Especificador de output)
formato personalizado "F".
Especificador Descrição Exemplos
de formato

"FF" Se diferente de zero, os centésimos de 2009-06-15T13:45:30.6170000 –> 61


segundo em um valor de data e hora.
2009-06-15T13:45:30.0050000 –> (no
Mais informações: Especificador de output)
formato personalizado "FF".

"FFF" Se diferente de zero, os milissegundos 2009-06-15T13:45:30.6170000 –> 617


em um valor de data e hora.
2009-06-15T13:45:30.0005000 –> (no
Mais informações: Especificador de output)
formato personalizado "FFF".

"FFFF" Se diferente de zero, os décimos de 2009-06-15T13:45:30.5275000 –> 5275


milésimos de segundo em um valor de
data e hora. 2009-06-15T13:45:30.0000500 –> (no
output)
Mais informações: Especificador de
formato personalizado "FFFF".

"FFFFF" Se diferente de zero, os centésimos de 2009-06-15T13:45:30.6175400 –>


milésimos de segundo em um valor de 61754
data e hora.
2009-06-15T13:45:30.0000050 –> (no
Mais informações: Especificador de output)
formato personalizado "FFFFF".

"FFFFFF" Se diferente de zero, os milionésimos 2009-06-15T13:45:30.6175420 –>


de segundo em um valor de data e 617542
hora.
2009-06-15T13:45:30.0000005 –> (no
Mais informações: Especificador de output)
formato personalizado "FFFFFF".

"FFFFFFF" Se diferente de zero, os décimos de 2009-06-15T13:45:30.6175425 –>


milionésimos de segundo em um valor 6175425
de data e hora.
2009-06-15T13:45:30.0001150 –>
Mais informações: Especificador de 000115
formato personalizado "FFFFFFF".

"g", "gg" O período ou a era. 2009-06-15T13:45:30.6170000 –> A.D.

Mais informações: Especificador de


formato personalizado "g" ou "gg".
Especificador Descrição Exemplos
de formato

"h" A hora, usando um relógio de 12 horas 2009-06-15T01:45:30 –> 1


de 1 a 12.
2009-06-15T13:45:30 –> 1
Mais informações: Especificador de
formato personalizado "h".

"hh" A hora, usando um relógio de 12 horas 2009-06-15T01:45:30 –> 01


de 01 a 12.
2009-06-15T13:45:30 –> 01
Mais informações: Especificador de
formato personalizado "hh".

"H" A hora, usando um relógio de 24 horas 2009-06-15T01:45:30 –> 1


de 0 a 23.
2009-06-15T13:45:30 –> 13
Mais informações: Especificador de
formato personalizado "H".

"HH" A hora, usando um relógio de 24 horas 2009-06-15T01:45:30 –> 01


de 00 a 23.
2009-06-15T13:45:30 –> 13
Mais informações: Especificador de
formato personalizado "HH".

"K" Informações de fuso horário. Com valores de DateTime:

Mais informações: Especificador de 2009-06-15T13:45:30, Tipo não


formato personalizado "K". especificado –>

2009-06-15T13:45:30, Tipo Utc –> Z

2009-06-15T13:45:30, Tipo local –> –


07:00 (depende das configurações do
computador local)

Com valores de DateTimeOffset:

2009-06-15T01:45:30-07:00 --> -07:00

2009-06-15T08:45:30+00:00 -->
+00:00

"m" O minuto, de 0 a 59. 2009-06-15T01:09:30 –> 9

Mais informações: Especificador de 2009-06-15T13:29:30 –> 29


formato personalizado "M".
Especificador Descrição Exemplos
de formato

"mm" O minuto, de 00 a 59. 2009-06-15T01:09:30 –> 09

Mais informações: Especificador de 2009-06-15T01:45:30 –> 45


formato personalizado "MM".

“M” O mês, de 1 a 12. 2009-06-15T13:45:30 –> 6

Mais informações: Especificador de


formato personalizado "M".

"MM" O mês, de 01 a 12. 2009-06-15T13:45:30 –> 06

Mais informações: Especificador de


formato personalizado "MM".

"MMM" O nome do mês abreviado. 2009-06-15T13:45:30 –> Jun (en-US)

Mais informações: Especificador de 2009-06-15T13:45:30 –> juin (fr-FR)


formato personalizado "MMM".
2009-06-15T13:45:30 –> Jun (zu-ZA)

"MMMM" O nome completo do mês. 2009-06-15T13:45:30 –> June (en-US)

Mais informações: Especificador de 2009-06-15T13:45:30 –> juni (da-DK)


formato personalizado "MMMM".
2009-06-15T13:45:30 –> uJuni (zu-ZA)

"s" O segundo, de 0 a 59. 2009-06-15T13:45:09 –> 9

Mais informações: Especificador de


formato personalizado "s".

"ss" O segundo, de 00 a 59. 2009-06-15T13:45:09 –> 09

Mais informações: Especificador de


formato personalizado "ss".

"t" O primeiro caractere do designador 2009-06-15T13:45:30 –> P (en-US)


AM/PM.
2009-06-15T13:45:30 –> 午 (ja-JP)
Mais informações: Especificador de
formato personalizado "t". 2009-06-15T13:45:30 –> (fr-FR)
Especificador Descrição Exemplos
de formato

"tt" O designador AM/PM. 2009-06-15T13:45:30 –> PM (en-US)

Mais informações: Especificador de 2009-06-15T13:45:30 –> 午後 (ja-JP)


formato personalizado "tt".
2009-06-15T13:45:30 –> (fr-FR)

"y" O ano, de 0 a 99. 0001-01-01T00:00:00 –> 1

Mais informações: Especificador de 0900-01-01T00:00:00 –> 0


formato personalizado "y".
1900-01-01T00:00:00 –> 0

2009-06-15T13:45:30 –> 9

2019-06-15T13:45:30 –> 19

"yy" O ano, de 00 a 99. 0001-01-01T00:00:00 –> 01

Mais informações: Especificador de 0900-01-01T00:00:00 –> 00


formato personalizado "yy".
1900-01-01T00:00:00 –> 00

2019-06-15T13:45:30 –> 19

"yyy" O ano, com um mínimo de três dígitos. 0001-01-01T00:00:00 –> 001

Mais informações: Especificador de 0900-01-01T00:00:00 –> 900


formato personalizado "yyy".
1900-01-01T00:00:00 –> 1900

2009-06-15T13:45:30 –> 2009

"yyyy" O ano como um número de quatro 0001-01-01T00:00:00 –> 0001


dígitos.
0900-01-01T00:00:00 –> 0900
Mais informações: Especificador de
formato personalizado "yyyy". 1900-01-01T00:00:00 –> 1900

2009-06-15T13:45:30 –> 2009

"yyyyy" O ano como um número de cinco 0001-01-01T00:00:00 –> 00001


dígitos.
2009-06-15T13:45:30 –> 02009
Mais informações: Especificador de
formato personalizado "yyyyy".
Especificador Descrição Exemplos
de formato

"z" Diferença de horas em relação ao UTC, 2009-06-15T13:45:30-07:00 –> –7


sem zeros à esquerda.

Mais informações: Especificador de


formato personalizado "z".

"zz" Diferença de horas em relação ao UTC, 2009-06-15T13:45:30-07:00 –> –07


com um zero à esquerda para um valor
de dígito único.

Mais informações: Especificador de


formato personalizado "zz".

"zzz" Diferença de horas e minutos em 2009-06-15T13:45:30-07:00 –> –07:00


relação ao UTC.

Mais informações: Especificador de


formato personalizado "zzz".

":" O separador de hora. 2009-06-15T13:45:30 –> : (en-US)

Mais informações: Especificador de 2009-06-15T13:45:30 –> . (it-IT)


formato personalizado ":".
2009-06-15T13:45:30 –> : (ja-JP)

"/" O separador de data. 2009-06-15T13:45:30 –> / (en-US)

Mais informações: Especificador de 2009-06-15T13:45:30 –> – (ar-DZ)


formato personalizado "/".
2009-06-15T13:45:30 –> . (tr-TR)

"string" Delimitador de cadeia de caracteres 2009-06-15T13:45:30 ("arr:" h:m t) –>


literal. arr: 1:45 P
'string'
Para saber mais: Literais de cadeia de 2009-06-15T13:45:30 ('arr:' h:m t) –>
caracteres. arr: 1:45 P

% Define o caractere seguinte como um 2009-06-15T13:45:30 (%h) –> 1


especificador de formato
personalizado.

Mais informações:Usar especificadores


de formato personalizado simples.
Especificador Descrição Exemplos
de formato

\ O caractere de escape. 2009-06-15T13:45:30 (h \h) –> 1 h

Para saber mais: Literais de cadeia de


caracteres e Como usar o caractere de
escape.

Qualquer O caractere é copiado, inalterado, para 2009-06-15T01:45:30 (arr hh:mm t) –>


outro a cadeia de caracteres de resultado. arr 01:45 A
caractere
Para saber mais: Literais de cadeia de
caracteres.

As seções a seguir oferecem informações adicionais sobre cada especificador de


formato de data e hora personalizado. A menos que observado do contrário, cada
especificador produz uma representação de cadeia de caracteres idêntica independente
de ela ser usada com um valor DateTime ou um valor DateTimeOffset.

Especificador de formato de dia “d”

Especificador de formato personalizado "d"


O especificador de formato personalizado "d" representa o dia do mês como um
número de 1 a 31. Dias de dígito único são formatados sem um zero à esquerda.

Se o especificador de formato "d" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "d". Para saber mais sobre como usar um especificador de formato único, confira
Usar especificadores de formato único personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "d" em várias


cadeias de caracteres de formato.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15);

Console.WriteLine(date1.ToString("d, M",
CultureInfo.InvariantCulture));
// Displays 29, 8

Console.WriteLine(date1.ToString("d MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays 29 August
Console.WriteLine(date1.ToString("d MMMM",
CultureInfo.CreateSpecificCulture("es-MX")));
// Displays 29 agosto

Voltar à tabela

Especificador de formato personalizado "dd"


A cadeia de caracteres de formato personalizado "dd" representa o dia do mês como
um número de 01 a 31. Dias de dígito único são formatados com um zero à esquerda.

O exemplo a seguir inclui o especificador de formato personalizado "dd" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 1, 2, 6, 30, 15);

Console.WriteLine(date1.ToString("dd, MM",
CultureInfo.InvariantCulture));
// 02, 01

Voltar à tabela

Especificador de formato personalizado "ddd"


O especificador de formato personalizado "ddd" representa o nome do dia da semana
abreviado. O nome do dia da semana localizado abreviado é recuperado da
propriedade DateTimeFormatInfo.AbbreviatedDayNames da cultura atual ou
especificada.

O exemplo a seguir inclui o especificador de formato personalizado "ddd" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15);

Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Fri 29 Aug
Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays ven. 29 août
Voltar à tabela

Especificador de formato personalizado "dddd"


O especificador de formato personalizado "dddd" (mais um número qualquer de
especificadores "d" adicionais) representa o nome completo do dia da semana. O nome
do dia da semana localizado é recuperado da propriedade
DateTimeFormatInfo.DayNames da cultura atual ou especificada.

O exemplo a seguir inclui o especificador de formato personalizado "dddd" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15);

Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Friday 29 August
Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("it-IT")));
// Displays venerdì 29 agosto

Voltar à tabela

Especificador de fração “f” de segundos em


minúsculas

Especificador de formato personalizado "f"


O especificador de formato personalizado "f" representa o dígito mais significativo da
fração de segundos, ou seja, representa os décimos de segundo em um valor de data e
hora.

Se o especificador de formato "f" for usado sem outros especificadores de formato, ele
será interpretado como o especificador padrão de formato de data e hora "f". Para saber
mais sobre como usar um especificador de formato único, confira Usar especificadores
de formato único personalizados posteriormente nesse artigo.

Quando você usa especificadores de formato "f" como parte de uma cadeia de
caracteres de formato fornecida para o método ParseExact, TryParseExact, ParseExact ou
TryParseExact, o número de especificadores de formato "f" indica o número de dígitos
mais significativos da fração de segundos que deve estar presente para analisar a cadeia
de caracteres com sucesso.

O exemplo a seguir inclui o especificador de formato personalizado "f" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15, 18);


CultureInfo ci = CultureInfo.InvariantCulture;

Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018

Voltar à tabela

Especificador de formato personalizado "ff"


O especificador de formato personalizado "ff" representa os dois dígitos mais
significativos da fração de segundos, ou seja, ele representa os centésimos de segundo
em um valor de data e hora.

O exemplo a seguir inclui o especificador de formato personalizado "ff" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15, 18);


CultureInfo ci = CultureInfo.InvariantCulture;

Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018

Voltar à tabela

Especificador de formato personalizado "fff"


O especificador de formato personalizado "fff" representa os três dígitos mais
significativos da fração de segundos, ou seja, ele representa os milissegundos em um
valor de data e hora.

O exemplo a seguir inclui o especificador de formato personalizado "fff" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15, 18);


CultureInfo ci = CultureInfo.InvariantCulture;

Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018

Voltar à tabela

Especificador de formato personalizado "ffff"


O especificador de formato personalizado "ffff" representa os quatro dígitos mais
significativos da fração de segundos, ou seja, ele representa os décimos de milésimos
de um segundo em um valor de data e hora.

Embora seja possível exibir os décimos de milésimos de um componente de segundos


de um valor temporal, esse valor pode não ser significativo. A precisão dos valores de
data e hora depende da resolução do relação ao relógio do sistema. Nos sistemas
operacionais Windows NT versão 3.5 (e posterior) e Windows Vista, a resolução do
relógio é de aproximadamente 10 a 15 milissegundos.
Voltar à tabela

Especificador de formato personalizado "fffff"


O especificador de formato personalizado "fffff" representa os cinco dígitos mais
significativos da fração de segundos, ou seja, ele representa os centésimos de milésimos
de um segundo em um valor de data e hora.

Embora seja possível exibir os centésimos de milésimos de um componente de


segundos de um valor temporal, esse valor pode não ser significativo. A precisão dos
valores de data e hora depende da resolução do relação ao relógio do sistema. Nos
sistemas operacionais Windows NT 3.5 (e posterior) e Windows Vista, a resolução do
relógio é de aproximadamente 10 a 15 milissegundos.

Voltar à tabela

Especificador de formato personalizado "ffffff"


O especificador de formato personalizado "ffffff" representa os seis dígitos mais
significativos da fração de segundos, ou seja, ele representa os milionésimos de um
segundo em um valor de data e hora.

Embora seja possível exibir os milionésimos de um componente de segundos de um


valor temporal, esse valor pode não ser significativo. A precisão dos valores de data e
hora depende da resolução do relação ao relógio do sistema. Nos sistemas operacionais
Windows NT 3.5 (e posterior) e Windows Vista, a resolução do relógio é de
aproximadamente 10 a 15 milissegundos.

Voltar à tabela

Especificador de formato personalizado "fffffff"


O especificador de formato personalizado "fffffff" representa os sete dígitos mais
significativos da fração de segundos; ou seja, representa os décimos de milionésimos de
segundo em um valor de data e hora.

Embora seja possível exibir os décimos de milionésimos de um componente de


segundos de um valor temporal, esse valor pode não ser significativo. A precisão dos
valores de data e hora depende da resolução do relação ao relógio do sistema. Nos
sistemas operacionais Windows NT 3.5 (e posterior) e Windows Vista, a resolução do
relógio é de aproximadamente 10 a 15 milissegundos.
Voltar à tabela

Especificador de fração “f” de segundos em


maiúsculas

Especificador de formato personalizado "F"


O especificador de formato personalizado "F" representa o dígito mais significativo da
fração de segundos, ou seja, representa os décimos de segundo em um valor de data e
hora. Nada será exibido se o dígito for zero e o ponto decimal que segue o número de
segundos também não for exibido.

Se o especificador de formato "F" for usado sem outros especificadores de formato, ele
será interpretado como o especificador padrão de formato de data e hora "F". Para
saber mais sobre como usar um especificador de formato único, confira Usar
especificadores de formato único personalizados posteriormente nesse artigo.

O número de especificadores de formato "F" usados com o método ParseExact,


TryParseExact, ParseExact ou TryParseExact indica o número máximo de dígitos
significativos da fração de segundos que podem estar presentes para que a análise da
cadeia de caracteres seja feita com êxito.

O exemplo a seguir inclui o especificador de formato personalizado "F" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15, 18);


CultureInfo ci = CultureInfo.InvariantCulture;

Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018

Voltar à tabela
Especificador de formato personalizado "FF"
O especificador de formato personalizado "FF" representa os dois dígitos mais
significativos da fração de segundos, ou seja, ele representa os centésimos de segundo
em um valor de data e hora. Zeros à direita não são exibidos. Nada será exibido se os
dois dígitos significativos forem zero e, nesse caso, o ponto decimal que segue o
número de segundos também não for exibido.

O exemplo a seguir inclui o especificador de formato personalizado "FF" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15, 18);


CultureInfo ci = CultureInfo.InvariantCulture;

Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018

Voltar à tabela

Especificador de formato personalizado "FFF"


O especificador de formato personalizado "FFF" representa os três dígitos mais
significativos da fração de segundos, ou seja, ele representa os milissegundos em um
valor de data e hora. Zeros à direita não são exibidos. Nada será exibido se os três
dígitos significativos forem zero e, nesse caso, o ponto decimal que segue o número de
segundos também não for exibido.

O exemplo a seguir inclui o especificador de formato personalizado "FFF" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15, 18);


CultureInfo ci = CultureInfo.InvariantCulture;
Console.WriteLine(date1.ToString("hh:mm:ss.f", ci));
// Displays 07:27:15.0
Console.WriteLine(date1.ToString("hh:mm:ss.F", ci));
// Displays 07:27:15
Console.WriteLine(date1.ToString("hh:mm:ss.ff", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.FF", ci));
// Displays 07:27:15.01
Console.WriteLine(date1.ToString("hh:mm:ss.fff", ci));
// Displays 07:27:15.018
Console.WriteLine(date1.ToString("hh:mm:ss.FFF", ci));
// Displays 07:27:15.018

Voltar à tabela

Especificador de formato personalizado "FFFF"


O especificador de formato personalizado "FFFF" representa os quatro dígitos mais
significativos da fração de segundos, ou seja, ele representa os décimos de milésimos
de um segundo em um valor de data e hora. Zeros à direita não são exibidos. Nada será
exibido se os quatro dígitos significativos forem zero e, nesse caso, o ponto decimal que
segue o número de segundos também não for exibido.

Embora seja possível exibir os décimos de milésimos de um componente de segundos


de um valor temporal, esse valor pode não ser significativo. A precisão dos valores de
data e hora depende da resolução do relação ao relógio do sistema. Nos sistemas
operacionais Windows NT 3.5 (e posterior) e Windows Vista, a resolução do relógio é de
aproximadamente 10 a 15 milissegundos.

Voltar à tabela

Especificador de formato personalizado "FFFFF"


O especificador de formato personalizado "FFFFF" representa os cinco dígitos mais
significativos da fração de segundos, ou seja, ele representa os centésimos de milésimos
de um segundo em um valor de data e hora. Zeros à direita não são exibidos. Nada será
exibido se os cinco dígitos significativos forem zero e, nesse caso, o ponto decimal que
segue o número de segundos também não for exibido.

Embora seja possível exibir os centésimos de milésimos de um componente de


segundos de um valor temporal, esse valor pode não ser significativo. A precisão dos
valores de data e hora depende da resolução do relação ao relógio do sistema. Nos
sistemas operacionais Windows NT 3.5 (e posterior) e Windows Vista, a resolução do
relógio é de aproximadamente 10 a 15 milissegundos.

Voltar à tabela

Especificador de formato personalizado "FFFFFF"


O especificador de formato personalizado "FFFFFF" representa os seis dígitos mais
significativos da fração de segundos, ou seja, ele representa os milionésimos de um
segundo em um valor de data e hora. Zeros à direita não são exibidos. Nada será
exibido se os seis dígitos significativos forem zero e, nesse caso, o ponto decimal que
segue o número de segundos também não for exibido.

Embora seja possível exibir os milionésimos de um componente de segundos de um


valor temporal, esse valor pode não ser significativo. A precisão dos valores de data e
hora depende da resolução do relação ao relógio do sistema. Nos sistemas operacionais
Windows NT 3.5 (e posterior) e Windows Vista, a resolução do relógio é de
aproximadamente 10 a 15 milissegundos.

Voltar à tabela

Especificador de formato personalizado "FFFFFFF"


O especificador de formato personalizado "FFFFFFF" representa os sete dígitos mais
significativos da fração de segundos; ou seja, representa os décimos de milionésimos de
segundo em um valor de data e hora. Zeros à direita não são exibidos. Nada será
exibido se os sete dígitos significativos forem zero e, nesse caso, o ponto decimal que
segue o número de segundos também não for exibido.

Embora seja possível exibir os décimos de milionésimos de um componente de


segundos de um valor temporal, esse valor pode não ser significativo. A precisão dos
valores de data e hora depende da resolução do relação ao relógio do sistema. Nos
sistemas operacionais Windows NT 3.5 (e posterior) e Windows Vista, a resolução do
relógio é de aproximadamente 10 a 15 milissegundos.

Voltar à tabela

Especificador de formato incorreto “g” era

Especificador de formato personalizado "g" ou "gg"


Os especificadores de formato personalizado “g” ou “gg” (mais qualquer número de
especificadores “g” adicionais) representam o período ou era, como A.D. A operação de
formatação ignorará esse especificador se a data a ser formatada não tiver um período
ou uma cadeia de caracteres de era associada.

Se o especificador de formato "g" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "g". Para saber mais sobre como usar um especificador de formato único, confira
Usar especificadores de formato único personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "g" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(70, 08, 04);

Console.WriteLine(date1.ToString("MM/dd/yyyy g",
CultureInfo.InvariantCulture));
// Displays 08/04/0070 A.D.
Console.WriteLine(date1.ToString("MM/dd/yyyy g",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays 08/04/0070 ap. J.-C.

Voltar à tabela

Especificador de formato “h” de hora em


minúsculas

Especificador de formato personalizado "h"


O especificador de formato personalizado "h" representa a hora como um número de 1
a 12, ou seja, a hora é representada por um relógio de 12 horas que conta todas as
horas desde a meia-noite. Uma hora específica após a meia-noite é indistinguível da
mesma hora depois do meio-dia. A hora não é arredondada e uma hora de dígito único
é formatada sem um zero à esquerda. Por exemplo, considerando a hora 5:43 da manhã
ou da tarde, este especificador de formato personalizado exibe “5".

Se o especificador de formato "h" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão e gerará uma FormatException. Para saber mais sobre como usar um
especificador de formato único, confira Usar especificadores de formato único
personalizados posteriormente nesse artigo.
O exemplo a seguir inclui o especificador de formato personalizado "h" em uma cadeia
de caracteres de formato personalizado.

C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ

Voltar à tabela

Especificador de formato personalizado "hh"


O especificador de formato personalizado "hh" (mais qualquer número de
especificadores "h" adicionais) representa a hora como um número de 01 a 12, ou seja,
a hora é representada por um relógio de 12 horas que conta todas as horas desde a
meia-noite ou o meio-dia. Uma hora específica após a meia-noite é indistinguível da
mesma hora depois do meio-dia. A hora não é arredondada e uma hora de dígito único
é formatada com um zero à esquerda. Por exemplo, considerando a hora 5:43 da manhã
ou da tarde, este especificador de formato exibe “05".

O exemplo a seguir inclui o especificador de formato personalizado "hh" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.

Voltar à tabela

Especificador de formato “h” de hora em


maiúsculas

Especificador de formato personalizado "H"


O especificador de formato personalizado "H" representa a hora como um número de 0
a 23; ou seja, a hora é representada por um relógio de 24 horas baseado em zero que
conta todas as horas desde a meia-noite. Uma hora de dígito único é formatada sem
um zero à esquerda.

Se o especificador de formato "H" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão e gerará uma FormatException. Para saber mais sobre como usar um
especificador de formato único, confira Usar especificadores de formato único
personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "H" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 1, 1, 6, 9, 1);


Console.WriteLine(date1.ToString("H:mm:ss",
CultureInfo.InvariantCulture));
// Displays 6:09:01

Voltar à tabela

Especificador de formato personalizado "HH"


O especificador de formato personalizado "HH" (mais qualquer número de
especificadores "H" adicionais) representa a hora como um número de 00 a 23; ou seja,
a hora é representada por um relógio de 24 horas baseado em zero que conta todas as
horas desde a meia-noite. Uma hora de dígito único é formatada com um zero à
esquerda.

O exemplo a seguir inclui o especificador de formato personalizado "HH" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 1, 1, 6, 9, 1);


Console.WriteLine(date1.ToString("HH:mm:ss",
CultureInfo.InvariantCulture));
// Displays 06:09:01

Voltar à tabela

Especificador de formato “K” de fuso horário

Especificador de formato personalizado "K"


O especificador de formato personalizado "K" representa as informações de fuso horário
de um valor temporal. Quando esse especificador de formato é usado com valores
DateTime, a cadeia de caracteres de resultado é definida pelo valor da propriedade
DateTime.Kind:

Para o fuso horário local (um valor da propriedade DateTime.Kind de


DateTimeKind.Local), esse especificador produz uma cadeia de caracteres
resultante que contém a diferença local em relação ao Horário Universal
Coordenado (UTC). Por exemplo, “-07h00”.

Para uma hora UTC (um valor da propriedade DateTime.Kind de


DateTimeKind.Utc), a cadeia de caracteres de resultado inclui um caractere "Z"
para representar uma data UTC.

Para um horário de um fuso horário não especificado (um horário cuja


propriedade DateTime.Kind é igual a DateTimeKind.Unspecified), o resultado é
equivalente a String.Empty.

Para valores DateTimeOffset, o especificador de formato "K" é equivalente ao


especificador de formato "zzz" e produz uma cadeia de caracteres resultante que
contém a diferença em relação ao valor de DateTimeOffset do UTC.

Se o especificador de formato "K" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão e gerará uma FormatException. Para saber mais sobre como usar um
especificador de formato único, confira Usar especificadores de formato único
personalizados posteriormente nesse artigo.

O exemplo a seguir exibe a cadeia de caracteres resultante do uso do especificador de


formato personalizado "K" com vários valores DateTime e DateTimeOffset em um
sistema no fuso horário padrão do Pacífico dos EUA.

C#

Console.WriteLine(DateTime.Now.ToString("%K"));
// Displays -07:00
Console.WriteLine(DateTime.UtcNow.ToString("%K"));
// Displays Z
Console.WriteLine("'{0}'",
DateTime.SpecifyKind(DateTime.Now,
DateTimeKind.Unspecified).ToString("%K"));
// Displays ''
Console.WriteLine(DateTimeOffset.Now.ToString("%K"));
// Displays -07:00
Console.WriteLine(DateTimeOffset.UtcNow.ToString("%K"));
// Displays +00:00
Console.WriteLine(new DateTimeOffset(2008, 5, 1, 6, 30, 0,
new TimeSpan(5, 0, 0)).ToString("%K"));
// Displays +05:00

Voltar à tabela

Especificador de formato “m” de minuto

Especificador de formato personalizado "m"


O especificador de formato personalizado "m" representa o minuto como um número
de 0 a 59. O minuto representa os minutos inteiros decorridos desde a última hora. Um
minuto de dígito único é formatado sem um zero à esquerda.

Se o especificador de formato "m" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "m". Para saber mais sobre como usar um especificador de formato único,
confira Usar especificadores de formato único personalizados posteriormente nesse
artigo.

O exemplo a seguir inclui o especificador de formato personalizado "m" em uma cadeia


de caracteres de formato personalizado.
C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ

Voltar à tabela

Especificador de formato personalizado "mm"


O especificador de formato personalizado "mm" (mais qualquer número de
especificadores "m" adicionais) representam o minuto como um número de 00 a 59. O
minuto representa os minutos inteiros decorridos desde a última hora. Um minuto de
dígito único é formatado com um zero à esquerda.

O exemplo a seguir inclui o especificador de formato personalizado "mm" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.

Voltar à tabela
Especificador de formato “M” de mês

Especificador de formato personalizado "M"


O especificador de formato personalizado "M" representa o mês como um número de 1
a 12 (ou de 1 a 13 para os calendários com 13 meses). Um mês de dígito único é
formatado sem um zero à esquerda.

Se o especificador de formato "M" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "M". Para saber mais sobre como usar um especificador de formato único,
confira Usar especificadores de formato único personalizados posteriormente nesse
artigo.

O exemplo a seguir inclui o especificador de formato personalizado "M" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 18);


Console.WriteLine(date1.ToString("(M) MMM, MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays (8) Aug, August
Console.WriteLine(date1.ToString("(M) MMM, MMMM",
CultureInfo.CreateSpecificCulture("nl-NL")));
// Displays (8) aug, augustus
Console.WriteLine(date1.ToString("(M) MMM, MMMM",
CultureInfo.CreateSpecificCulture("lv-LV")));
// Displays (8) Aug, augusts

Voltar à tabela

Especificador de formato personalizado "MM"


O especificador de formato personalizado "MM" representa o mês como um número de
01 a 12 (ou de 1 a 13 para os calendários com 13 meses). Um mês de dígito único é
formatado com um zero à esquerda.

O exemplo a seguir inclui o especificador de formato personalizado "MM" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 1, 2, 6, 30, 15);


Console.WriteLine(date1.ToString("dd, MM",
CultureInfo.InvariantCulture));
// 02, 01

Voltar à tabela

Especificador de formato personalizado "MMM"


O especificador de formato personalizado "MMM" representa o nome do mês
abreviado. O nome do dia do mês localizado abreviado é recuperado da propriedade
DateTimeFormatInfo.AbbreviatedMonthNames da cultura atual ou especificada.

O exemplo a seguir inclui o especificador de formato personalizado "MMM" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15);

Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Fri 29 Aug
Console.WriteLine(date1.ToString("ddd d MMM",
CultureInfo.CreateSpecificCulture("fr-FR")));
// Displays ven. 29 août

Voltar à tabela

Especificador de formato personalizado "MMMM"


O especificador de formato personalizado "MMMM" representa o nome do mês
completo. O nome do mês localizado é recuperado da propriedade
DateTimeFormatInfo.MonthNames da cultura atual ou especificada.

O exemplo a seguir inclui o especificador de formato personalizado "MMMM" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(2008, 8, 29, 19, 27, 15);

Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("en-US")));
// Displays Friday 29 August
Console.WriteLine(date1.ToString("dddd dd MMMM",
CultureInfo.CreateSpecificCulture("it-IT")));
// Displays venerdì 29 agosto

Voltar à tabela

Especificador de formato “s” de segundos

Especificador de formato personalizado "s"


O especificador de formato personalizado "s" representa os segundos como um número
de 0 a 59. O resultado representa os segundos inteiros decorridos desde o último
minuto. Um segundo de dígito único é formatado sem um zero à esquerda.

Se o especificador de formato "s" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "s". Para saber mais sobre como usar um especificador de formato único, confira
Usar especificadores de formato único personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "s" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ

Voltar à tabela

Especificador de formato personalizado "ss"


O especificador de formato personalizado "ss" (mais qualquer número de
especificadores "s" adicionais) representa os segundos como um número de 00 a 59. O
resultado representa os segundos inteiros decorridos desde o último minuto. Um
segundo de dígito único é formatado com um zero à esquerda.

O exemplo a seguir inclui o especificador de formato personalizado "ss" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.

Voltar à tabela

Especificador de formato “t” de meridiano

Especificador de formato personalizado "t"


O especificador de formato personalizado "t" representa o primeiro caractere do
designador AM/PM. O designador localizado apropriado é recuperado da propriedade
DateTimeFormatInfo.AMDesignator ou DateTimeFormatInfo.PMDesignator da cultura
atual ou específica. O designador AM é usado para todas as horas de 0:00:00 (meia-
noite) até 11:59:59,999. O designador PM é usado para todas as horas de 12:00:00
(meio-dia) até 23:59:59,999.

Se o especificador de formato "t" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "t". Para saber mais sobre como usar um especificador de formato único, confira
Usar especificadores de formato único personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "t" em uma cadeia


de caracteres de formato personalizado.
C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1 µ
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.InvariantCulture));
// Displays 6:9:1.5 P
Console.WriteLine(date1.ToString("h:m:s.F t",
CultureInfo.CreateSpecificCulture("el-GR")));
// Displays 6:9:1.5 µ

Voltar à tabela

Especificador de formato personalizado "tt"


O especificador de formato personalizado "tt" (mais qualquer número de
especificadores "t" adicionais) representa o designador AM/PM inteiro. O designador
localizado apropriado é recuperado da propriedade DateTimeFormatInfo.AMDesignator
ou DateTimeFormatInfo.PMDesignator da cultura atual ou específica. O designador AM
é usado para todas as horas de 0:00:00 (meia-noite) até 11:59:59,999. O designador PM
é usado para todas as horas de 12:00:00 (meio-dia) até 23:59:59,999.

Certifique-se de usar o especificador "tt" para linguagens para as quais é necessário


manter a distinção entre AM e PM. Um exemplo é o idioma japonês, no qual os
designadores AM e PM diferem no segundo caractere e não no primeiro.

O exemplo a seguir inclui o especificador de formato personalizado "tt" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1;
date1 = new DateTime(2008, 1, 1, 18, 9, 1);
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01 PM
Console.WriteLine(date1.ToString("hh:mm:ss tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01 du.
date1 = new DateTime(2008, 1, 1, 18, 9, 1, 500);
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.InvariantCulture));
// Displays 06:09:01.50 PM
Console.WriteLine(date1.ToString("hh:mm:ss.ff tt",
CultureInfo.CreateSpecificCulture("hu-HU")));
// Displays 06:09:01.50 du.

Voltar à tabela

Especificador de formato “y” de ano

Especificador de formato personalizado "y"


O especificador de formato personalizado "y" representa o ano como um número de
um dígito ou de dois dígitos. Se o ano tiver mais que dois dígitos, somente os dois
dígitos de ordem baixa aparecerão no resultado. Se o primeiro dígito de um ano de dois
dígitos começa com zero (por exemplo, 2008), o número é formatado sem um zero à
esquerda.

Se o especificador de formato "y" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão "y". Para saber mais sobre como usar um especificador de formato único, confira
Usar especificadores de formato único personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "y" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(1, 12, 1);


DateTime date2 = new DateTime(2010, 1, 1);
Console.WriteLine(date1.ToString("%y"));
// Displays 1
Console.WriteLine(date1.ToString("yy"));
// Displays 01
Console.WriteLine(date1.ToString("yyy"));
// Displays 001
Console.WriteLine(date1.ToString("yyyy"));
// Displays 0001
Console.WriteLine(date1.ToString("yyyyy"));
// Displays 00001
Console.WriteLine(date2.ToString("%y"));
// Displays 10
Console.WriteLine(date2.ToString("yy"));
// Displays 10
Console.WriteLine(date2.ToString("yyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyyy"));
// Displays 02010

Voltar à tabela

Especificador de formato personalizado "yy"


O especificador de formato personalizado "yy" representa o ano como um número de
dois dígitos. Se o ano tiver mais que dois dígitos, somente os dois dígitos de ordem
baixa aparecerão no resultado. Se o ano de dois dígitos tiver menos de dois dígitos
significativos, o número será preenchido com zeros à esquerda para produzir dois
dígitos.

Em uma operação de análise, um ano de dois dígitos é analisado usando o especificador


de formato personalizado “yy” interpretado com base na propriedade
Calendar.TwoDigitYearMax do calendário atual do provedor de formato. O exemplo a
seguir analisa a representação de cadeia de caracteres de uma data com um ano de
dois dígitos usando o calendário gregoriano padrão da cultura en-US que, neste caso, é
a cultura atual. Ele então altera o objeto CultureInfo da cultura atual para usar um
objeto GregorianCalendar cuja propriedade TwoDigitYearMax foi modificada.

C#

using System;
using System.Globalization;
using System.Threading;

public class Example7


{
public static void Main()
{
string fmt = "dd-MMM-yy";
string value = "24-Jan-49";

Calendar cal =
(Calendar)CultureInfo.CurrentCulture.Calendar.Clone();
Console.WriteLine("Two Digit Year Range: {0} - {1}",
cal.TwoDigitYearMax - 99, cal.TwoDigitYearMax);

Console.WriteLine("{0:d}", DateTime.ParseExact(value, fmt, null));


Console.WriteLine();

cal.TwoDigitYearMax = 2099;
CultureInfo culture =
(CultureInfo)CultureInfo.CurrentCulture.Clone();
culture.DateTimeFormat.Calendar = cal;
Thread.CurrentThread.CurrentCulture = culture;
Console.WriteLine("Two Digit Year Range: {0} - {1}",
cal.TwoDigitYearMax - 99, cal.TwoDigitYearMax);
Console.WriteLine("{0:d}", DateTime.ParseExact(value, fmt, null));
}
}
// The example displays the following output:
// Two Digit Year Range: 1930 - 2029
// 1/24/1949
//
// Two Digit Year Range: 2000 - 2099
// 1/24/2049

O exemplo a seguir inclui o especificador de formato personalizado "yy" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(1, 12, 1);


DateTime date2 = new DateTime(2010, 1, 1);
Console.WriteLine(date1.ToString("%y"));
// Displays 1
Console.WriteLine(date1.ToString("yy"));
// Displays 01
Console.WriteLine(date1.ToString("yyy"));
// Displays 001
Console.WriteLine(date1.ToString("yyyy"));
// Displays 0001
Console.WriteLine(date1.ToString("yyyyy"));
// Displays 00001
Console.WriteLine(date2.ToString("%y"));
// Displays 10
Console.WriteLine(date2.ToString("yy"));
// Displays 10
Console.WriteLine(date2.ToString("yyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyyy"));
// Displays 02010

Voltar à tabela

Especificador de formato personalizado "yyy"


O especificador de formato personalizado "yyy" representa o ano com, no mínimo, três
dígitos. Se o ano tem mais de três dígitos significativos, eles são incluídos na cadeia de
caracteres de resultado. Se o ano tem menos de três dígitos, o número é preenchido
com zeros à esquerda para produzir três dígitos.
7 Observação

Para o calendário budista tailandês, que pode ter anos com cinco dígitos, este
especificador de formato exibe todos os dígitos significativos.

O exemplo a seguir inclui o especificador de formato personalizado "yyy" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(1, 12, 1);


DateTime date2 = new DateTime(2010, 1, 1);
Console.WriteLine(date1.ToString("%y"));
// Displays 1
Console.WriteLine(date1.ToString("yy"));
// Displays 01
Console.WriteLine(date1.ToString("yyy"));
// Displays 001
Console.WriteLine(date1.ToString("yyyy"));
// Displays 0001
Console.WriteLine(date1.ToString("yyyyy"));
// Displays 00001
Console.WriteLine(date2.ToString("%y"));
// Displays 10
Console.WriteLine(date2.ToString("yy"));
// Displays 10
Console.WriteLine(date2.ToString("yyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyyy"));
// Displays 02010

Voltar à tabela

Especificador de formato personalizado "yyyy"


O especificador de formato personalizado "yyyy" representa o ano com, no mínimo,
quatro dígitos. Se o ano tem mais de quatro dígitos significativos, eles são incluídos na
cadeia de caracteres de resultado. Se o ano possui menos de quatro dígitos, o número é
preenchido com zeros à esquerda para produzir quatro dígitos.

7 Observação

Para o calendário budista tailandês, que pode ter anos de cinco dígitos, este
especificador de formato exibe no mínimo quatro dígitos.
O exemplo a seguir inclui o especificador de formato personalizado "yyyy" em uma
cadeia de caracteres de formato personalizado.

C#

DateTime date1 = new DateTime(1, 12, 1);


DateTime date2 = new DateTime(2010, 1, 1);
Console.WriteLine(date1.ToString("%y"));
// Displays 1
Console.WriteLine(date1.ToString("yy"));
// Displays 01
Console.WriteLine(date1.ToString("yyy"));
// Displays 001
Console.WriteLine(date1.ToString("yyyy"));
// Displays 0001
Console.WriteLine(date1.ToString("yyyyy"));
// Displays 00001
Console.WriteLine(date2.ToString("%y"));
// Displays 10
Console.WriteLine(date2.ToString("yy"));
// Displays 10
Console.WriteLine(date2.ToString("yyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyyy"));
// Displays 02010

Voltar à tabela

Especificador de formato personalizado "yyyyy"


O especificador de formato personalizado "yyyyy" (mais qualquer número de
especificadores "y" adicionais) representa o ano com, no mínimo, cinco dígitos. Se o ano
tem mais de cinco dígitos significativos, eles são incluídos na cadeia de caracteres de
resultado. Se o ano tem menos de cinco dígitos, o número é preenchido com zeros à
esquerda para produzir cinco dígitos.

Se houver especificadores "y" adicionais, o número será preenchido com tantos zeros à
esquerda quantos forem necessários para produzir o número de especificadores "y".

O exemplo a seguir inclui o especificador de formato personalizado "yyyyy" em uma


cadeia de caracteres de formato personalizado.

C#
DateTime date1 = new DateTime(1, 12, 1);
DateTime date2 = new DateTime(2010, 1, 1);
Console.WriteLine(date1.ToString("%y"));
// Displays 1
Console.WriteLine(date1.ToString("yy"));
// Displays 01
Console.WriteLine(date1.ToString("yyy"));
// Displays 001
Console.WriteLine(date1.ToString("yyyy"));
// Displays 0001
Console.WriteLine(date1.ToString("yyyyy"));
// Displays 00001
Console.WriteLine(date2.ToString("%y"));
// Displays 10
Console.WriteLine(date2.ToString("yy"));
// Displays 10
Console.WriteLine(date2.ToString("yyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyy"));
// Displays 2010
Console.WriteLine(date2.ToString("yyyyy"));
// Displays 02010

Voltar à tabela

Especificador de formato “z” de diferença

Especificador de formato personalizado "z"


Com valores DateTime, o especificador de formato personalizado “z” representa a
diferença com sinal do fuso horário especificado em relação ao UTC, medido em horas.
A diferença é sempre exibida com um sinal à esquerda. Um sinal de adição (+) indica
horas depois do UTC, enquanto que um sinal de subtração (-) indica horas antes do
UTC. Uma diferença com um único dígito é formatada sem um zero à esquerda.

A tabela a seguir mostra como o valor de deslocamento é alterado dependendo do


DateTimeKind.

DateTimeKind Valor de diferença


valor

Local A diferença assinada do fuso horário do sistema operacional local de UTC.

Unspecified A diferença assinada do fuso horário do sistema operacional local de UTC.


DateTimeKind Valor de diferença
valor

Utc +0 no .NET Core e .NET 5+.

No .NET Framework, a diferença assinada do fuso horário do sistema


operacional local de UTC.

Com os valores de DateTimeOffset, este formato representa a diferença do valor de


DateTimeOffset em relação ao UTC em horas.

Se o especificador de formato "z" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão e gerará uma FormatException. Para saber mais sobre como usar um
especificador de formato único, confira Usar especificadores de formato único
personalizados posteriormente nesse artigo.

O exemplo a seguir inclui o especificador de formato personalizado "z" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = DateTime.UtcNow;


Console.WriteLine(String.Format("{0:%z}, {0:zz}, {0:zzz}",
date1));
// Displays -7, -07, -07:00 on .NET Framework
// Displays +0, +00, +00:00 on .NET Core and .NET 5+

DateTimeOffset date2 = new DateTimeOffset(2008, 8, 1, 0, 0, 0,


new TimeSpan(6, 0, 0));
Console.WriteLine(String.Format("{0:%z}, {0:zz}, {0:zzz}",
date2));
// Displays +6, +06, +06:00

Voltar à tabela

Especificador de formato personalizado "zz"


Com valores DateTime, o especificador de formato personalizado "zz" representa a
diferença do fuso horário especificado em relação ao UTC em horas. A diferença é
sempre exibida com um sinal à esquerda. Um sinal de adição (+) indica horas depois do
UTC, enquanto que um sinal de subtração (-) indica horas antes do UTC. Uma diferença
com um único dígito único é formatada com um zero à esquerda.

A tabela a seguir mostra como o valor de deslocamento é alterado dependendo do


DateTimeKind.
DateTimeKind Valor de diferença
valor

Local A diferença assinada do fuso horário do sistema operacional local de UTC.

Unspecified A diferença assinada do fuso horário do sistema operacional local de UTC.

Utc +00 no .NET Core e .NET 5+.

No .NET Framework, a diferença assinada do fuso horário do sistema


operacional local de UTC.

Com os valores de DateTimeOffset, este formato representa a diferença do valor de


DateTimeOffset em relação ao UTC em horas.

O exemplo a seguir inclui o especificador de formato personalizado "zz" em uma cadeia


de caracteres de formato personalizado.

C#

DateTime date1 = DateTime.UtcNow;


Console.WriteLine(String.Format("{0:%z}, {0:zz}, {0:zzz}",
date1));
// Displays -7, -07, -07:00 on .NET Framework
// Displays +0, +00, +00:00 on .NET Core and .NET 5+

DateTimeOffset date2 = new DateTimeOffset(2008, 8, 1, 0, 0, 0,


new TimeSpan(6, 0, 0));
Console.WriteLine(String.Format("{0:%z}, {0:zz}, {0:zzz}",
date2));
// Displays +6, +06, +06:00

Voltar à tabela

Especificador de formato personalizado "zzz"


Com valores DateTime, o especificador de formato personalizado "zzz" representa a
diferença do fuso horário especificado em relação ao UTC em horas e minutos. A
diferença é sempre exibida com um sinal à esquerda. Um sinal de adição (+) indica
horas depois do UTC, enquanto que um sinal de subtração (-) indica horas antes do
UTC. Uma diferença com um único dígito único é formatada com um zero à esquerda.

A tabela a seguir mostra como o valor de deslocamento é alterado dependendo do


DateTimeKind.
DateTimeKind Valor de diferença
valor

Local A diferença assinada do fuso horário do sistema operacional local de UTC.

Unspecified A diferença assinada do fuso horário do sistema operacional local de UTC.

Utc +00:00 no .NET Core e .NET 5+.

No .NET Framework, a diferença assinada do fuso horário do sistema


operacional local de UTC.

Com valores DateTimeOffset, esse especificador de formato representa a diferença do


valor de DateTimeOffset em relação ao UTC em horas e minutos.

O exemplo a seguir inclui o especificador de formato personalizado "zzz" em uma


cadeia de caracteres de formato personalizado.

C#

DateTime date1 = DateTime.UtcNow;


Console.WriteLine(String.Format("{0:%z}, {0:zz}, {0:zzz}",
date1));
// Displays -7, -07, -07:00 on .NET Framework
// Displays +0, +00, +00:00 on .NET Core and .NET 5+

DateTimeOffset date2 = new DateTimeOffset(2008, 8, 1, 0, 0, 0,


new TimeSpan(6, 0, 0));
Console.WriteLine(String.Format("{0:%z}, {0:zz}, {0:zzz}",
date2));
// Displays +6, +06, +06:00

Voltar à tabela

Especificadores de separador de data e hora

Especificador de formato personalizado ":"


O especificador de formato personalizado ":" representa o separador de hora, o qual é
usado para diferenciar horas, minutos e segundos. O separador de hora localizado
apropriado é recuperado da propriedade DateTimeFormatInfo.TimeSeparator da cultura
atual ou especificada.

7 Observação
Para alterar o separador de hora de uma sequência de data e hora específica,
especifique o caractere separador dentro de um delimitador de cadeia de
caracteres literal. Por exemplo, a cadeia de caracteres de formato personalizada
hh'_'dd'_'ss produz uma cadeia de caracteres de resultado em que "_" (um
sublinhado) é sempre usado como o separador de hora. Para alterar o separador de
hora de todas as datas de uma cultura, seja para alterar o valor da propriedade
DateTimeFormatInfo.TimeSeparator da cultura atual ou para instanciar um objeto
DateTimeFormatInfo, atribua o caractere à sua propriedade TimeSeparator e
chame uma sobrecarga do método de formatação que inclua um parâmetro
IFormatProvider.

Se o especificador de formato ":" for usado sem outros especificadores de formato


personalizado, ele será interpretado como um especificador de formato de data e hora
padrão e gerará uma FormatException. Para saber mais sobre como usar um
especificador de formato único, confira Usar especificadores de formato único
personalizados posteriormente nesse artigo.

Voltar à tabela

Especificador de formato personalizado "/"


O especificador de formato personalizado "/" representa o separador de data, o qual é
usado para diferenciar anos, meses e dias. O separador de data localizado apropriado é
recuperado da propriedade DateTimeFormatInfo.DateSeparator da cultura atual ou
especificada.

7 Observação

Para alterar o separador de data de uma sequência de data e hora específica,


especifique o caractere separador dentro de um delimitador de cadeia de
caracteres literal. Por exemplo, a sequência de formato personalizado
mm'/'dd'/'yyyy produz uma cadeia de caracteres de resultado em que "/" é

sempre usado como o separador de data. Para alterar o separador de data de


todas as datas de uma cultura, seja para alterar o valor da propriedade
DateTimeFormatInfo.DateSeparator da cultura atual ou para instanciar um objeto
DateTimeFormatInfo, atribua o caractere à sua propriedade DateSeparator e
chame uma sobrecarga do método de formatação que inclua um parâmetro
IFormatProvider.
Se o especificador de formato "/" for usado sem outros especificadores de formato
personalizado, ele será interpretado como um especificador de formato de data e hora
padrão e gerará uma FormatException. Para saber mais sobre como usar um
especificador de formato único, confira Usar especificadores de formato único
personalizados posteriormente nesse artigo.

Voltar à tabela

Literais de caracteres
Os seguintes caracteres de uma cadeia de caracteres de formato de data e hora
personalizado são reservados e sempre são interpretados como caracteres formatação
ou, no caso de " , ' , / e \ , como caracteres especiais.

F
H

M
d

f
g

m
s

t
y

%
:

/
"

'

Todos os outros caracteres sempre são interpretados como literais de caracteres e, em


uma operação de formatação, são incluídos na cadeia de caracteres de resultado
inalterada. Em uma operação de análise, eles devem corresponder exatamente aos
caracteres na cadeia de entrada; a comparação diferencia maiúsculas de minúsculas.
O exemplo a seguir inclui os caracteres literais "PST" (que indicam a Hora Padrão do
Pacífico) e “PDT” (que indicam a Hora de Verão do Pacífico) para representar o fuso
horário local em uma cadeia de caracteres de formato. Observe que a cadeia de
caracteres está incluída na cadeia de caracteres de resultado e que uma cadeia de
caracteres que inclui a cadeia de caracteres de fuso horário local também é analisada
com êxito.

C#

using System;
using System.Globalization;

public class Example5


{
public static void Main()
{
String[] formats = { "dd MMM yyyy hh:mm tt PST",
"dd MMM yyyy hh:mm tt PDT" };
var dat = new DateTime(2016, 8, 18, 16, 50, 0);
// Display the result string.
Console.WriteLine(dat.ToString(formats[1]));

// Parse a string.
String value = "25 Dec 2016 12:00 pm PST";
DateTime newDate;
if (DateTime.TryParseExact(value, formats, null,
DateTimeStyles.None, out newDate))
Console.WriteLine(newDate);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 18 Aug 2016 04:50 PM PDT
// 12/25/2016 12:00:00 PM

Há duas maneiras de indicar que os caracteres devem ser interpretados como caracteres
literais e não como caracteres reservados, para que possam ser incluídos em uma cadeia
de caracteres de resultado ou analisados com êxito em uma cadeia de caracteres de
entrada:

Com o escape de cada caractere reservado. Para obter mais informações, consulte
Usando o caractere de escape.

O exemplo a seguir inclui os caracteres literais "pst" (que indicam a Hora Padrão do
Pacífico) para representar o fuso horário local em uma cadeia de caracteres de formato.
Como "s" e "t" são cadeias de caracteres de formato personalizado, ambos os caracteres
devem ter um escape para serem interpretados como caracteres literais.
C#

using System;
using System.Globalization;

public class Example3


{
public static void Main()
{
String format = "dd MMM yyyy hh:mm tt p\\s\\t";
var dat = new DateTime(2016, 8, 18, 16, 50, 0);
// Display the result string.
Console.WriteLine(dat.ToString(format));

// Parse a string.
String value = "25 Dec 2016 12:00 pm pst";
DateTime newDate;
if (DateTime.TryParseExact(value, format, null,
DateTimeStyles.None, out newDate))
Console.WriteLine(newDate);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 18 Aug 2016 04:50 PM pst
// 12/25/2016 12:00:00 PM

Colocando toda a cadeia de caracteres literal entre aspas ou apóstrofos. O


exemplo a seguir é semelhante ao anterior, mas "pst" é colocado entre aspas para
indicar que toda a cadeia de caracteres delimitada deve ser interpretada como
literais de caracteres.

C#

using System;
using System.Globalization;

public class Example6


{
public static void Main()
{
String format = "dd MMM yyyy hh:mm tt \"pst\"";
var dat = new DateTime(2016, 8, 18, 16, 50, 0);
// Display the result string.
Console.WriteLine(dat.ToString(format));

// Parse a string.
String value = "25 Dec 2016 12:00 pm pst";
DateTime newDate;
if (DateTime.TryParseExact(value, format, null,
DateTimeStyles.None, out newDate))
Console.WriteLine(newDate);
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 18 Aug 2016 04:50 PM pst
// 12/25/2016 12:00:00 PM

Observações

Usando especificadores de formato personalizado


simples
Uma cadeia de caracteres de formato de data e hora personalizado consiste em dois ou
mais caracteres. Os métodos de formatação de data e hora interpretam qualquer cadeia
de um único caractere como uma cadeia de caracteres de formato de data e hora
padrão. Quando não reconhecem o caractere como um especificador de formato válido,
eles geram uma FormatException. Por exemplo, uma cadeia de caracteres de formato
que consiste somente no especificador "h" é interpretada como uma cadeia de
caracteres de formato padrão de data e hora. No entanto, nesse caso específico, uma
exceção é gerada porque não há nenhum especificador "h" de formato padrão de data
e hora.

Para usar qualquer um dos especificadores de formato de data e hora personalizado


como sendo o único especificador em uma cadeia de caracteres de formato (ou seja,
para usar o especificador de formato personalizado "d", "f", "F", "g", "h", "H", "K", "m",
"M", "s", "t", "y", "z", ":" ou “/” por si só), inclua um espaço antes ou após o especificador
ou inclua um especificador de formato de porcentagem "%" antes do especificador de
data e hora personalizado simples.

Por exemplo, " %h" é interpretada como uma cadeia de caracteres de formato de data e
hora personalizado que exibe a hora representada pelo valor atual de data e hora. Você
também pode usar a cadeia de caracteres de formato " h" ou "H ", embora isso inclua
um espaço na cadeia de caracteres resultante em conjunto com a hora. O exemplo a
seguir ilustra essas três cadeias de caracteres de formatos.

C#

DateTime dat1 = new DateTime(2009, 6, 15, 13, 45, 0);

Console.WriteLine("'{0:%h}'", dat1);
Console.WriteLine("'{0: h}'", dat1);
Console.WriteLine("'{0:h }'", dat1);
// The example displays the following output:
// '1'
// ' 1'
// '1 '

Usando o caractere de Escape


Os caracteres "d", "f", "F", "g", "h", "H", "K", "m", "M", "s", "t", "y", "z", ":" ou "/" em uma
cadeia de caracteres de formato são interpretados como especificadores de formato
personalizados em vez de caracteres literais. Para impedir que um caractere seja
interpretado como um especificador de formato, você pode precedê-lo com uma barra
invertida (\), que é o caractere de escape. O caractere de escape significa que o próximo
caractere é um literal de caractere que deve ser incluído inalterado na cadeia de
caracteres de resultado.

Para incluir uma barra invertida em uma cadeia de caracteres de resultado, você deve
escapá-la com outra barra invertida ( \\ ).

7 Observação

Alguns compiladores, como os compiladores C++ e C#, também podem interpretar


um único caractere de barra invertida como um caractere de escape. Para garantir
que uma cadeia de caracteres seja interpretada corretamente quando formatada,
você poderá usar o caractere literal de cadeia de caracteres textual (o caractere @)
antes da cadeia de caracteres em C# ou adicionar outro caractere de barra
invertida antes de cada barra invertida em C# e em C++. O exemplo de C# a seguir
ilustra ambas as abordagens.

O exemplo a seguir usa o caractere de escape para impedir que a operação de


formatação interprete os caracteres de "h" e "m" como especificadores de formato.

C#

DateTime date = new DateTime(2009, 06, 15, 13, 45, 30, 90);
string fmt1 = "h \\h m \\m";
string fmt2 = @"h \h m \m";

Console.WriteLine("{0} ({1}) -> {2}", date, fmt1, date.ToString(fmt1));


Console.WriteLine("{0} ({1}) -> {2}", date, fmt2, date.ToString(fmt2));
// The example displays the following output:
// 6/15/2009 1:45:30 PM (h \h m \m) -> 1 h 45 m
// 6/15/2009 1:45:30 PM (h \h m \m) -> 1 h 45 m
Configurações do Painel de Controle
As configurações de Opções Regionais e de Idiomas no Painel de Controle influenciam
a cadeia de caracteres de resultado produzida por uma operação de formatação que
inclui muitos dos especificadores de formato de data e hora personalizado. Essas
configurações são usadas para inicializar o objeto DateTimeFormatInfo associado à
cultura atual, a qual fornece os valores usados para determinar a formatação.
Computadores que usam configurações diferentes geram cadeias de caracteres de
resultado diferentes.

Além disso, se o constructo CultureInfo(String) for usado para criar uma instância de um
novo objeto CultureInfo que representa a mesma cultura que a cultura atual do sistema,
quaisquer personalizações estabelecidas pelo item Opções Regionais e de Idioma no
Painel de Controle serão aplicadas ao novo objeto CultureInfo. Você pode usar o
construtor CultureInfo(String, Boolean) para criar um objeto CultureInfo que não reflita
as personalizações de um sistema.

Propriedades DateTimeFormatInfo
A formatação é influenciada pelas propriedades do objeto DateTimeFormatInfo atual,
que é fornecido implicitamente pela cultura atual ou explicitamente pelo parâmetro
IFormatProvider do método que invoca a formatação. Para o parâmetro
IFormatProvider, você deve especificar um objeto CultureInfo, o qual representa uma
cultura ou um objeto DateTimeFormatInfo.

A cadeia de caracteres de resultado produzida por muitos dos especificadores de


formato de data e hora personalizado também depende das propriedades do objeto
DateTimeFormatInfo atual. Seu aplicativo pode alterar o resultado produzido por alguns
especificadores de formato personalizado de data e hora ao alterar a propriedade
DateTimeFormatInfo correspondente. Por exemplo, o especificador de formato "ddd"
adiciona um nome de dia da semana abreviado encontrado na matriz de cadeia de
caracteres AbbreviatedDayNames à cadeia de caracteres de resultado. Da mesma forma,
o especificador de formato "MMMM" adiciona um nome de mês completo encontrado
na matriz de cadeias de caracteres MonthNames à cadeia de caracteres de resultado.

Confira também
System.DateTime
System.IFormatProvider
Formatando tipos
Cadeias de caracteres de formato de data e hora padrão
Amostra: utilitário de formatação do WinForms do .NET Core (C#)
Amostra: utilitário de formatação do WinForms do .NET Core (Visual Basic)
Cadeias de caracteres de formato
TimeSpan padrão
Artigo • 09/05/2023

Uma cadeia de caracteres de formato padrão TimeSpan usa um único especificador de


formato para definir a representação de texto de um valor TimeSpan que resulta de uma
operação de formatação. Qualquer sequência de formato que contenha mais de um
caractere, incluindo espaço em branco, é interpretada como uma sequência de formato
TimeSpan personalizada. Para obter mais informações, consulte cadeias de caracteres de
formato TimeSpan personalizado .

As representações de sequência de valores TimeSpan são produzidas por chamadas


para as sobrecargas do método TimeSpan.ToString, bem como por métodos que
oferecem suporte à formatação composta, como String.Format. Para obter mais
informações, consulte Tipos de formatação e Formatação de composição. O exemplo a
seguir ilustra o uso de sequências de formato padrão em operações de formatação.

C#

using System;

public class Example


{
public static void Main()
{
TimeSpan duration = new TimeSpan(1, 12, 23, 62);
string output = "Time of Travel: " + duration.ToString("c");
Console.WriteLine(output);

Console.WriteLine("Time of Travel: {0:c}", duration);


}
}
// The example displays the following output:
// Time of Travel: 1.12:24:02
// Time of Travel: 1.12:24:02

As sequências de formato TimeSpan padrão são usadas também pelos métodos


TimeSpan.ParseExact e TimeSpan.TryParseExact para definir o formato necessário de
sequências de caracteres de entrada para análise de operações. (A análise converte a
representação de cadeia de caracteres de um valor nesse valor.) O exemplo a seguir
ilustra o uso de cadeias de caracteres de formato padrão em operações de análise.

C#
using System;

public class Example


{
public static void Main()
{
string value = "1.03:14:56.1667";
TimeSpan interval;
try {
interval = TimeSpan.ParseExact(value, "c", null);
Console.WriteLine("Converted '{0}' to {1}", value, interval);
}
catch (FormatException) {
Console.WriteLine("{0}: Bad Format", value);
}
catch (OverflowException) {
Console.WriteLine("{0}: Out of Range", value);
}

if (TimeSpan.TryParseExact(value, "c", null, out interval))


Console.WriteLine("Converted '{0}' to {1}", value, interval);
else
Console.WriteLine("Unable to convert {0} to a time interval.",
value);
}
}
// The example displays the following output:
// Converted '1.03:14:56.1667' to 1.03:14:56.1667000
// Converted '1.03:14:56.1667' to 1.03:14:56.1667000

A tabela a seguir lista os especificadores de formato de intervalo de tempo padrão.

Especificador Nome Descrição Exemplos


de formato

"c" Formato Esse especificador não é sensível à cultura. Ele TimeSpan.Zero ->
de assume o formato [-] 00:00:00
constante [d'.']hh':'mm':'ss['.'fffffff] .
(invariável) New TimeSpan(0,
(As sequências de formato "t" e "T" produzem 0, 30, 0) ->
os mesmos resultados). 00:30:00

Mais informações: o especificador de formato New TimeSpan(3,


de constante ("c"). 17, 25, 30, 500) -
>
3.17:25:30.5000000
Especificador Nome Descrição Exemplos
de formato

"g" Formato Esse especificador gera apenas o que é New TimeSpan(1,


curto geral necessário. Ele é sensível à cultura e assume o 3, 16, 50, 500) -
formato [-][d':']h':'mm':'ss[.FFFFFFF] . > 1:3:16:50.5 (en-
US)
Mais informações: o especificador de formato
curto geral ("g"). New TimeSpan(1,
3, 16, 50, 500) -
> 1:3:16:50,5 (fr-
FR)

New TimeSpan(1,
3, 16, 50, 599) -
> 1:3:16:50.599
(en-US)

New TimeSpan(1,
3, 16, 50, 599) -
> 1:3:16:50,599 (fr-
FR)

"G" Formato Esse especificador sempre gera dias e sete New TimeSpan(18,
longo dígitos de fração. Ele é sensível à cultura e 30, 0) ->
geral assume o formato 0:18:30:00.0000000
[-]d':'hh':'mm':'ss.fffffff . (en-US)

Mais informações: o especificador de formato New TimeSpan(18,


longo geral ("G"). 30, 0) ->
0:18:30:00,0000000
(fr-FR)

O especificador de formato de constante ("c")


O especificador de formato "c" retorna a representação de sequência de um valor
TimeSpan da seguinte forma:

[-][d.]hh:mm:ss[.fffffff]

Os elementos entre colchetes ([ e ]) são opcionais. O ponto (.) e os dois pontos (:) são
símbolos literais. A tabela a seguir descreve os elementos restantes.

Elemento Descrição

- Um sinal negativo opcional, que indica um intervalo de tempo negativo.


Elemento Descrição

d O número opcional de dias, sem zeros à esquerda.

hh O número de horas, que varia de "00" a "23".

mm O número de minutos, que varia de "00" a "59".

ss O número de segundos, que varia de "00" a "59".

fffffff A parte de fração opcional de um segundo. Seu valor pode variar de "0000001" (um
pulso ou um décimo milionésimo de segundo) até "9999999" (9.999.999 dez
milionésimos de segundo ou um segundo menos um pulso).

Ao contrário dos especificadores de formato de "g" e "G", o especificador de formato


"c" não é sensível à cultura. Ele produz a representação de cadeia de caracteres de um
valor TimeSpan que é invariável e que é comum a versões anteriores ao .NET Framework
4. "c"é a cadeia de caracteres de formato TimeSpan padrão, o método
TimeSpan.ToString() formata um valor de intervalo de tempo usando a sequência de
formato "c".

7 Observação

TimeSpan também oferece suporte às sequências de formato padrão "t" e "T", que
são idênticas em comportamento à sequência de formato padrão "c".

O exemplo a seguir instancia dois objetos TimeSpan, usa-os para executar operações
aritméticas e exibe o resultado. Em cada caso, ele usa formatação composta para exibir
o valor TimeSpan usando o especificador de formato "c".

C#

using System;

public class Example


{
public static void Main()
{
TimeSpan interval1, interval2;
interval1 = new TimeSpan(7, 45, 16);
interval2 = new TimeSpan(18, 12, 38);

Console.WriteLine("{0:c} - {1:c} = {2:c}", interval1,


interval2, interval1 - interval2);
Console.WriteLine("{0:c} + {1:c} = {2:c}", interval1,
interval2, interval1 + interval2);

interval1 = new TimeSpan(0, 0, 1, 14, 365);


interval2 = TimeSpan.FromTicks(2143756);
Console.WriteLine("{0:c} + {1:c} = {2:c}", interval1,
interval2, interval1 + interval2);
}
}
// The example displays the following output:
// 07:45:16 - 18:12:38 = -10:27:22
// 07:45:16 + 18:12:38 = 1.01:57:54
// 00:01:14.3650000 + 00:00:00.2143756 = 00:01:14.5793756

O especificador de formato curto geral ("g")


O especificador de formato TimeSpan de "g" retorna a representação de sequência de
um valor TimeSpan em um formato compacto, incluindo apenas os elementos que são
necessários. Ele tem o seguinte formato:

[-][d:]h:mm:ss[.FFFFFFF]

Os elementos entre colchetes ([ e ]) são opcionais. Os dois pontos (:) são um símbolo
literal. A tabela a seguir descreve os elementos restantes.

Elemento Descrição

- Um sinal negativo opcional, que indica um intervalo de tempo negativo.

d O número opcional de dias, sem zeros à esquerda.

h O número de horas, que varia de "0" a "23", sem zeros à esquerda.

mm O número de minutos, que varia de "00" a "59".

ss O número de minutos, que varia de "00" a "59".

. O separador de fração de segundo. É equivalente à propriedade


NumberDecimalSeparator da cultura especificada sem substituições pelo usuário.

FFFFFFF As frações de segundo. Uma vez que é exibido o mínimo de dígitos possível.

Como o especificador de formato "G", o especificador de formato "g" é localizado. Seu


separador de fração de segundo se baseia na cultura atual ou de uma propriedade
NumberDecimalSeparator da cultura especificada.

O exemplo a seguir instancia dois objetos TimeSpan, usa-os para executar operações
aritméticas e exibe o resultado. Em cada caso, ele usa formatação composta para exibir
o valor TimeSpan usando o especificador de formato "g". Além disso, ele formata o
valor TimeSpan usando as convenções de formatação da cultura atual do sistema (que,
nesse caso, é Inglês - Estados Unidos ou en-US) e da cultura francês - França (fr-FR).
C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
TimeSpan interval1, interval2;
interval1 = new TimeSpan(7, 45, 16);
interval2 = new TimeSpan(18, 12, 38);

Console.WriteLine("{0:g} - {1:g} = {2:g}", interval1,


interval2, interval1 - interval2);
Console.WriteLine(String.Format(new CultureInfo("fr-FR"),
"{0:g} + {1:g} = {2:g}", interval1,
interval2, interval1 + interval2));

interval1 = new TimeSpan(0, 0, 1, 14, 36);


interval2 = TimeSpan.FromTicks(2143756);
Console.WriteLine("{0:g} + {1:g} = {2:g}", interval1,
interval2, interval1 + interval2);
}
}
// The example displays the following output:
// 7:45:16 - 18:12:38 = -10:27:22
// 7:45:16 + 18:12:38 = 1:1:57:54
// 0:01:14.036 + 0:00:00.2143756 = 0:01:14.2503756

O especificador de formato longo geral ("G")


O especificador de formato TimeSpan de "G" retorna a representação de sequência de
um valor TimeSpan em um formato longo que sempre inclui dias e a fração de segundo.
A sequência que resulta do especificador de formato padrão "G" tem o seguinte
formato:

[-]d:hh:mm:ss.fffffff

Os elementos entre colchetes ([ e ]) são opcionais. Os dois pontos (:) são um símbolo
literal. A tabela a seguir descreve os elementos restantes.

Elemento Descrição

- Um sinal negativo opcional, que indica um intervalo de tempo negativo.

d O número de dias, sem zeros à esquerda.

hh O número de horas, que varia de "00" a "23".


Elemento Descrição

mm O número de minutos, que varia de "00" a "59".

ss O número de segundos, que varia de "00" a "59".

. O separador de fração de segundo. É equivalente à propriedade


NumberDecimalSeparator da cultura especificada sem substituições pelo usuário.

fffffff As frações de segundo.

Como o especificador de formato "G", o especificador de formato "g" é localizado. Seu


separador de fração de segundo se baseia na cultura atual ou de uma propriedade
NumberDecimalSeparator da cultura especificada.

O exemplo a seguir instancia dois objetos TimeSpan, usa-os para executar operações
aritméticas e exibe o resultado. Em cada caso, ele usa formatação composta para exibir
o valor TimeSpan usando o especificador de formato "G". Além disso, ele formata o
valor TimeSpan usando as convenções de formatação da cultura atual do sistema (que,
nesse caso, é Inglês - Estados Unidos ou en-US) e da cultura francês - França (fr-FR).

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
TimeSpan interval1, interval2;
interval1 = new TimeSpan(7, 45, 16);
interval2 = new TimeSpan(18, 12, 38);

Console.WriteLine("{0:G} - {1:G} = {2:G}", interval1,


interval2, interval1 - interval2);
Console.WriteLine(String.Format(new CultureInfo("fr-FR"),
"{0:G} + {1:G} = {2:G}", interval1,
interval2, interval1 + interval2));

interval1 = new TimeSpan(0, 0, 1, 14, 36);


interval2 = TimeSpan.FromTicks(2143756);
Console.WriteLine("{0:G} + {1:G} = {2:G}", interval1,
interval2, interval1 + interval2);
}
}
// The example displays the following output:
// 0:07:45:16.0000000 - 0:18:12:38.0000000 = -0:10:27:22.0000000
// 0:07:45:16,0000000 + 0:18:12:38,0000000 = 1:01:57:54,0000000
// 0:00:01:14.0360000 + 0:00:00:00.2143756 = 0:00:01:14.2503756
Confira também
Formatar tipos
Cadeias de caracteres de formato TimeSpan personalizado
Analisando cadeias de caracteres
Cadeias de caracteres de formato
TimeSpan personalizado
Artigo • 09/05/2023

Uma cadeia de caracteres de formato TimeSpan define a representação de cadeia de


caracteres de um valor de TimeSpan que é resultante de uma operação de formatação.
Uma cadeia de caracteres de formato personalizado consiste em um ou mais O
especificadores de formato TimeSpan em conjunto com qualquer número de caracteres
literais. Qualquer cadeia de caracteres que não seja uma Cadeia de caracteres em
formato TimeSpan padrão é interpretada como uma cadeia de caracteres em formato
TimeSpan personalizado.

) Importante

Os especificadores de formato TimeSpan personalizados não incluem símbolos de


separador de espaço reservado, como os símbolos que separam dias de horas,
horas de minutos ou segundos de frações de segundo. Em vez disso, esses
símbolos devem ser incluídos na cadeia de caracteres de formato personalizado
como literais de cadeia de caracteres. Por exemplo, "dd\.hh\:mm" define um ponto
(.) como o separador entre dias e horas e dois-pontos (:) como o separador entre
horas e minutos.

Os especificadores de formato TimeSpan personalizado também não incluem um


símbolo de sinal que permite diferenciar entre intervalos de tempo positivos e
negativos. Para incluir um símbolo de sinal, você precisa construir uma cadeia de
caracteres de formato usando lógica condicional. A seção Outros caracteres inclui
um exemplo.

As representações de sequência de valores TimeSpan são produzidas por chamadas


para as sobrecargas do método TimeSpan.ToString e por métodos que oferecem
suporte à formatação composta, como String.Format. Para obter mais informações,
consulte Tipos de formatação e Formatação de composição. O exemplo a seguir ilustra
o uso de sequências de formato personalizado em operações de formatação.

C#

using System;

public class Example


{
public static void Main()
{
TimeSpan duration = new TimeSpan(1, 12, 23, 62);

string output = null;


output = "Time of Travel: " + duration.ToString("%d") + " days";
Console.WriteLine(output);
output = "Time of Travel: " + duration.ToString(@"dd\.hh\:mm\:ss");
Console.WriteLine(output);

Console.WriteLine("Time of Travel: {0:%d} day(s)", duration);


Console.WriteLine("Time of Travel: {0:dd\\.hh\\:mm\\:ss} days",
duration);
}
}
// The example displays the following output:
// Time of Travel: 1 days
// Time of Travel: 01.12:24:02
// Time of Travel: 1 day(s)
// Time of Travel: 01.12:24:02 days

As sequências de formato TimeSpan personalizado são usadas também pelos métodos


TimeSpan.ParseExact e TimeSpan.TryParseExact para definir o formato necessário de
sequências de caracteres de entrada para análise de operações. (A análise converte a
representação de cadeia de caracteres de um valor nesse valor.) O exemplo a seguir
ilustra o uso de cadeias de caracteres de formato padrão em operações de análise.

C#

using System;

public class Example


{
public static void Main()
{
string value = null;
TimeSpan interval;

value = "6";
if (TimeSpan.TryParseExact(value, "%d", null, out interval))
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"));
else
Console.WriteLine("Unable to parse '{0}'", value);

value = "16:32.05";
if (TimeSpan.TryParseExact(value, @"mm\:ss\.ff", null, out interval))
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"));
else
Console.WriteLine("Unable to parse '{0}'", value);

value= "12.035";
if (TimeSpan.TryParseExact(value, "ss\\.fff", null, out interval))
Console.WriteLine("{0} --> {1}", value, interval.ToString("c"));
else
Console.WriteLine("Unable to parse '{0}'", value);
}
}
// The example displays the following output:
// 6 --> 6.00:00:00
// 16:32.05 --> 00:16:32.0500000
// 12.035 --> 00:00:12.0350000

A tabela a seguir descreve os especificadores de formato personalizado de data e hora.

Especificador Descrição Exemplo


de formato

"d", "%d" O número de dias inteiros no intervalo new TimeSpan(6, 14, 32, 17, 685):
de tempo.
%d --> "6"
Mais informações: especificador de
formato personalizado "d". d\.hh\:mm --> "6.14:32"

"dd"- O número de dias inteiros no intervalo new TimeSpan(6, 14, 32, 17, 685):
"dddddddd" de tempo, preenchido com zeros à
esquerda, conforme necessário. ddd --> "006"

Mais informações: especificadores de dd\.hh\:mm --> "06.14:32"


formato personalizado "dd"-
"dddddddd".

"h", "%h" O número de horas inteiras no intervalo new TimeSpan(6, 14, 32, 17, 685):
de tempo que não são contadas como
parte dos dias. Horas de dígito único não %h --> "14"
têm um zero à esquerda.
hh\:mm --> "14:32"
Mais informações: especificador de
formato personalizado "h".

"hh" O número de horas inteiras no intervalo new TimeSpan(6, 14, 32, 17, 685):
de tempo que não são contadas como
parte dos dias. Horas de dígito único têm hh --> "14"
um zero à esquerda.
new TimeSpan(6, 8, 32, 17, 685):
Mais informações: especificador de
formato personalizado "hh". hh --> 08
Especificador Descrição Exemplo
de formato

"m", "%m" O número de minutos inteiros no new TimeSpan(6, 14, 8, 17, 685):
intervalo de tempo que não são incluídos
como parte das horas ou dias. Minutos %m --> "8"
de dígito único não têm um zero à
esquerda. h\:m --> "14:8"

Mais informações: especificador de


formato personalizado "m".

"mm" O número de minutos inteiros no new TimeSpan(6, 14, 8, 17, 685):


intervalo de tempo que não são incluídos
como parte das horas ou dias. Minutos mm --> "08"
de dígito único têm um zero à esquerda.
new TimeSpan(6, 8, 5, 17, 685):
Mais informações: especificador de
formato personalizado "mm". d\.hh\:mm\:ss --> 6.08:05:17

"s", "%s" O número de segundos inteiros no TimeSpan.FromSeconds(12.965) :


intervalo de tempo que não são incluídos
como parte das horas, dias ou minutos. %s --> 12
Segundos de dígito único não têm um
zero à esquerda. s\.fff --> 12.965

Mais informações: especificador de


formato personalizado "s".

"ss" O número de segundos inteiros no TimeSpan.FromSeconds(6.965) :


intervalo de tempo que não são incluídos
como parte das horas, dias ou minutos. ss --> 06
Segundos de dígito único têm um zero à
esquerda. ss\.fff --> 06.965

Mais informações: especificador de


formato personalizado "ss".

"f", "%f" Os décimos de segundo em um intervalo TimeSpan.FromSeconds(6.895) :


de tempo.
f --> 8
Mais informações: especificador de
formato personalizado "f". ss\.f --> 06.8

"ff" Os centésimos de segundo em um TimeSpan.FromSeconds(6.895) :


intervalo de tempo.
ff --> 89
Mais informações: especificador de
formato personalizado "ff". ss\.ff --> 06.89
Especificador Descrição Exemplo
de formato

"fff" Os milissegundos em um intervalo de TimeSpan.FromSeconds(6.895) :


tempo.
fff --> 895
Mais informações: especificador de
formato personalizado "fff". ss\.fff --> 06.895

"ffff" Os décimos de milésimos de segundo TimeSpan.Parse("0:0:6.8954321") :


em um intervalo de tempo.
ffff --> 8954
Mais informações: especificador de
formato personalizado "ffff". ss\.ffff --> 06.8954

"fffff" As centenas de milésimos de segundo TimeSpan.Parse("0:0:6.8954321") :


em um intervalo de tempo.
fffff --> 89543
Mais informações: especificador de
formato personalizado "fffff". ss\.fffff --> 06.89543

"ffffff" Os milionésimos de segundo em um TimeSpan.Parse("0:0:6.8954321") :


intervalo de tempo.
ffffff --> 895432
Mais informações: especificador de
formato personalizado "ffffff". ss\.ffffff --> 06.895432

"fffffff" Os dez milionésimos de segundo (ou as TimeSpan.Parse("0:0:6.8954321") :


marcas fracionárias) em um intervalo de
tempo. fffffff --> 8954321

Mais informações: especificador de ss\.fffffff --> 06.8954321


formato personalizado "fffffff".

"F", "%F" Os décimos de segundo em um intervalo TimeSpan.Parse("00:00:06.32") :


de tempo. Nada será exibido se o dígito
for zero. %F : 3

Mais informações: especificador de TimeSpan.Parse("0:0:3.091") :


formato personalizado "F".
ss\.F : 03.

"FF" Os centésimos de segundo em um TimeSpan.Parse("00:00:06.329") :


intervalo de tempo. Zeros à direita
fracionais ou dois dígitos zero não são FF : 32
incluídos.
TimeSpan.Parse("0:0:3.101") :
Mais informações: especificador de
formato personalizado "FF". ss\.FF : 03.1
Especificador Descrição Exemplo
de formato

"FFF" Os milissegundos em um intervalo de TimeSpan.Parse("00:00:06.3291") :


tempo. Zeros à direita fracionais não são
incluídos. FFF : 329

Mais informações: TimeSpan.Parse("0:0:3.1009") :

ss\.FFF : 03.1

"FFFF" Os décimos de milésimos de segundo TimeSpan.Parse("00:00:06.32917") :


em um intervalo de tempo. Zeros à
direita fracionais não são incluídos. FFFFF : 3291

Mais informações: especificador de TimeSpan.Parse("0:0:3.10009") :


formato personalizado "FFFF".
ss\.FFFF : 03.1

"FFFFF" As centenas de milésimos de segundo TimeSpan.Parse("00:00:06.329179") :


em um intervalo de tempo. Zeros à
direita fracionais não são incluídos. FFFFF : 32917

Mais informações: especificador de TimeSpan.Parse("0:0:3.100009") :


formato personalizado "FFFF".
ss\.FFFFF : 03.1

"FFFFFF" Os milionésimos de segundo em um TimeSpan.Parse("00:00:06.3291791") :


intervalo de tempo. Zeros à direita
fracionais não são exibidos. FFFFFF : 329179

Mais informações: especificador de TimeSpan.Parse("0:0:3.1000009") :


formato personalizado "FFFFFF".
ss\.FFFFFF : 03.1

"FFFFFFF" Os décimos de milionésimos de segundo TimeSpan.Parse("00:00:06.3291791") :


em um intervalo de tempo. Zeros à
direita fracionais ou sete dígitos zero não FFFFFF : 3291791
são exibidos.
TimeSpan.Parse("0:0:3.1900000") :
Mais informações: especificador de
formato personalizado "FFFFFF". ss\.FFFFFF : 03.19

'string' Delimitador de cadeia de caracteres new TimeSpan(14, 32, 17):


literal.
hh':'mm':'ss --> "14:32:17"
Mais informações: outros caracteres.
Especificador Descrição Exemplo
de formato

\ O caractere de escape. new TimeSpan(14, 32, 17):

Mais informações: outros caracteres. hh\:mm\:ss --> "14:32:17"

Qualquer Qualquer outro caractere sem escape é new TimeSpan(14, 32, 17):
outro interpretado como um especificador de
caractere formato personalizado. hh\:mm\:ss --> "14:32:17"

Mais informações: Outros caracteres.

Especificador de formato personalizado "d"


O especificador de formato personalizado "d" fornece o valor da propriedade
TimeSpan.Days, que representa o número de dias inteiros no intervalo de tempo. Ele
gera o número total de dias em um valor de TimeSpan, mesmo se o valor tiver mais de
um dígito. Se o valor da propriedade TimeSpan.Days for zero, o especificador gerará "0".

Se o especificador de formato personalizado "d" for usado sozinho, especifique "%d"


para que ele não seja interpretado incorretamente como uma cadeia de caracteres de
formato padrão. O exemplo a seguir ilustra esse cenário.

C#

TimeSpan ts1 = new TimeSpan(16, 4, 3, 17, 250);


Console.WriteLine(ts1.ToString("%d"));
// Displays 16

O exemplo a seguir ilustra o uso do especificador de formato personalizado “d”.

C#

TimeSpan ts2 = new TimeSpan(4, 3, 17);


Console.WriteLine(ts2.ToString(@"d\.hh\:mm\:ss"));

TimeSpan ts3 = new TimeSpan(3, 4, 3, 17);


Console.WriteLine(ts3.ToString(@"d\.hh\:mm\:ss"));
// The example displays the following output:
// 0.04:03:17
// 3.04:03:17

Voltar à tabela
Especificadores de formato personalizado
"dd"-"dddddddd"
Os especificadores de formato personalizados "dd", "ddd", "dddd", "ddddd", "dddddd",
"ddddddd" e "dddddddd" fornecem o valor da propriedade TimeSpan.Days, que
representa o número de dias inteiros no intervalo de tempo.

A cadeia de caracteres de saída inclui um número mínimo de dígitos especificado pelo


número de caracteres "d" no especificador de formato e é preenchido com zeros à
esquerda conforme necessário. Se os dígitos do número de dias ultrapassarem o
número de caracteres "d" no especificador de formato, o número total de dias será
fornecido na cadeia de caracteres de resultado.

O exemplo a seguir usa esses O especificadores de formato para exibir a representação


de cadeia de caracteres de dois valores de TimeSpan. O valor do componente de dias
do primeiro intervalo de tempo é zero; o valor do componente de dias do segundo é
365.

C#

TimeSpan ts1 = new TimeSpan(0, 23, 17, 47);


TimeSpan ts2 = new TimeSpan(365, 21, 19, 45);

for (int ctr = 2; ctr <= 8; ctr++)


{
string fmt = new String('d', ctr) + @"\.hh\:mm\:ss";
Console.WriteLine("{0} --> {1:" + fmt + "}", fmt, ts1);
Console.WriteLine("{0} --> {1:" + fmt + "}", fmt, ts2);
Console.WriteLine();
}
// The example displays the following output:
// dd\.hh\:mm\:ss --> 00.23:17:47
// dd\.hh\:mm\:ss --> 365.21:19:45
//
// ddd\.hh\:mm\:ss --> 000.23:17:47
// ddd\.hh\:mm\:ss --> 365.21:19:45
//
// dddd\.hh\:mm\:ss --> 0000.23:17:47
// dddd\.hh\:mm\:ss --> 0365.21:19:45
//
// ddddd\.hh\:mm\:ss --> 00000.23:17:47
// ddddd\.hh\:mm\:ss --> 00365.21:19:45
//
// dddddd\.hh\:mm\:ss --> 000000.23:17:47
// dddddd\.hh\:mm\:ss --> 000365.21:19:45
//
// ddddddd\.hh\:mm\:ss --> 0000000.23:17:47
// ddddddd\.hh\:mm\:ss --> 0000365.21:19:45
//
// dddddddd\.hh\:mm\:ss --> 00000000.23:17:47
// dddddddd\.hh\:mm\:ss --> 00000365.21:19:45

Voltar à tabela

Especificador de formato personalizado "h"


O especificador de formato personalizado "h" fornece o valor da propriedade
TimeSpan.Hours, que representa o número de horas inteiras no intervalo de tempo que
não são contadas como parte do componente de dia. Ele retorna um valor de cadeia de
caracteres de um dígito se o valor da propriedade TimeSpan.Hours for de 0 a 9 e
retorna um valor de cadeia de caracteres de dois dígitos se o valor da propriedade
TimeSpan.Hours for de 10 a 23.

Se o especificador de formato personalizado "h" for usado sozinho, especifique "%h"


para que ele não seja interpretado incorretamente como uma cadeia de caracteres de
formato padrão. O exemplo a seguir ilustra esse cenário.

C#

TimeSpan ts = new TimeSpan(3, 42, 0);


Console.WriteLine("{0:%h} hours {0:%m} minutes", ts);
// The example displays the following output:
// 3 hours 42 minutes

Normalmente, em uma operação de análise, uma cadeia de caracteres de entrada que


inclui apenas um único número é interpretada como o número de dias. Você pode usar
o especificador de formato personalizado "%h" em vez disso para interpretar a cadeia
de caracteres numérica como o número de horas. O exemplo a seguir ilustra esse
cenário.

C#

string value = "8";


TimeSpan interval;
if (TimeSpan.TryParseExact(value, "%h", null, out interval))
Console.WriteLine(interval.ToString("c"));
else
Console.WriteLine("Unable to convert '{0}' to a time interval",
value);
// The example displays the following output:
// 08:00:00

O exemplo a seguir ilustra o uso do especificador de formato personalizado “h”.


C#

TimeSpan ts1 = new TimeSpan(14, 3, 17);


Console.WriteLine(ts1.ToString(@"d\.h\:mm\:ss"));

TimeSpan ts2 = new TimeSpan(3, 4, 3, 17);


Console.WriteLine(ts2.ToString(@"d\.h\:mm\:ss"));
// The example displays the following output:
// 0.14:03:17
// 3.4:03:17

Voltar à tabela

Especificador de formato personalizado "hh"


O especificador de formato personalizado "hh" fornece o valor da propriedade
TimeSpan.Hours, que representa o número de horas inteiras no intervalo de tempo que
não são contadas como parte do componente de dia. Para valores de 0 a 9, a cadeia de
caracteres de saída inclui um zero à esquerda.

Normalmente, em uma operação de análise, uma cadeia de caracteres de entrada que


inclui apenas um único número é interpretada como o número de dias. Você pode usar
o especificador de formato personalizado "hh" em vez disso para interpretar a cadeia de
caracteres numérica como o número de horas. O exemplo a seguir ilustra esse cenário.

C#

string value = "08";


TimeSpan interval;
if (TimeSpan.TryParseExact(value, "hh", null, out interval))
Console.WriteLine(interval.ToString("c"));
else
Console.WriteLine("Unable to convert '{0}' to a time interval",
value);
// The example displays the following output:
// 08:00:00

O exemplo a seguir ilustra o uso do especificador de formato personalizado “hh”.

C#

TimeSpan ts1 = new TimeSpan(14, 3, 17);


Console.WriteLine(ts1.ToString(@"d\.hh\:mm\:ss"));

TimeSpan ts2 = new TimeSpan(3, 4, 3, 17);


Console.WriteLine(ts2.ToString(@"d\.hh\:mm\:ss"));
// The example displays the following output:
// 0.14:03:17
// 3.04:03:17

Voltar à tabela

Especificador de formato personalizado "m"


O especificador de formato personalizado "m" fornece o valor da propriedade
TimeSpan.Minutes, que representa o número de minutos inteiros no intervalo de tempo
que não são contados como parte do componente de dia. Ele retorna um valor de
cadeia de caracteres de um dígito se o valor da propriedade TimeSpan.Minutes for de 0
a 9 e retorna um valor de cadeia de caracteres de dois dígitos se o valor da propriedade
TimeSpan.Minutes for de 10 a 59.

Se o especificador de formato personalizado "m" for usado sozinho, especifique "%m"


para que ele não seja interpretado incorretamente como uma cadeia de caracteres de
formato padrão. O exemplo a seguir ilustra esse cenário.

C#

TimeSpan ts = new TimeSpan(3, 42, 0);


Console.WriteLine("{0:%h} hours {0:%m} minutes", ts);
// The example displays the following output:
// 3 hours 42 minutes

Normalmente, em uma operação de análise, uma cadeia de caracteres de entrada que


inclui apenas um único número é interpretada como o número de dias. Você pode usar
o especificador de formato personalizado "%m" em vez disso para interpretar a cadeia
de caracteres numérica como o número de minutos. O exemplo a seguir ilustra esse
cenário.

C#

string value = "3";


TimeSpan interval;
if (TimeSpan.TryParseExact(value, "%m", null, out interval))
Console.WriteLine(interval.ToString("c"));
else
Console.WriteLine("Unable to convert '{0}' to a time interval",
value);
// The example displays the following output:
// 00:03:00

O exemplo a seguir ilustra o uso do especificador de formato personalizado “m”.


C#

TimeSpan ts1 = new TimeSpan(0, 6, 32);


Console.WriteLine("{0:m\\:ss} minutes", ts1);

TimeSpan ts2 = new TimeSpan(3, 4, 3, 17);


Console.WriteLine("Elapsed time: {0:m\\:ss}", ts2);
// The example displays the following output:
// 6:32 minutes
// Elapsed time: 18:44

Voltar à tabela

Especificador de formato personalizado "mm"


O especificador de formato personalizado "mm" fornece o valor da propriedade
TimeSpan.Minutes, que representa o número de minutos inteiros no intervalo de tempo
que não está incluído como parte do componente de dia. Para valores de 0 a 9, a cadeia
de caracteres de saída inclui um zero à esquerda.

Normalmente, em uma operação de análise, uma cadeia de caracteres de entrada que


inclui apenas um único número é interpretada como o número de dias. Você pode usar
o especificador de formato personalizado "mm" em vez disso para interpretar a cadeia
de caracteres numérica como o número de minutos. O exemplo a seguir ilustra esse
cenário.

C#

string value = "07";


TimeSpan interval;
if (TimeSpan.TryParseExact(value, "mm", null, out interval))
Console.WriteLine(interval.ToString("c"));
else
Console.WriteLine("Unable to convert '{0}' to a time interval",
value);
// The example displays the following output:
// 00:07:00

O exemplo a seguir ilustra o uso do especificador de formato personalizado “mm”.

C#

TimeSpan departTime = new TimeSpan(11, 12, 00);


TimeSpan arriveTime = new TimeSpan(16, 28, 00);
Console.WriteLine("Travel time: {0:hh\\:mm}",
arriveTime - departTime);
// The example displays the following output:
// Travel time: 05:16

Voltar à tabela

Especificador de formato personalizado "s"


O especificador de formato personalizado "s" fornece o valor da propriedade
TimeSpan.Seconds, que representa o número de segundos inteiros no intervalo de
tempo que não são incluídos como parte do componente de horas, dias ou minutos. Ele
retorna um valor de cadeia de caracteres de um dígito se o valor da propriedade
TimeSpan.Seconds for de 0 a 9 e retorna um valor de cadeia de caracteres de dois
dígitos se o valor da propriedade TimeSpan.Seconds for de 10 a 59.

Se o especificador de formato personalizado "s" for usado sozinho, especifique "%s"


para que ele não seja interpretado incorretamente como uma cadeia de caracteres de
formato padrão. O exemplo a seguir ilustra esse cenário.

C#

TimeSpan ts = TimeSpan.FromSeconds(12.465);
Console.WriteLine(ts.ToString("%s"));
// The example displays the following output:
// 12

Normalmente, em uma operação de análise, uma cadeia de caracteres de entrada que


inclui apenas um único número é interpretada como o número de dias. Você pode usar
o especificador de formato personalizado "%s" em vez disso para interpretar a cadeia de
caracteres numérica como o número de segundos. O exemplo a seguir ilustra esse
cenário.

C#

string value = "9";


TimeSpan interval;
if (TimeSpan.TryParseExact(value, "%s", null, out interval))
Console.WriteLine(interval.ToString("c"));
else
Console.WriteLine("Unable to convert '{0}' to a time interval",
value);
// The example displays the following output:
// 00:00:09

O exemplo a seguir ilustra o uso do especificador de formato personalizado “s”.


C#

TimeSpan startTime = new TimeSpan(0, 12, 30, 15, 0);


TimeSpan endTime = new TimeSpan(0, 12, 30, 21, 3);
Console.WriteLine(@"Elapsed Time: {0:s\:fff} seconds",
endTime - startTime);
// The example displays the following output:
// Elapsed Time: 6:003 seconds

Voltar à tabela

Especificador de formato personalizado "ss"


O especificador de formato personalizado "ss" fornece o valor da propriedade
TimeSpan.Seconds, que representa o número de segundos inteiros no intervalo de
tempo que não são incluídos como parte do componente de horas, dias ou minutos.
Para valores de 0 a 9, a cadeia de caracteres de saída inclui um zero à esquerda.

Normalmente, em uma operação de análise, uma cadeia de caracteres de entrada que


inclui apenas um único número é interpretada como o número de dias. Você pode usar
o especificador de formato personalizado "ss" em vez disso para interpretar a cadeia de
caracteres numérica como o número de segundos. O exemplo a seguir ilustra esse
cenário.

C#

string[] values = { "49", "9", "06" };


TimeSpan interval;
foreach (string value in values)
{
if (TimeSpan.TryParseExact(value, "ss", null, out interval))
Console.WriteLine(interval.ToString("c"));
else
Console.WriteLine("Unable to convert '{0}' to a time interval",
value);
}
// The example displays the following output:
// 00:00:49
// Unable to convert '9' to a time interval
// 00:00:06

O exemplo a seguir ilustra o uso do especificador de formato personalizado “ss”.

C#

TimeSpan interval1 = TimeSpan.FromSeconds(12.60);


Console.WriteLine(interval1.ToString(@"ss\.fff"));
TimeSpan interval2 = TimeSpan.FromSeconds(6.485);
Console.WriteLine(interval2.ToString(@"ss\.fff"));
// The example displays the following output:
// 12.600
// 06.485

Voltar à tabela

Especificador de formato personalizado "f"


O especificador de formato personalizado "f" gera os décimos de segundo em um
intervalo de tempo. Em uma operação de formatação, os dígitos fracionários restantes
são truncados. Em uma operação de análise que chama o método TimeSpan.ParseExact
ou TimeSpan.TryParseExact, a cadeia de caracteres de entrada deve conter exatamente
um dígitos fracionários.

Se o especificador de formato personalizado "f" for usado sozinho, especifique "%F"


para que ele não seja interpretado incorretamente como uma cadeia de caracteres de
formato padrão.

O exemplo a seguir usa o especificador de formato personalizado "F" para exibir os


décimos de segundo em um valor de TimeSpan. "f" é usado primeiro como o
especificador de formato único e, depois, combinado com o especificador "s" em uma
cadeia de caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "ff"


O especificador de formato personalizado "ff" gera os centésimos de segundo em um
intervalo de tempo. Em uma operação de formatação, os dígitos fracionários restantes
são truncados. Em uma operação de análise que chama o método TimeSpan.ParseExact
ou TimeSpan.TryParseExact, a cadeia de caracteres de entrada deve conter exatamente
dois dígitos fracionários.

O exemplo a seguir usa o especificador de formato personalizado "FF" para exibir os


centésimos de segundo em um valor de TimeSpan. "ff" é usado primeiro como o
especificador de formato único e, depois, combinado com o especificador "s" em uma
cadeia de caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "fff"


O especificador de formato personalizado "fff" (com três caracteres "f") gera os
milissegundos em um intervalo de tempo. Em uma operação de formatação, os dígitos
fracionários restantes são truncados. Em uma operação de análise que chama o método
TimeSpan.ParseExact ou TimeSpan.TryParseExact, a cadeia de caracteres de entrada deve
conter exatamente três dígitos fracionários.

O exemplo a seguir usa o especificador de formato personalizado "fff" para exibir os


milissegundos em um valor de TimeSpan. "fff" é usado primeiro como o especificador
de formato único e, depois, combinado com o especificador "s" em uma cadeia de
caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "ffff"


O especificador de formato personalizado "ffff" (com quatro caracteres "f") gera os
décimos de milésimos de segundo em um intervalo de tempo. Em uma operação de
formatação, os dígitos fracionários restantes são truncados. Em uma operação de
análise que chama o método TimeSpan.ParseExact ou TimeSpan.TryParseExact, a cadeia
de caracteres de entrada deve conter exatamente quatro dígitos fracionários.

O exemplo a seguir usa o especificador de formato personalizado "ffff" para exibir os


décimos de milésimos de segundo em um valor de TimeSpan. "ffff" é usado primeiro
como o especificador de formato único e, depois, combinado com o especificador "s"
em uma cadeia de caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "fffff"


O especificador de formato personalizado "fffff" (com cinco caracteres "f") gera os
centésimos de milésimos de segundo em um intervalo de tempo. Em uma operação de
formatação, os dígitos fracionários restantes são truncados. Em uma operação de
análise que chama o método TimeSpan.ParseExact ou TimeSpan.TryParseExact, a cadeia
de caracteres de entrada deve conter exatamente cinco dígitos fracionários.

O exemplo a seguir usa o especificador de formato personalizado "fffff" para exibir os


centésimos de milésimos de segundo em um valor de TimeSpan. "fffff" é usado primeiro
como o especificador de formato único e, depois, combinado com o especificador "s"
em uma cadeia de caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "ffffff"


O especificador de formato personalizado "ffffff" (com seis caracteres "f") gera os
milionésimos de segundo em um intervalo de tempo. Em uma operação de formatação,
os dígitos fracionários restantes são truncados. Em uma operação de análise que chama
o método TimeSpan.ParseExact ou TimeSpan.TryParseExact, a cadeia de caracteres de
entrada deve conter exatamente seis dígitos fracionários.

O exemplo a seguir usa o especificador de formato personalizado "ffffff" para exibir os


milionésimos de segundo em um valor de TimeSpan. Ele é usado primeiro como o
especificador de formato único e, depois, combinado com o especificador "s" em uma
cadeia de caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "fffffff"


O especificador de formato personalizado "fffffff" (com sete caracteres "f") gera os
décimos de milionésimos de segundo (ou o número fracionário de marcadores) em um
intervalo de tempo. Em uma operação de análise que chama o método
TimeSpan.ParseExact ou TimeSpan.TryParseExact, a cadeia de caracteres de entrada deve
conter exatamente sete dígitos fracionários.

O exemplo a seguir usa o especificador de formato personalizado "fffffff" para exibir o


número fracionário de marcadores em um valor de TimeSpan. Ele é usado primeiro
como o especificador de formato único e, depois, combinado com o especificador "s"
em uma cadeia de caracteres de formato personalizado.

C#

TimeSpan ts = new TimeSpan(1003498765432);


string fmt;
Console.WriteLine(ts.ToString("c"));
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
if (fmt.Length == 1) fmt = "%" + fmt;
Console.WriteLine("{0,10}: {1:" + fmt + "}", fmt, ts);
}
Console.WriteLine();

for (int ctr = 1; ctr <= 7; ctr++) {


fmt = new String('f', ctr);
Console.WriteLine("{0,10}: {1:s\\." + fmt + "}", "s\\." + fmt, ts);
}
// The example displays the following output:
// %f: 8
// ff: 87
// fff: 876
// ffff: 8765
// fffff: 87654
// ffffff: 876543
// fffffff: 8765432
//
// s\.f: 29.8
// s\.ff: 29.87
// s\.fff: 29.876
// s\.ffff: 29.8765
// s\.fffff: 29.87654
// s\.ffffff: 29.876543
// s\.fffffff: 29.8765432

Voltar à tabela

Especificador de formato personalizado "F"


O especificador de formato personalizado "F" gera os décimos de segundo em um
intervalo de tempo. Em uma operação de formatação, os dígitos fracionários restantes
são truncados. Se o valor dos décimos de segundo do intervalo de tempo for zero, ele
não será incluído na cadeia de caracteres de resultado. Em uma operação de análise que
chama o método TimeSpan.ParseExact ou TimeSpan.TryParseExact, a presença de
décimos de um segundo dígito é opcional.

Se o especificador de formato personalizado "F" for usado sozinho, especifique "%F"


para que ele não seja interpretado incorretamente como uma cadeia de caracteres de
formato padrão.

O exemplo a seguir usa o especificador de formato personalizado "F" para exibir os


décimos de segundo em um valor de TimeSpan. Ele também usa este especificador de
formato personalizado em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.669");
Console.WriteLine("{0} ('%F') --> {0:%F}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.091");


Console.WriteLine("{0} ('ss\\.F') --> {0:ss\\.F}", ts2);
Console.WriteLine();
Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.1", "0:0:03.12" };
string fmt = @"h\:m\:ss\.F";
TimeSpan ts3;

foreach (string input in inputs) {


if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6690000 ('%F') --> 6
// 00:00:03.0910000 ('ss\.F') --> 03.
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.F') --> 00:00:03
// 0:0:03.1 ('h\:m\:ss\.F') --> 00:00:03.1000000
// Cannot parse 0:0:03.12 with 'h\:m\:ss\.F'.

Voltar à tabela

Especificador de formato personalizado "FF"


O especificador de formato personalizado "FF" gera os centésimos de segundo em um
intervalo de tempo. Em uma operação de formatação, os dígitos fracionários restantes
são truncados. Se houver zeros fracionários à direita, eles não serão incluídos na cadeia
de caracteres de resultado. Em uma operação de análise que chama o método
TimeSpan.ParseExact ou TimeSpan.TryParseExact, a presença de décimos e centésimos
de um segundo dígito é opcional.

O exemplo a seguir usa o especificador de formato personalizado "FF" para exibir os


centésimos de segundo em um valor de TimeSpan. Ele também usa este especificador
de formato personalizado em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.697");
Console.WriteLine("{0} ('FF') --> {0:FF}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.809");


Console.WriteLine("{0} ('ss\\.FF') --> {0:ss\\.FF}", ts2);
Console.WriteLine();

Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.1", "0:0:03.127" };
string fmt = @"h\:m\:ss\.FF";
TimeSpan ts3;

foreach (string input in inputs) {


if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6970000 ('FF') --> 69
// 00:00:03.8090000 ('ss\.FF') --> 03.8
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.FF') --> 00:00:03
// 0:0:03.1 ('h\:m\:ss\.FF') --> 00:00:03.1000000
// Cannot parse 0:0:03.127 with 'h\:m\:ss\.FF'.

Voltar à tabela

Especificador de formato personalizado "FFF"


O especificador de formato personalizado "FFF" (com três caracteres "F") gera os
milissegundos em um intervalo de tempo. Em uma operação de formatação, os dígitos
fracionários restantes são truncados. Se houver zeros fracionários à direita, eles não
serão incluídos na cadeia de caracteres de resultado. Em uma operação de análise que
chama o método TimeSpan.ParseExact ou TimeSpan.TryParseExact, a presença do dígito
de décimos, centésimos e milésimos de segundo é opcional.

O exemplo a seguir usa o especificador de formato personalizado "FFF" para exibir os


milésimos de segundo em um valor de TimeSpan. Ele também usa este especificador de
formato personalizado em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.6974");
Console.WriteLine("{0} ('FFF') --> {0:FFF}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.8009");


Console.WriteLine("{0} ('ss\\.FFF') --> {0:ss\\.FFF}", ts2);
Console.WriteLine();

Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.1279" };
string fmt = @"h\:m\:ss\.FFF";
TimeSpan ts3;
foreach (string input in inputs) {
if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6974000 ('FFF') --> 697
// 00:00:03.8009000 ('ss\.FFF') --> 03.8
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.FFF') --> 00:00:03
// 0:0:03.12 ('h\:m\:ss\.FFF') --> 00:00:03.1200000
// Cannot parse 0:0:03.1279 with 'h\:m\:ss\.FFF'.

Voltar à tabela

Especificador de formato personalizado "FFFF"


O especificador de formato personalizado "FFFF" (com quatro caracteres "f") gera os
décimos de milésimos de segundo em um intervalo de tempo. Em uma operação de
formatação, os dígitos fracionários restantes são truncados. Se houver zeros fracionários
à direita, eles não serão incluídos na cadeia de caracteres de resultado. Em uma
operação de análise que chama o método TimeSpan.ParseExact ou
TimeSpan.TryParseExact, a presença do dígito de décimos, centésimos, milésimos e
décimos de milésimos de segundo é opcional.

O exemplo a seguir usa o especificador de formato personalizado "FFFF" para exibir os


décimos de milésimos de segundo em um valor de TimeSpan. Ele também usa o
especificador de formato personalizado "FFFF" em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.69749");
Console.WriteLine("{0} ('FFFF') --> {0:FFFF}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.80009");


Console.WriteLine("{0} ('ss\\.FFFF') --> {0:ss\\.FFFF}", ts2);
Console.WriteLine();

Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.12795" };
string fmt = @"h\:m\:ss\.FFFF";
TimeSpan ts3;
foreach (string input in inputs) {
if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6974900 ('FFFF') --> 6974
// 00:00:03.8000900 ('ss\.FFFF') --> 03.8
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.FFFF') --> 00:00:03
// 0:0:03.12 ('h\:m\:ss\.FFFF') --> 00:00:03.1200000
// Cannot parse 0:0:03.12795 with 'h\:m\:ss\.FFFF'.

Voltar à tabela

Especificador de formato personalizado "FFFFF"


O especificador de formato personalizado "FFFFF" (com cinco caracteres "F") gera os
centésimos de milésimos de segundo em um intervalo de tempo. Em uma operação de
formatação, os dígitos fracionários restantes são truncados. Se houver zeros fracionários
à direita, eles não serão incluídos na cadeia de caracteres de resultado. Em uma
operação de análise que chama o método TimeSpan.ParseExact ou
TimeSpan.TryParseExact, a presença do dígito de décimos, centésimos, milésimos e
décimos de milésimos e centésimos de milésimos de segundo é opcional.

O exemplo a seguir usa o especificador de formato personalizado "FFFFF" para exibir os


centésimos de milésimos de segundo em um valor de TimeSpan. Ele também usa o
especificador de formato personalizado "FFFFF" em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.697497");
Console.WriteLine("{0} ('FFFFF') --> {0:FFFFF}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.800009");


Console.WriteLine("{0} ('ss\\.FFFFF') --> {0:ss\\.FFFFF}", ts2);
Console.WriteLine();

Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.127956" };
string fmt = @"h\:m\:ss\.FFFFF";
TimeSpan ts3;
foreach (string input in inputs) {
if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6974970 ('FFFFF') --> 69749
// 00:00:03.8000090 ('ss\.FFFFF') --> 03.8
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.FFFF') --> 00:00:03
// 0:0:03.12 ('h\:m\:ss\.FFFF') --> 00:00:03.1200000
// Cannot parse 0:0:03.127956 with 'h\:m\:ss\.FFFF'.

Voltar à tabela

Especificador de formato personalizado


"FFFFFF"
O especificador de formato personalizado "FFFFFF" (com seis caracteres "F") gera os
milionésimos de segundo em um intervalo de tempo. Em uma operação de formatação,
os dígitos fracionários restantes são truncados. Se houver zeros fracionários à direita,
eles não serão incluídos na cadeia de caracteres de resultado. Em uma operação de
análise que chama o método TimeSpan.ParseExact ou TimeSpan.TryParseExact, a
presença do dígito de décimos, centésimos, milésimos, décimos de milésimos,
centésimos de milésimos e milionésimos de segundo é opcional.

O exemplo a seguir usa o especificador de formato personalizado "FFFFFF" para exibir


os milionésimos de segundo em um valor de TimeSpan. Ele também usa este
especificador de formato personalizado em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.6974974");
Console.WriteLine("{0} ('FFFFFF') --> {0:FFFFFF}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.8000009");


Console.WriteLine("{0} ('ss\\.FFFFFF') --> {0:ss\\.FFFFFF}", ts2);
Console.WriteLine();

Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.1279569" };
string fmt = @"h\:m\:ss\.FFFFFF";
TimeSpan ts3;

foreach (string input in inputs) {


if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6974974 ('FFFFFF') --> 697497
// 00:00:03.8000009 ('ss\.FFFFFF') --> 03.8
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.FFFFFF') --> 00:00:03
// 0:0:03.12 ('h\:m\:ss\.FFFFFF') --> 00:00:03.1200000
// Cannot parse 0:0:03.1279569 with 'h\:m\:ss\.FFFFFF'.

Voltar à tabela

Especificador de formato personalizado


"FFFFFFF"
O especificador de formato personalizado "FFFFFFF" (com sete caracteres "F") gera os
décimos de milionésimos de segundo (ou o número fracionário de marcadores) em um
intervalo de tempo. Se houver zeros fracionários à direita, eles não serão incluídos na
cadeia de caracteres de resultado. Em uma operação de análise que chama o método
TimeSpan.ParseExact ou TimeSpan.TryParseExact, a presença dos sete dígitos
fracionários na cadeia de entrada é opcional.

O exemplo a seguir usa o especificador de formato personalizado "FFFFFFF" para exibir


as partes fracionárias de um segundo em um valor de TimeSpan. Ele também usa este
especificador de formato personalizado em uma operação de análise.

C#

Console.WriteLine("Formatting:");
TimeSpan ts1 = TimeSpan.Parse("0:0:3.6974974");
Console.WriteLine("{0} ('FFFFFFF') --> {0:FFFFFFF}", ts1);

TimeSpan ts2 = TimeSpan.Parse("0:0:3.9500000");


Console.WriteLine("{0} ('ss\\.FFFFFFF') --> {0:ss\\.FFFFFFF}", ts2);
Console.WriteLine();

Console.WriteLine("Parsing:");
string[] inputs = { "0:0:03.", "0:0:03.12", "0:0:03.1279569" };
string fmt = @"h\:m\:ss\.FFFFFFF";
TimeSpan ts3;

foreach (string input in inputs) {


if (TimeSpan.TryParseExact(input, fmt, null, out ts3))
Console.WriteLine("{0} ('{1}') --> {2}", input, fmt, ts3);
else
Console.WriteLine("Cannot parse {0} with '{1}'.",
input, fmt);
}
// The example displays the following output:
// Formatting:
// 00:00:03.6974974 ('FFFFFFF') --> 6974974
// 00:00:03.9500000 ('ss\.FFFFFFF') --> 03.95
//
// Parsing:
// 0:0:03. ('h\:m\:ss\.FFFFFFF') --> 00:00:03
// 0:0:03.12 ('h\:m\:ss\.FFFFFFF') --> 00:00:03.1200000
// 0:0:03.1279569 ('h\:m\:ss\.FFFFFFF') --> 00:00:03.1279569

Voltar à tabela

Outros caracteres
Qualquer outro caractere sem escape em uma cadeia de caracteres de formato,
incluindo um caractere de espaço em branco, é interpretado como um especificador de
formato personalizado. Na maioria dos casos, a presença de qualquer outro caractere
sem escape resulta em uma FormatException.

Há duas maneiras de incluir um caractere literal em uma cadeia de caracteres de


formato:

Coloque-o entre aspas simples (o delimitador de cadeia de caracteres literal).

Preceda-o com uma barra invertida ("\"), que é interpretada como caractere de
escape. Isso significa que, em C#, a cadeia de caracteres de formato deve
demarcado por @, ou o caractere literal deve ser precedido por uma barra
invertida adicional.

Em alguns casos, talvez você precise usar lógica condicional para incluir um literal
de escape em uma cadeia de caracteres de formato. O exemplo a seguir usa lógica
condicional para incluir um símbolo de sinal para intervalos de tempo negativos.

C#

using System;
public class Example
{
public static void Main()
{
TimeSpan result = new DateTime(2010, 01, 01) - DateTime.Now;
String fmt = (result < TimeSpan.Zero ? "\\-" : "") +
"dd\\.hh\\:mm";

Console.WriteLine(result.ToString(fmt));
Console.WriteLine("Interval: {0:" + fmt + "}", result);
}
}
// The example displays output like the following:
// -1291.10:54
// Interval: -1291.10:54

O .NET não define uma gramática para separadores em intervalos de tempo. Isso
significa que os separadores entre dias e horas, horas e minutos, minutos e segundos, e
segundos e frações de segundo devem ser tratados como literais de caracteres em uma
cadeia de caracteres de formato.

O exemplo a seguir usa o caractere de escape e a aspa simples para definir uma cadeia
de caracteres de formato personalizado que inclui a palavra "minutos" na cadeia de
caracteres de saída.

C#

TimeSpan interval = new TimeSpan(0, 32, 45);


// Escape literal characters in a format string.
string fmt = @"mm\:ss\ \m\i\n\u\t\e\s";
Console.WriteLine(interval.ToString(fmt));
// Delimit literal characters in a format string with the ' symbol.
fmt = "mm':'ss' minutes'";
Console.WriteLine(interval.ToString(fmt));
// The example displays the following output:
// 32:45 minutes
// 32:45 minutes

Voltar à tabela

Confira também
Formatar tipos
Cadeias de caracteres de formato TimeSpan padrão
Cadeias de caracteres de formato de
enumeração
Artigo • 19/06/2023

Você pode usar o método Enum.ToString para criar um novo objeto de cadeia de
caracteres que representa o valor numérico, hexadecimal ou de cadeia de caracteres de
um membro da enumeração. Esse método usa uma das cadeias de caracteres de
formatação de enumeração para especificar o valor que você deseja que seja retornado.

As seções a seguir listam as cadeias de caracteres de formatação de enumeração e os


valores que elas retornam. Esses especificadores de formato não diferenciam maiúsculas
de minúsculas.

"G" ou "g"
Exibem a entrada de enumeração como um valor de cadeia de caracteres. Caso
contrário, é exibido o valor inteiro da instância atual. Se a enumeração for definida com
o FlagsAttribute definido, os valores de cadeia de caracteres de cada entrada válida são
concatenados, separados por vírgulas. Se o atributo Flags não estiver definido, um
valor inválido será exibido como uma entrada numérica. O exemplo a seguir ilustra o
especificador de formato G .

C#

Console.WriteLine(((DayOfWeek)7).ToString("G")); // 7
Console.WriteLine(ConsoleColor.Red.ToString("G")); // Red

var attributes = FileAttributes.Hidden | FileAttributes.Archive;


Console.WriteLine(attributes.ToString("G")); // Hidden, Archive

F ou f
Exibem a entrada de enumeração como um valor de cadeia de caracteres, se possível. Se
o valor puder ser exibido como uma soma das entradas na enumeração (mesmo que o
Flags atributo não esteja presente), os valores de cadeia de caracteres de cada entrada
válida serão concatenados, separados por vírgulas. Se não puder ser determinado pelas
entradas de enumeração, o valor será formatado como valor inteiro. O exemplo a seguir
ilustra o especificador de formato F .
C#

Console.WriteLine(((DayOfWeek)7).ToString("F")); // Monday, Saturday


Console.WriteLine(ConsoleColor.Blue.ToString("F")); // Blue

var attributes = FileAttributes.Hidden | FileAttributes.Archive;


Console.WriteLine(attributes.ToString("F")); // Hidden, Archive

D ou d
Exibem a entrada de enumeração como um valor inteiro na representação mais curta
possível. O exemplo a seguir ilustra o especificador de formato D .

C#

Console.WriteLine(((DayOfWeek)7).ToString("D")); // 7
Console.WriteLine(ConsoleColor.Cyan.ToString("D")); // 11

var attributes = FileAttributes.Hidden | FileAttributes.Archive;


Console.WriteLine(attributes.ToString("D")); // 34

"X" ou "x"
Exibe a entrada de enumeração como um valor hexadecimal. O valor é representado
com zeros à esquerda conforme o necessário, para garantir que a cadeia de caracteres
resultante tenha dois caracteres para cada byte no tipo de enumeração tipo numérico
subjacente. O exemplo a seguir ilustra o especificador de formato X. No exemplo, o tipo
subjacente de DayOfWeek, ConsoleColor e FileAttributes é Int32, ou um inteiro de 32
bits (ou 4 bytes), que produz uma cadeia de caracteres resultante de oito caracteres.

C#

Console.WriteLine(((DayOfWeek)7).ToString("X")); // 00000007
Console.WriteLine(ConsoleColor.Cyan.ToString("X")); // 0000000B

var attributes = FileAttributes.Hidden | FileAttributes.Archive;


Console.WriteLine(attributes.ToString("X")); // 00000022

Exemplo
O exemplo a seguir define uma enumeração chamada Colors , que consiste em três
entradas: Red , Blue e Green .
C#

public enum Color { Red = 1, Blue = 2, Green = 3 };

Após a enumeração ser definida, uma instância pode ser declarada da seguinte maneira.

C#

Color myColor = Color.Green;

O método Color.ToString(System.String) pode, então, ser usado para exibir o valor da


enumeração de diferentes maneiras, dependendo do especificador de formato passado
para ele.

C#

Console.WriteLine("The value of myColor is {0}.",


myColor.ToString("G"));
Console.WriteLine("The value of myColor is {0}.",
myColor.ToString("F"));
Console.WriteLine("The value of myColor is {0}.",
myColor.ToString("D"));
Console.WriteLine("The value of myColor is 0x{0}.",
myColor.ToString("X"));
// The example displays the following output to the console:
// The value of myColor is Green.
// The value of myColor is Green.
// The value of myColor is 3.
// The value of myColor is 0x00000003.

Confira também
Formatando tipos
Formatação de composição
Artigo • 08/03/2024

O recurso de formatação de composição do .NET utiliza uma lista de objetos e uma


cadeia de caracteres de formato de composição como entrada. Uma cadeia de
caracteres de formato composto consiste em texto fixo combinado com espaços
reservados indexados, chamados de itens de formato. Esses itens de formato
correspondem aos objetos na lista. A operação de formatação produz uma cadeia de
caracteres de resultado que consiste no texto fixo original intercalado com a
representação de cadeia de caracteres dos objetos na lista.

) Importante

Em vez de usar cadeias de caracteres de formato composto, você pode usar cadeias
de caracteres interpoladas se a linguagem e a versão usada dão suporte a elas. Uma
cadeia de caracteres interpolada contém expressões interpoladas. Cada expressão
interpolada é resolvida com o valor da expressão e incluída na cadeia de caracteres
resultante quando a cadeia de caracteres é atribuída. Para saber mais, confira
Interpolação de cadeia de caracteres (Referência de C#) ou Cadeias de caracteres
interpoladas (Referência do Visual Basic).

Os métodos a seguir dão suporte ao recurso de formatação de composição:

String.Format, que retorna uma cadeia de caracteres de resultado formatada.


StringBuilder.AppendFormat, que acrescenta uma cadeia de caracteres de
resultado formatada a um objeto StringBuilder.
Algumas sobrecargas do método Console.WriteLine, as quais exibem uma cadeia
de caracteres de resultado formatada para o console.
Algumas sobrecargas do método TextWriter.WriteLine, as quais escrevem a cadeia
de caracteres de resultado formatada em um fluxo ou arquivo. As classes derivadas
de TextWriter, como StreamWriter e HtmlTextWriter, também têm essa
funcionalidade.
Debug.WriteLine(String, Object[]), que gera uma mensagem formatada para
rastrear ouvintes.
Os métodos Trace.TraceError(String, Object[]), Trace.TraceInformation(String,
Object[]) e Trace.TraceWarning(String, Object[]), os quais produzem mensagens
formatadas rastrear ouvintes.
O método TraceSource.TraceInformation(String, Object[]), que grava um método
informativo para rastrear ouvintes.
Cadeia de formato composto
Uma cadeia de formato de composição e uma lista de objetos são usadas como
argumentos dos métodos que dão suporte ao recurso de formatação de composição.
Uma cadeia de formato de composição consiste em zero ou mais sequências de texto
fixo intercaladas com um ou mais itens de formato. O texto fixo é qualquer cadeia de
caracteres que você escolher e cada item de formato corresponde a um objeto ou a
uma estrutura demarcada na lista. A representação de cadeia de caracteres de cada
objeto substitui o item de formato correspondente.

Considere o seguinte fragmento de código Format:

C#

string.Format("Name = {0}, hours = {1:hh}", "Fred", DateTime.Now);

O texto fixo é Name = e , hours = . Os itens de formato são {0} , cujo índice 0
corresponde ao objeto name , e {1:hh} , cujo índice 1 corresponde ao objeto
DateTime.Now .

Sintaxe do item de formato


Cada item de formato assume a forma a seguir e consiste nos seguintes componentes:

{index[,alignment][:formatString]}

As chaves correspondentes ( { e } ) são necessárias.

Componente de índice
O componente obrigatório index, também chamado de especificador de parâmetro, é
um número iniciado por 0 que identifica um item correspondente na lista de objetos.
Ou seja, o item de formato cujo especificador de parâmetro é 0 formata o primeiro
objeto na lista. O item de formato cujo especificador de parâmetro é 1 formata o
segundo objeto na lista e assim por diante. O seguinte exemplo inclui quatro
especificadores de parâmetro, numerados de zero a três, para representar números
primos inferiores a 10:

C#

string.Format("Name = {0}, hours = {1:hh}", "Fred", DateTime.Now);


Vários itens de formato podem fazer referência ao mesmo elemento na lista de objetos
ao especificar o mesmo especificador de parâmetro. Por exemplo, você pode formatar o
mesmo valor numérico no formato hexadecimal, científico e numérico especificando
uma cadeia de caracteres de formato composto, como "0x{0:X} {0:E} {0:N}" , como
mostra o seguinte exemplo:

C#

string multiple = string.Format("0x{0:X} {0:E} {0:N}",


Int64.MaxValue);
Console.WriteLine(multiple);

// The example displays the following output:


// 0x7FFFFFFFFFFFFFFF 9.223372E+018 9,223,372,036,854,775,807.00

Cada item de formato pode fazer referência a qualquer objeto na lista. Por exemplo, se
houver três objetos, você poderá formatar o segundo, o primeiro e o terceiro objeto
especificando uma cadeia de caracteres de formato composto, como {1} {0} {2} . Um
objeto que não é referenciado por um item de formato é ignorado. Uma
FormatException será gerada em tempo de execução se um especificador de parâmetro
designar um item fora dos limites da lista de objetos.

Componente de alinhamento
O componente opcional alignment é um inteiro com sinal que indica a largura
preferencial do campo formatado. Se o valor de alignment for menor que o
comprimento da cadeia de caracteres formatada, alignment será ignorado e o
comprimento da cadeia de caracteres formatada será usado como a largura do campo.
Os dados formatados no campo serão alinhados à direita se alignment for positivo e
serão alinhados à esquerda se alignment for negativo. Se for necessário preenchimento,
espaços em branco serão usados. A vírgula é necessária se alignment for especificado.

O exemplo a seguir define duas matrizes, uma contendo os nomes dos funcionários e a
outra contendo as horas em que eles trabalharam ao longo de duas semanas. A cadeia
de caracteres de formato composto alinha à esquerda os nomes em um campo de 20
caracteres e alinha à direita as horas em um campo de 5 caracteres. A cadeia de
caracteres de formato padrão "N1" formata as horas com um dígito fracionário.

C#

string[] names = { "Adam", "Bridgette", "Carla", "Daniel",


"Ebenezer", "Francine", "George" };
decimal[] hours = { 40, 6.667m, 40.39m, 82,
40.333m, 80, 16.75m };
Console.WriteLine("{0,-20} {1,5}\n", "Name", "Hours");

for (int counter = 0; counter < names.Length; counter++)


Console.WriteLine("{0,-20} {1,5:N1}", names[counter], hours[counter]);

// The example displays the following output:


// Name Hours
//
// Adam 40.0
// Bridgette 6.7
// Carla 40.4
// Daniel 82.0
// Ebenezer 40.3
// Francine 80.0
// George 16.8

Componente da cadeia de caracteres de formato


O componente formatString opcional é uma cadeia de caracteres de formato apropriada
para o tipo de objeto que está sendo formatado. É possível especificar:

Uma cadeia de caracteres de formato numérico padrão ou personalizado se o


objeto correspondente for um valor numérico.
Uma cadeia de caracteres de formato de data e hora padrão ou personalizado se o
objeto correspondente for um objeto DateTime.
Uma cadeia de caracteres de formato de enumeração se o objeto correspondente
for um valor de enumeração.

Se formatString não for especificado, o especificador de formato geral ("G") de um tipo


numérico, de data e hora ou de enumeração será usado. Os dois-pontos são necessários
quando formatString é especificado.

A tabela a seguir lista tipos ou categorias de tipos na biblioteca de classes .NET que dão
suporte a um conjunto predefinido de cadeias de caracteres de formato e fornece links
para os artigos que listam as cadeias de caracteres de formato com suporte. A
formatação de cadeia de caracteres é um mecanismo extensível que possibilita definir
novas cadeias de caracteres de formato para todos os tipos existentes e definir um
conjunto de cadeias de caracteres de formato compatíveis com um tipo definido pelo
aplicativo.

Para obter mais informações, confira os artigos de interface IFormattable e


ICustomFormatter.

ノ Expandir a tabela
Tipo ou categoria de tipo Consulte

Tipos de data e hora (DateTime, DateTimeOffset) Cadeias de caracteres de formato de


data e hora padrão

Cadeias de caracteres de formato de


data e hora personalizado

Tipos de enumeração (todos os tipos derivados de Cadeias de Caracteres de Formato de


System.Enum) Enumeração

Tipos numéricos (BigInteger, Byte, Decimal, Double, Cadeias de Caracteres de Formato


Int16, Int32, Int64, SByte, Single, UInt16, UInt32, UInt64) Numérico Padrão

Cadeias de caracteres de formato


numérico personalizado

Guid Guid.ToString(String)

TimeSpan Cadeias de caracteres de formato


TimeSpan padrão

Cadeias de caracteres de formato


TimeSpan personalizado

Chaves de escape
As chaves de abertura e fechamento são interpretadas como o início e o fim de um item
de formato. Para exibir uma chave de abertura literal ou uma chave de fechamento,
você precisa usar uma sequência de escape. Especifique duas chaves de abertura ( {{ )
no texto fixo para exibir uma chave de abertura ( { ) ou duas chaves de fechamento ( }} )
para exibir uma chave de fechamento ( } ).

Chaves com escape com um item de formato são analisadas de forma diferente entre o
.NET e o .NET Framework.

.NET

Chaves podem ser escapadas em torno de um item de formato. Por exemplo, considere
o item de formato {{{0:D}}} , que se destina a exibir uma chave de abertura, um valor
numérico formatado como um número decimal e uma chave de fechamento. O item de
formato é interpretado da seguinte maneira:

1. As duas primeiras chaves de abertura ( {{ ) têm escape e produzem uma chave de


abertura.
2. Os próximos três caracteres ( {0: ) são interpretados como o início de um item de
formato.
3. O próximo caractere ( D ) é interpretado como o especificador de formato numérico
padrão decimal.
4. A última chave ( } ) é interpretada como o final do item de formato.
5. As duas últimas chaves de fechamento são escapadas e produzem uma chave de
fechamento.
6. O resultado final exibido é a cadeia de caracteres literal, {6324} .

C#

int value = 6324;


string output = string.Format("{{{0:D}}}", value);

Console.WriteLine(output);
// The example displays the following output:
// {6324}

.NET Framework
As chaves em um item de formato são interpretadas sequencialmente na ordem em que
são encontradas. Não há suporte para interpretar chaves aninhadas.

A forma como as chaves de escape são interpretadas pode levar a resultados


inesperados. Por exemplo, considere o item de formato {{{0:D}}} , que se destina a
exibir uma chave de abertura, um valor numérico formatado como um número decimal
e uma chave de fechamento. No entanto, o item de formato é interpretado da seguinte
maneira:

1. As duas primeiras chaves de abertura ( {{ ) têm escape e produzem uma chave de


abertura.
2. Os próximos três caracteres ( {0: ) são interpretados como o início de um item de
formato.
3. O próximo caractere ( D ) seria interpretado como o especificador de formato
numérico padrão decimal, mas as duas chaves de escape seguintes ( }} ) produzem
uma só chave. Como a cadeia de caracteres resultante ( D} ) não é um especificador
de formato numérico padrão, a cadeia de caracteres resultante é interpretada
como uma cadeia de caracteres de formato personalizado que significa exibir a
cadeia de caracteres literal D} .
4. A última chave ( } ) é interpretada como o final do item de formato.
5. O resultado final exibido é a cadeia de caracteres literal, {D} . O valor numérico que
deveria ser formatado não é exibido.

C#

int value = 6324;


string output = string.Format("{{{0:D}}}",
value);
Console.WriteLine(output);

// The example displays the following output:


// {D}

Uma forma de escrever o código para evitar a interpretação incorreta de chaves com
escape e formatar itens é formatar as chaves e os itens separadamente. Ou seja, na
primeira operação de formato, exiba uma chave de abertura literal. Na próxima
operação, exiba o resultado do item de formato e, na operação final, exiba uma chave
de fechamento literal. O seguinte exemplo ilustra esta abordagem:

C#

int value = 6324;


string output = string.Format("{0}{1:D}{2}",
"{", value, "}");
Console.WriteLine(output);

// The example displays the following output:


// {6324}

Ordem de processamento
Se a chamada para o método de formatação composta incluir um argumento
IFormatProvider cujo valor não seja null , o runtime chamará o método
IFormatProvider.GetFormat para solicitar uma implementação de ICustomFormatter. Se
o método puder retornar uma implementação de ICustomFormatter, ele será
armazenado em cache durante a chamada do método de formatação de composição.

Cada valor na lista de parâmetros que corresponde a um item de formato é convertido


em uma cadeia de caracteres da seguinte maneira:

1. Se o valor a ser formatado for null , uma cadeia de caracteres vazia String.Empty
será retornada.

2. Se uma implementação de ICustomFormatter estiver disponível, o runtime


chamará seu método Format. O runtime passa o valor de formatString do item de
formato (ou null se ele não estiver presente) para o método. O runtime também
passa a implementação de IFormatProvider para o método. Se a chamada ao
método ICustomFormatter.Format retornar null , a execução prosseguirá para a
próxima etapa. Caso contrário, o resultado da chamada ICustomFormatter.Format
será retornado.

3. Se o valor implementa a interface IFormattable, o método ToString(String,


IFormatProvider) da interface é chamado. Se algum estiver presente no item de
formato, o valor de formatString será passado para o método. Caso contrário, null
será passado. O argumento IFormatProvider é determinado da seguinte forma:

Para um valor numérico, se um método de formatação composto com um


argumento IFormatProvider não nulo é chamado, o runtime solicita um
objeto NumberFormatInfo do seu método IFormatProvider.GetFormat. Se ele
não conseguir fornecer, se o valor do argumento for null ou se o método de
formatação composta não tiver um parâmetro IFormatProvider, será usado o
objeto NumberFormatInfo para a cultura atual.

Para um valor de data e hora, se um método de formatação composto com


um argumento IFormatProvider não nulo é chamado, o runtime solicita um
objeto DateTimeFormatInfo do seu método IFormatProvider.GetFormat. Nas
seguintes situações, o objeto DateTimeFormatInfo da cultura atual é usado:
O método IFormatProvider.GetFormat não pode fornecer um objeto
DateTimeFormatInfo.
O valor do argumento é null .
O método de formatação composta não tem um parâmetro
IFormatProvider.

Para objetos de outros tipos, se um método de formatação de composição


for chamado com um argumento IFormatProvider, seu valor é passado
diretamente para a implementação IFormattable.ToString. Caso contrário,
null é passado para a implementação IFormattable.ToString.

4. O método sem parâmetros ToString do tipo, o qual substitui Object.ToString() ou


herda o comportamento da sua classe base, é chamado. Nesse caso, a cadeia de
caracteres de formato especificada pelo componente formatString no item de
formato, quando presente, é ignorada.

O alinhamento é aplicado após as etapas anteriores terem sido executadas.

Exemplos de código
O exemplo a seguir mostra uma cadeia de caracteres criada usando formatação de
composição e outra criada usando o método ToString de um objeto. Os dois tipos de
formatação produzem resultados equivalentes.

C#

string formatString1 = string.Format("{0:dddd MMMM}", DateTime.Now);


string formatString2 = DateTime.Now.ToString("dddd MMMM");

Supondo que o dia atual seja uma quinta-feira de maio, o valor de ambas as cadeias de
caracteres no exemplo anterior é Thursday May na cultura do inglês dos EUA.

Console.WriteLine expõe a mesma funcionalidade que String.Format. A única diferença


entre os dois métodos é que String.Format retorna o resultado como uma cadeia de
caracteres, enquanto que Console.WriteLine grava o resultado no fluxo de saída
associado ao objeto Console. O seguinte exemplo usa o método Console.WriteLine para
formatar o valor de myNumber para um valor de moeda:

C#

int myNumber = 100;


Console.WriteLine("{0:C}", myNumber);

// The example displays the following output


// if en-US is the current culture:
// $100.00

O seguinte exemplo demonstra a formatação de vários objetos, incluindo a formatação


de um objeto de duas maneiras diferentes:

C#

string myName = "Fred";


Console.WriteLine(string.Format("Name = {0}, hours = {1:hh}, minutes =
{1:mm}",
myName, DateTime.Now));

// Depending on the current time, the example displays output like the
following:
// Name = Fred, hours = 11, minutes = 30

O exemplo a seguir demonstra o uso de alinhamento na formatação. Os argumentos


que são formatados são colocados entre caracteres de barra vertical ( | ) para realçar o
alinhamento resultante.
C#

string firstName = "Fred";


string lastName = "Opals";
int myNumber = 100;

string formatFirstName = string.Format("First Name = |{0,10}|", firstName);


string formatLastName = string.Format("Last Name = |{0,10}|", lastName);
string formatPrice = string.Format("Price = |{0,10:C}|", myNumber);
Console.WriteLine(formatFirstName);
Console.WriteLine(formatLastName);
Console.WriteLine(formatPrice);
Console.WriteLine();

formatFirstName = string.Format("First Name = |{0,-10}|", firstName);


formatLastName = string.Format("Last Name = |{0,-10}|", lastName);
formatPrice = string.Format("Price = |{0,-10:C}|", myNumber);
Console.WriteLine(formatFirstName);
Console.WriteLine(formatLastName);
Console.WriteLine(formatPrice);

// The example displays the following output on a system whose current


// culture is en-US:
// First Name = | Fred|
// Last Name = | Opals|
// Price = | $100.00|
//
// First Name = |Fred |
// Last Name = |Opals |
// Price = |$100.00 |

Confira também
WriteLine
String.Format
Interpolação de cadeia de caracteres (C#)
Interpolação de cadeia de caracteres (Visual Basic)
Formatar tipos
Cadeias de Caracteres de Formato Numérico Padrão
Cadeias de caracteres de formato numérico personalizado
Cadeias de caracteres de formato de data e hora padrão
Cadeias de caracteres de formato de data e hora personalizado
Cadeias de caracteres de formato TimeSpan padrão
Cadeias de caracteres de formato TimeSpan personalizado
Cadeias de Caracteres de Formato de Enumeração
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como preencher um número com zeros
à esquerda
Artigo • 08/06/2023

É possível adicionar zeros à esquerda de um inteiro usando a cadeia de caracteres de


formato numérico padrão “D” com um especificador de precisão. Você pode adicionar
zeros à esquerda de inteiros e pontos flutuantes usando uma cadeia de caracteres de
formato numérico personalizado. Este artigo mostra como usar os dois métodos para
preencher um número com zeros à esquerda.

Para acrescentar um número inteiro com zeros


à esquerda com um comprimento específico
1. Determine o número mínimo de dígitos que o valor inteiro deve exibir. Inclua os
dígitos à esquerda nesse número.

2. Determine se quer exibir o inteiro como decimal ou hexadecimal.

Para exibir o inteiro como um valor decimal, chame seu método


ToString(String) e passe a cadeia de caracteres "Dn" como o valor do

parâmetro format , em que n representa o tamanho mínimo da cadeia de


caracteres.

Para exibir o número inteiro como hexadecimal, chame seu método


ToString(String) e informe a cadeia de caracteres “Xn” como valor do
parâmetro format, em que n representa o comprimento mínimo da cadeia de
caracteres.

Você também pode usar a cadeia de caracteres de formato em uma cadeia de


caracteres interpolada em C# e no Visual Basic. Como alternativa, você pode chamar um
método como String.Format ou Console.WriteLine que usa formatação composta.

O exemplo a seguir formata diversos valores inteiros com zeros à esquerda para que o
comprimento total do número formatado tenha pelo menos oito caracteres.

C#

byte byteValue = 254;


short shortValue = 10342;
int intValue = 1023983;
long lngValue = 6985321;
ulong ulngValue = UInt64.MaxValue;

// Display integer values by calling the ToString method.


Console.WriteLine("{0,22} {1,22}", byteValue.ToString("D8"),
byteValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", shortValue.ToString("D8"),
shortValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", intValue.ToString("D8"),
intValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", lngValue.ToString("D8"),
lngValue.ToString("X8"));
Console.WriteLine("{0,22} {1,22}", ulngValue.ToString("D8"),
ulngValue.ToString("X8"));
Console.WriteLine();

// Display the same integer values by using composite formatting.


Console.WriteLine("{0,22:D8} {0,22:X8}", byteValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", shortValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", intValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", lngValue);
Console.WriteLine("{0,22:D8} {0,22:X8}", ulngValue);
// The example displays the following output:
// 00000254 000000FE
// 00010342 00002866
// 01023983 000F9FEF
// 06985321 006A9669
// 18446744073709551615 FFFFFFFFFFFFFFFF
//
// 00000254 000000FE
// 00010342 00002866
// 01023983 000F9FEF
// 06985321 006A9669
// 18446744073709551615 FFFFFFFFFFFFFFFF
// 18446744073709551615 FFFFFFFFFFFFFFFF

Para acrescentar um número inteiro com uma


determinada quantidade de zeros à esquerda
1. Determine quantos zeros à esquerda o valor inteiro deve exibir.

2. Determine se quer exibir o inteiro como um valor decimal ou hexadecimal.

A formatação como um valor decimal requer o especificador de formato


padrão "D".

A formatação como um valor hexadecimal requer que você use o


especificador de formato padrão "X".
3. Determine o comprimento da cadeia de caracteres numérica não acrescentada.
Para isso, chame o método ToString("D").Length ou ToString("X").Length do
valor inteiro.

4. Adicione ao tamanho da cadeia de caracteres numérica não formatada o número


de zeros à esquerda desejados na cadeia de caracteres formatada. O resultado é o
tamanho total da cadeia de caracteres formatada.

5. Chame o método ToString(String) do valor inteiro e informe a cadeia de


caracteres “Dn” para cadeias de caracteres decimais e “Xn” para cadeias de
caracteres hexadecimais, em que n representa o comprimento total da cadeia de
caracteres acrescentada. Você também pode usar uma cadeia de caracteres de
formato “Dn” ou “Xn” em um método com suporte para formatação de
composição.

O seguinte exemplo acrescenta um valor inteiro com cinco dígitos zero à esquerda:

C#

int value = 160934;


int decimalLength = value.ToString("D").Length + 5;
int hexLength = value.ToString("X").Length + 5;
Console.WriteLine(value.ToString("D" + decimalLength.ToString()));
Console.WriteLine(value.ToString("X" + hexLength.ToString()));
// The example displays the following output:
// 00000160934
// 00000274A6

Para acrescentar um valor numérico com zeros


à esquerda com comprimento específico
1. Determine quantos dígitos a representação da cadeia de caracteres de números
deve ter à esquerda dos decimais. Inclua os dígitos zero à esquerda no número
total de dígitos.

2. Defina uma cadeia de caracteres de formato numérico personalizado que usa o


espaço reservado com valor zero (“0”) para representar a quantidade mínima de
dígitos zero.

3. Chame o método ToString(String) do número e apresente-o à cadeia de


caracteres de formato personalizado. Você também pode usar uma cadeia de
caracteres de formato personalizado com interpolação de cadeia de caracteres ou
um método com suporte para formatação de composição.
O exemplo a seguir formata diversos valores numéricos com zeros à esquerda. Como
resultado, o comprimento total do número formatado é pelo menos oito dígitos à
esquerda do separador decimal.

C#

string fmt = "00000000.##";


int intValue = 1053240;
decimal decValue = 103932.52m;
float sngValue = 1549230.10873992f;
double dblValue = 9034521202.93217412;

// Display the numbers using the ToString method.


Console.WriteLine(intValue.ToString(fmt));
Console.WriteLine(decValue.ToString(fmt));
Console.WriteLine(sngValue.ToString(fmt));
Console.WriteLine(dblValue.ToString(fmt));
Console.WriteLine();

// Display the numbers using composite formatting.


string formatString = " {0,15:" + fmt + "}";
Console.WriteLine(formatString, intValue);
Console.WriteLine(formatString, decValue);
Console.WriteLine(formatString, sngValue);
Console.WriteLine(formatString, dblValue);
// The example displays the following output:
// 01053240
// 00103932.52
// 01549230
// 9034521202.93
//
// 01053240
// 00103932.52
// 01549230
// 9034521202.93

Para acrescentar um valor numérico com uma


determinada quantidade de zeros à esquerda
1. Determine quantos zeros à esquerda o valor numérico deve ter.

2. Determine a quantidade de dígitos à esquerda do decimal na cadeia de caracteres


numéricos não preenchida:

a. Determine se a representação da cadeia de caracteres de um número inclui um


símbolo de ponto decimal.
b. Caso o símbolo esteja presente, determine a quantidade de caracteres à
esquerda do ponto decimal. Caso o símbolo de decimal não esteja presente,
determine o comprimento da cadeia de caracteres.

3. Crie uma cadeia de caracteres de formato personalizado que use:

O espaço reservado para zero (“0”) para cada um dos zeros à esquerda que
aparecem na cadeia de caracteres.

O espaço reservado para zero ou o espaço reservado de dígito “#” para


representar cada dígito na cadeia de caracteres padrão.

4. Forneça a cadeia de caracteres de formato personalizado como parâmetro para o


método ToString(String) do número ou para um método que use a formatação
composta.

O exemplo abaixo acrescenta dois valores Double com cinco dígitos zero à esquerda:

C#

double[] dblValues = { 9034521202.93217412, 9034521202 };


foreach (double dblValue in dblValues)
{
string decSeparator =
System.Globalization.NumberFormatInfo.CurrentInfo.NumberDecimalSeparator;
string fmt, formatString;

if (dblValue.ToString().Contains(decSeparator))
{
int digits = dblValue.ToString().IndexOf(decSeparator);
fmt = new String('0', 5) + new String('#', digits) + ".##";
}
else
{
fmt = new String('0', dblValue.ToString().Length);
}
formatString = "{0,20:" + fmt + "}";

Console.WriteLine(dblValue.ToString(fmt));
Console.WriteLine(formatString, dblValue);
}
// The example displays the following output:
// 000009034521202.93
// 000009034521202.93
// 9034521202
// 9034521202

Confira também
Cadeias de caracteres de formato numérico personalizado
Cadeias de Caracteres de Formato Numérico Padrão
Formatação composta
Como: Extrair o dia da semana de uma
data específica
Artigo • 09/05/2023

O .NET facilita a verificação da posição numérica do dia da semana em uma


determinada data e a exibição do nome do dia localizado em uma determinada data. O
valor enumerado que indica o dia da semana correspondente a uma determinada data
está disponível na propriedade DayOfWeek ou DayOfWeek. Em contrapartida, para
recuperar o nome do dia da semana é necessário realizar uma operação de formatação
que pode ser executada com a chamada de um método de formatação, como o método
ToString ou String.Format de valor de data e hora. Este tópico mostra como executar

essas operações de formatação.

Extrair um número que indique o dia da


semana
1. Use o método estático DateTime.Parse ou DateTimeOffset.Parse para converter a
representação de cadeia de caracteres de uma data em um valor DateTimeou
DateTimeOffset.

2. Use a propriedade DateTime.DayOfWeek ou DateTimeOffset.DayOfWeek para


recuperar um valor de DayOfWeek que indica o dia da semana.

3. Se necessário, execute coerção (no C#) ou converta (no Visual Basic) o valor de
DayOfWeek para um número inteiro.

O exemplo a seguir mostra um inteiro que representa o dia da semana de uma


determinada data.

C#

using System;

public class Example


{
public static void Main()
{
DateTime dateValue = new DateTime(2008, 6, 11);
Console.WriteLine((int) dateValue.DayOfWeek);
}
}
// The example displays the following output:
// 3
Extrair o nome abreviado do dia da semana
1. Use o método estático DateTime.Parse ou DateTimeOffset.Parse para converter a
representação de cadeia de caracteres de uma data em um valor DateTimeou
DateTimeOffset.

2. Você pode extrair o nome de dia da semana abreviado da cultura atual ou de uma
cultura específica:

a. Para extrair o nome de dia da semana abreviado da cultura atual, chame o


método de valor de data e hora DateTime.ToString(String) ou
DateTimeOffset.ToString(String) de instância e informe a cadeia de caracteres
ddd como o parâmetro format . O exemplo a seguir ilustra a chamada para o

método ToString(String):

C#

using System;

public class Example


{
public static void Main()
{
DateTime dateValue = new DateTime(2008, 6, 11);
Console.WriteLine(dateValue.ToString("ddd"));
}
}
// The example displays the following output:
// Wed

b. Para extrair o nome abreviado do dia da semana de uma cultura específica,


chame o método de instância DateTime.ToString(String, IFormatProvider) ou
DateTimeOffset.ToString(String, IFormatProvider) do valor de data e hora.
Informe a cadeia de caracteres ddd como o parâmetro format . Apresente um
objeto CultureInfo ou DateTimeFormatInfo que representa a cultura cujo nome
de dia da semana deve ser recuperado como parâmetro provider . O código a
seguir mostra um chamada ao método ToString(String, IFormatProvider) usando
um objeto CultureInfo que representa a cultura fr-FR:

C#

using System;
using System.Globalization;
public class Example
{
public static void Main()
{
DateTime dateValue = new DateTime(2008, 6, 11);
Console.WriteLine(dateValue.ToString("ddd",
new CultureInfo("fr-FR")));
}
}
// The example displays the following output:
// mer.

Extrair o nome completo do dia da semana


1. Use o método estático DateTime.Parse ou DateTimeOffset.Parse para converter a
representação de cadeia de caracteres de uma data em um valor DateTimeou
DateTimeOffset.

2. Você pode extrair o nome completo de dia da semana da cultura atual ou de uma
cultura específica:

a. Para extrair o nome de dia da semana da cultura atual, chame o método de


valor de data e hora DateTime.ToString(String) ou
DateTimeOffset.ToString(String) de instância e informe a cadeia de caracteres
dddd como o parâmetro format . O exemplo a seguir ilustra a chamada para o

método ToString(String):

C#

using System;

public class Example


{
public static void Main()
{
DateTime dateValue = new DateTime(2008, 6, 11);
Console.WriteLine(dateValue.ToString("dddd"));
}
}
// The example displays the following output:
// Wednesday

b. Para extrair o nome do dia da semana de uma cultura específica, chame o


método de instância DateTime.ToString(String, IFormatProvider) ou
DateTimeOffset.ToString(String, IFormatProvider) do valor de data e hora.
Informe a cadeia de caracteres dddd como o parâmetro format . Apresente um
objeto CultureInfo ou DateTimeFormatInfo que representa a cultura cujo nome
de dia da semana deve ser recuperado como parâmetro provider . O código a
seguir ilustra um chamada ao método ToString(String, IFormatProvider) usando
um objeto CultureInfo que representa a cultura es-ES:

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
DateTime dateValue = new DateTime(2008, 6, 11);
Console.WriteLine(dateValue.ToString("dddd",
new CultureInfo("es-ES")));
}
}
// The example displays the following output:
// miércoles.

Exemplo
O exemplo a seguir ilustra chamadas para as propriedades DateTime.DayOfWeek e
DateTimeOffset.DayOfWeek para recuperar o número que representa o dia da semana
para uma data específica. Ele também inclui chamadas para os métodos
DateTime.ToString e DateTimeOffset.ToString para extrair o nome abreviado do dia da
semana e o nome completo do dia da semana.

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
string dateString = "6/11/2007";
DateTime dateValue;
DateTimeOffset dateOffsetValue;

try
{
DateTimeFormatInfo dateTimeFormats;
// Convert date representation to a date value
dateValue = DateTime.Parse(dateString,
CultureInfo.InvariantCulture);
dateOffsetValue = new DateTimeOffset(dateValue,

TimeZoneInfo.Local.GetUtcOffset(dateValue));

// Convert date representation to a number indicating the day of


week
Console.WriteLine((int) dateValue.DayOfWeek);
Console.WriteLine((int) dateOffsetValue.DayOfWeek);

// Display abbreviated weekday name using current culture


Console.WriteLine(dateValue.ToString("ddd"));
Console.WriteLine(dateOffsetValue.ToString("ddd"));

// Display full weekday name using current culture


Console.WriteLine(dateValue.ToString("dddd"));
Console.WriteLine(dateOffsetValue.ToString("dddd"));

// Display abbreviated weekday name for de-DE culture


Console.WriteLine(dateValue.ToString("ddd", new CultureInfo("de-
DE")));
Console.WriteLine(dateOffsetValue.ToString("ddd",
new CultureInfo("de-
DE")));

// Display abbreviated weekday name with de-DE DateTimeFormatInfo


object
dateTimeFormats = new CultureInfo("de-DE").DateTimeFormat;
Console.WriteLine(dateValue.ToString("ddd", dateTimeFormats));
Console.WriteLine(dateOffsetValue.ToString("ddd",
dateTimeFormats));

// Display full weekday name for fr-FR culture


Console.WriteLine(dateValue.ToString("ddd", new CultureInfo("fr-
FR")));
Console.WriteLine(dateOffsetValue.ToString("ddd",
new CultureInfo("fr-
FR")));

// Display abbreviated weekday name with fr-FR DateTimeFormatInfo


object
dateTimeFormats = new CultureInfo("fr-FR").DateTimeFormat;
Console.WriteLine(dateValue.ToString("dddd", dateTimeFormats));
Console.WriteLine(dateOffsetValue.ToString("dddd",
dateTimeFormats));
}
catch (FormatException)
{
Console.WriteLine("Unable to convert {0} to a date.", dateString);
}
}
}
// The example displays the following output:
// 1
// 1
// Mon
// Mon
// Monday
// Monday
// Mo
// Mo
// Mo
// Mo
// lun.
// lun.
// lundi
// lundi

As linguagens individuais podem contar com funcionalidades que duplicam ou


complementam a funcionalidade oferecida pelo .NET. Por exemplo, o Visual Basic tem
duas funções desse tipo:

Weekday , que retorna um número que indica o dia da semana de uma determinada
data. Ele leva em consideração que o primeiro dia da semana tem valor ordinal
um, enquanto a propriedade DateTime.DayOfWeek considera o valor ordinal desse
dia como zero.

WeekdayName , que retorna o nome da semana na cultura atual e que corresponde

ao número de um determinado dia da semana.

O exemplo a seguir ilustra o uso das funções do Visual Basic Weekday e WeekdayName :

VB

Imports System.Globalization
Imports System.Threading

Module Example
Public Sub Main()
Dim dateValue As Date = #6/11/2008#

' Get weekday number using Visual Basic Weekday function


Console.WriteLine(Weekday(dateValue)) ' Displays 4
' Compare with .NET DateTime.DayOfWeek property
Console.WriteLine(dateValue.DayOfWeek) ' Displays 3

' Get weekday name using Weekday and WeekdayName functions


Console.WriteLine(WeekdayName(Weekday(dateValue))) ' Displays
Wednesday

' Change culture to de-DE


Dim originalCulture As CultureInfo =
Thread.CurrentThread.CurrentCulture
Thread.CurrentThread.CurrentCulture = New CultureInfo("de-DE")
' Get weekday name using Weekday and WeekdayName functions
Console.WriteLine(WeekdayName(Weekday(dateValue))) ' Displays
Donnerstag

' Restore original culture


Thread.CurrentThread.CurrentCulture = originalCulture
End Sub
End Module

Você também pode usar o valor retornado pela propriedade DateTime.DayOfWeek para
recuperar o nome de dia da semana de uma determinada data. Isso requer apenas uma
chamada ao método ToString no valor DayOfWeek retornado pela propriedade. No
entanto, essa técnica não gera um nome de dia da semana localizado para a cultura
atual, como mostra o exemplo a seguir:

C#

using System;
using System.Globalization;
using System.Threading;

public class Example


{
public static void Main()
{
// Change current culture to fr-FR
CultureInfo originalCulture = Thread.CurrentThread.CurrentCulture;
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");

DateTime dateValue = new DateTime(2008, 6, 11);


// Display the DayOfWeek string representation
Console.WriteLine(dateValue.DayOfWeek.ToString());
// Restore original current culture
Thread.CurrentThread.CurrentCulture = originalCulture;
}
}
// The example displays the following output:
// Wednesday

Confira também
Cadeias de caracteres de formato de data e hora padrão
Cadeias de caracteres de formato de data e hora personalizado
Como: definir e usar provedores de
formatos numéricos personalizados
Artigo • 10/05/2023

O .NET oferece controle abrangente sobre a representação de cadeias de caracteres de


valores numéricos. Ele dá suporte aos seguintes recursos para personalizar o formato de
valores numéricos:

Cadeias de caracteres de formato numérico padrão, que fornecem um conjunto


predefinido de formatos para converter números para suas representações de
cadeia de caracteres. Você pode usá-las com qualquer método de formatação
numérica, como Decimal.ToString(String), que tem um parâmetro format . Para
obter detalhes, confira Cadeias de caracteres de formato numérico padrão.

Cadeias de caracteres de formato numérico personalizado, que fornecem um


conjunto de símbolos que podem ser combinados para definir especificadores de
formato numérico personalizado. Elas também podem ser usadas com qualquer
método de formatação numérica, como Decimal.ToString(String), que tem um
parâmetro format . Para ver detalhes, confira Cadeias de caracteres de formato
numérico personalizado.

Os objetos CultureInfo ou NumberFormatInfo personalizados que definem os


símbolos e padrões de formato usados para exibir as representações de cadeia de
caracteres de valores numéricos. Você pode usá-las com qualquer método de
formatação numérica, como ToString, que tem um parâmetro provider .
Normalmente, o parâmetro provider é usado para especificar a formatação
específica à cultura.

Em alguns casos (como quando um aplicativo deve exibir um número de conta


formatado, um número de identificação ou um código postal) essas três técnicas são
inadequadas. O .NET também permite que você defina um objeto de formatação que
não é um objeto CultureInfo ou NumberFormatInfo para determinar como um valor
numérico é formatado. Este tópico fornece informações passo a passo para
implementar esse tipo de objeto, bem como um exemplo que formata números de
telefone.

Define um provedor de formato personalizado


1. Defina uma classe que implementa as interfaces IFormatProvider e
ICustomFormatter.
2. Implementar o método de IFormatProvider.GetFormat . GetFormat é um método
de retorno de chamada que o método de formatação (como o método
String.Format(IFormatProvider, String, Object[])) invoca para recuperar o objeto
que é o responsável por executar a formatação personalizada. Uma
implementação típica de GetFormat faz o seguinte:

a. Determina se o objeto Type passado como método de parâmetro representa


uma interface ICustomFormatter.

b. Se o parâmetro representar a interface ICustomFormatter, GetFormat retorna


um objeto que implementa a interface ICustomFormatter que é responsável por
fornecer a formatação personalizada. Normalmente, o objeto de formatação
personalizado retorna a si próprio.

c. Se o parâmetro não representar a interfaceICustomFormatter, GetFormat


retorna null .

3. Implementar o método de Format . Este método é chamado pelo método


String.Format(IFormatProvider, String, Object[]) e é responsável por retornar a
representação de cadeia de caracteres de um número. Implementar o método
normalmente envolve o seguinte:

a. Opcionalmente, verifique se o método é legitimamente destinado a fornecer


serviços de formatação examinando o parâmetro provider . Para formatar
objetos que implementam IFormatProvider e ICustomFormatter, isso envolve
testar o parâmetro provider para igualdade com o objeto de formatação atual.

b. Determine se o objeto de formatação deve dar suporte a especificadores de


formato personalizado. (Por exemplo, um especificador de formato "N" pode
indicar que um número de telefone dos EUA deve ser gerado no formato NANP
e um "I" pode indicar a saída no formato E. 123 da Recomendação ITU-T.) Se
especificadores de formato forem usados, o método deve tratar o especificador
de formato específico. Ele é passado para o método no parâmetro format . Se
nenhum especificador estiver presente, o valor do parâmetro format será
String.Empty.

c. Recupere o valor numérico passado para o método como o parâmetro arg .


Execute as manipulações que forem necessárias para convertê-lo em sua
representação de cadeia de caracteres.

d. Retorne a representação de cadeia de caracteres do parâmetro arg .


Usa um objeto de formatação numérico
personalizado
1. Crie uma nova instância da classe de formatação personalizada.

2. Chame o método de formatação String.Format(IFormatProvider, String, Object[]) e


passe para ele o objeto de formatação personalizado, o especificador de
formatação (ou String.Empty, se um não for usado) e o valor numérico a ser
formatado.

Exemplo
O exemplo a seguir define um provedor de formato numérico personalizado chamado
TelephoneFormatter , que converte um número que representa um número de telefone

dos EUA para seu formato NANP ou E.123. O método lida com dois especificadores de
formato, "N" (que gera o formato NANP) e "I" (que gera o formato internacional E.123).

C#

using System;
using System.Globalization;

public class TelephoneFormatter : IFormatProvider, ICustomFormatter


{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(string format, object arg, IFormatProvider


formatProvider)
{
// Check whether this is an appropriate callback
if (! this.Equals(formatProvider))
return null;

// Set default format specifier


if (string.IsNullOrEmpty(format))
format = "N";

string numericString = arg.ToString();

if (format == "N")
{
if (numericString.Length <= 4)
return numericString;
else if (numericString.Length == 7)
return numericString.Substring(0, 3) + "-" +
numericString.Substring(3, 4);
else if (numericString.Length == 10)
return "(" + numericString.Substring(0, 3) + ") " +
numericString.Substring(3, 3) + "-" +
numericString.Substring(6);
else
throw new FormatException(
string.Format("'{0}' cannot be used to format {1}.",
format, arg.ToString()));
}
else if (format == "I")
{
if (numericString.Length < 10)
throw new FormatException(string.Format("{0} does not have 10
digits.", arg.ToString()));
else
numericString = "+1 " + numericString.Substring(0, 3) + " " +
numericString.Substring(3, 3) + " " + numericString.Substring(6);
}
else
{
throw new FormatException(string.Format("The {0} format specifier
is invalid.", format));
}
return numericString;
}
}

public class TestTelephoneFormatter


{
public static void Main()
{
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}", 0));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}",
911));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}",
8490216));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}",
4257884748));

Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}",


0));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}",
911));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}",
8490216));
Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}",
4257884748));

Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:I}",


4257884748));
}
}

O provedor de formato numérico personalizado pode ser usado apenas com o método
String.Format(IFormatProvider, String, Object[]). As outras sobrecargas de métodos de
formatação numérica (como ToString ) que têm um parâmetro do tipo IFormatProvider
passam para a implementação IFormatProvider.GetFormat um objeto Type que
representa o tipo NumberFormatInfo. Por sua vez, eles esperam que o método retorne
um objeto NumberFormatInfo. Se isso não acontecer, o provedor de formato numérico
personalizado será ignorado e o objeto NumberFormatInfo da cultura atual será usada
em seu lugar. No exemplo, o método TelephoneFormatter.GetFormat trata da
possibilidade de ele ser passado inadequadamente para um método de formatação
numérico examinando o parâmetro do método e retornando null se ele representar
um tipo diferente de ICustomFormatter.

Se um provedor de formato numérico personalizado der suporte a um conjunto de


especificadores de formato, forneça um comportamento padrão se nenhum
especificador de formato for fornecido no item de formato usado na chamada de
método String.Format(IFormatProvider, String, Object[]). No exemplo, "N" é o
especificador de formato padrão. Isso permite que um número a ser convertido em um
número de telefone formatado, fornecendo um especificador de formato explícito. O
exemplo a seguir ilustra essa chamada de método.

C#

Console.WriteLine(String.Format(new TelephoneFormatter(), "{0:N}",


4257884748));

Mas também permite que a conversão ocorra se nenhum especificador de formato


estiver presente. O exemplo a seguir ilustra essa chamada de método.

C#

Console.WriteLine(String.Format(new TelephoneFormatter(), "{0}",


4257884748));

Se nenhum especificador de formato padrão for definido, sua implementação do


método ICustomFormatter.Format deve incluir código como o seguinte para que o .NET
possa fornecer a formatação a que seu código não dá suporte.

C#
if (arg is IFormattable)
s = ((IFormattable)arg).ToString(format, formatProvider);
else if (arg != null)
s = arg.ToString();

No caso deste exemplo, o método que implementa ICustomFormatter.Format se destina


a servir como um método de retorno de chamada para o método
String.Format(IFormatProvider, String, Object[]). Portanto, ele examina o parâmetro
formatProvider para determinar se ele contém uma referência ao objeto
TelephoneFormatter atual. No entanto, o método também pode ser chamado

diretamente do código. Nesse caso, você pode usar o parâmetro formatProvider para
fornecer um objeto CultureInfo ou NumberFormatInfo que fornece informações de
formatação específicas da cultura.
Como: valores de data e hora de ida e
volta
Artigo • 07/04/2023

Em muitos aplicativos, um valor de data e hora destina-se a identificar sem


ambiguidade um único ponto no tempo. Este artigo mostra como salvar e restaurar um
valor DateTime, um valor DateTimeOffset e um valor de data e hora com informações
de fuso horário para que o valor restaurado identifique a mesma hora que o valor salvo.

Ida e volta de um valor DateTime


1. Converta o valor DateTime em sua representação de cadeia de caracteres
chamando o método DateTime.ToString(String) com o especificador de formato
"o".

2. Salve a representação de cadeia de caracteres do valor DateTime em um arquivo


ou passe-a por um processo, domínio de aplicativo ou limite de computador.

3. Recupere a cadeia de caracteres que representa o valor DateTime.

4. Chame o método DateTime.Parse(String, IFormatProvider, DateTimeStyles) e passe


DateTimeStyles.RoundtripKind como o valor do parâmetro styles .

O exemplo a seguir mostra como fazer a viagem de ida e volta de um valor DateTime.

C#

const string fileName = @".\DateFile.txt";

StreamWriter outFile = new StreamWriter(fileName);

// Save DateTime value.


DateTime dateToSave = DateTime.SpecifyKind(new DateTime(2008, 6, 12, 18, 45,
15),
DateTimeKind.Local);
string? dateString = dateToSave.ToString("o");
Console.WriteLine("Converted {0} ({1}) to {2}.",
dateToSave.ToString(),
dateToSave.Kind.ToString(),
dateString);
outFile.WriteLine(dateString);
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName);
outFile.Close();

// Restore DateTime value.


DateTime restoredDate;

using StreamReader inFile = new StreamReader(fileName);


dateString = inFile.ReadLine();

if (dateString is not null)


{
restoredDate = DateTime.Parse(dateString, null,
DateTimeStyles.RoundtripKind);
Console.WriteLine("Read {0} ({2}) from {1}.", restoredDate.ToString(),
fileName,

restoredDate.Kind.ToString());
}

// The example displays the following output:


// Converted 6/12/2008 6:45:15 PM (Local) to 2008-06-12T18:45:15.0000000-
05:00.
// Wrote 2008-06-12T18:45:15.0000000-05:00 to .\DateFile.txt.
// Read 6/12/2008 6:45:15 PM (Local) from .\DateFile.txt.

Fazendo a viagem de ida e volta de um valor DateTime, essa técnica consegue preservar
o horário para todos os horários locais e universais. Por exemplo, se um valor DateTime
local for salvo em um sistema no fuso horário padrão do Pacífico dos EUA e for
restaurado em um sistema no fuso horário padrão central dos EUA, a data e a hora
restauradas serão duas horas depois do horário original , que reflete a diferença horária
entre os dois fusos horários. No entanto, essa técnica não é necessariamente exata para
horários não especificados. Todos os valores DateTime cuja propriedade Kind é
Unspecified são tratados como se fossem horários locais. Se não for uma hora local, o
DateTime não identifica com êxito o momento correto. A solução alternativa para essa
limitação é acoplar rigorosamente um valor de data e hora com seu fuso horário para
salvar e restaurar a operação.

Ida e volta de um valor DateTimeOffset


1. Converta o valor DateTimeOffset em sua representação de cadeia de caracteres
chamando o método DateTimeOffset.ToString(String) com o especificador de
formato "o".

2. Salve a representação de cadeia de caracteres do valor DateTimeOffset em um


arquivo ou passe-a por um processo, domínio de aplicativo ou limite de
computador.

3. Recupere a cadeia de caracteres que representa o valor DateTimeOffset.


4. Chame o método DateTimeOffset.Parse(String, IFormatProvider, DateTimeStyles) e
passe DateTimeStyles.RoundtripKind como o valor do parâmetro styles .

O exemplo a seguir mostra como fazer a viagem de ida e volta de um valor


DateTimeOffset.

C#

const string fileName = @".\DateOff.txt";

StreamWriter outFile = new StreamWriter(fileName);

// Save DateTime value.


DateTimeOffset dateToSave = new DateTimeOffset(2008, 6, 12, 18, 45, 15,
new TimeSpan(7, 0, 0));
string? dateString = dateToSave.ToString("o");
Console.WriteLine("Converted {0} to {1}.", dateToSave.ToString(),
dateString);
outFile.WriteLine(dateString);
Console.WriteLine("Wrote {0} to {1}.", dateString, fileName);
outFile.Close();

// Restore DateTime value.


DateTimeOffset restoredDateOff;

using StreamReader inFile = new StreamReader(fileName);


dateString = inFile.ReadLine();

if (dateString is not null)


{
restoredDateOff = DateTimeOffset.Parse(dateString, null,
DateTimeStyles.RoundtripKind);
Console.WriteLine("Read {0} from {1}.", restoredDateOff.ToString(),
fileName);
}

// The example displays the following output:


// Converted 6/12/2008 6:45:15 PM +07:00 to 2008-06-
12T18:45:15.0000000+07:00.
// Wrote 2008-06-12T18:45:15.0000000+07:00 to .\DateOff.txt.
// Read 6/12/2008 6:45:15 PM +07:00 from .\DateOff.txt.

Essa técnica sempre identifica sem ambiguidade um valor DateTimeOffset como um


único momento. O valor pode ser convertido em UTC (Tempo Universal Coordenado)
chamando o método DateTimeOffset.ToUniversalTime ou pode ser convertido em
determinado fuso horário chamando o método DateTimeOffset.ToOffset ou
TimeZoneInfo.ConvertTime(DateTimeOffset, TimeZoneInfo). A principal limitação dessa
técnica é que a aritmética de data e hora, quando executada em um valor
DateTimeOffset que representa o horário em determinado fuso horário, talvez não
produza resultados precisos para esse fuso horário. Isso ocorre porque, quando um
valor DateTimeOffset é instanciado, é dissociado do fuso horário. Portanto, as regras de
ajuste do fuso horário não podem mais ser aplicadas quando você executa cálculos de
data e hora. É possível solucionar esse problema definindo um tipo personalizado que
inclui um valor de data e hora e o respectivo fuso horário.

Compilar o código
Esses exemplos exigem que os seguintes namespaces podem ser importados com
diretivas C# using ou instruções Visual Basic Imports :

System (somente C#)


System.Globalization
System.IO

Confira também
Escolhendo entre DateTime, DateTimeOffset, TimeSpan e TimeZoneInfo
Cadeias de caracteres de formato de data e hora padrão
Como exibir milissegundos em valores
de data e hora
Artigo • 10/05/2023

Os métodos padrão de formatação de data e hora, como DateTime.ToString(), incluem


as horas, minutos e segundos de um valor temporal, mas excluem seu componente de
milissegundos. Este artigo mostra como incluir um componente de milissegundos de
uma data e hora em cadeias de caracteres de data e hora formatadas.

Para exibir o componente de milissegundos de


um valor DateTime
1. Se você estiver trabalhando na representação da cadeia de caracteres de uma
data, converta-a para um valor DateTime ou DateTimeOffset usando a estatística
DateTime.Parse(String) ou o método DateTimeOffset.Parse(String).

2. Para extrair a representação de cadeia de caracteres do componente de


milissegundos de um valor de data e hora, chame o método
DateTime.ToString(String) ou ToString do valor de data e hora e passe o padrão de
formato personalizado fff ou FFF , sozinho ou com outros especificadores de
formato personalizado como o parâmetro format .

 Dica

A propriedade
System.Globalization.NumberFormatInfo.NumberDecimalSeparator especifica o
separador de milissegundos.

Exemplo
O exemplo exibe o componente de milissegundos de um valor de DateTime e
DateTimeOffset para console, sozinho e incluído em uma cadeia de caracteres de data e
hora mais longa.

C#

using System.Globalization;
using System.Text.RegularExpressions;
string dateString = "7/16/2008 8:32:45.126 AM";

try
{
DateTime dateValue = DateTime.Parse(dateString);
DateTimeOffset dateOffsetValue = DateTimeOffset.Parse(dateString);

// Display Millisecond component alone.


Console.WriteLine("Millisecond component only: {0}",
dateValue.ToString("fff"));
Console.WriteLine("Millisecond component only: {0}",
dateOffsetValue.ToString("fff"));

// Display Millisecond component with full date and time.


Console.WriteLine("Date and Time with Milliseconds: {0}",
dateValue.ToString("MM/dd/yyyy hh:mm:ss.fff tt"));
Console.WriteLine("Date and Time with Milliseconds: {0}",
dateOffsetValue.ToString("MM/dd/yyyy hh:mm:ss.fff tt"));

string fullPattern = DateTimeFormatInfo.CurrentInfo.FullDateTimePattern;

// Create a format similar to .fff but based on the current culture.


string millisecondFormat = $"
{NumberFormatInfo.CurrentInfo.NumberDecimalSeparator}fff";

// Append millisecond pattern to current culture's full date time


pattern.
fullPattern = Regex.Replace(fullPattern, "(:ss|:s)",
$"$1{millisecondFormat}");

// Display Millisecond component with modified full date and time


pattern.
Console.WriteLine("Modified full date time pattern: {0}",
dateValue.ToString(fullPattern));
Console.WriteLine("Modified full date time pattern: {0}",
dateOffsetValue.ToString(fullPattern));
}
catch (FormatException)
{
Console.WriteLine("Unable to convert {0} to a date.", dateString);
}
// The example displays the following output if the current culture is en-
US:
// Millisecond component only: 126
// Millisecond component only: 126
// Date and Time with Milliseconds: 07/16/2008 08:32:45.126 AM
// Date and Time with Milliseconds: 07/16/2008 08:32:45.126 AM
// Modified full date time pattern: Wednesday, July 16, 2008 8:32:45.126
AM
// Modified full date time pattern: Wednesday, July 16, 2008 8:32:45.126
AM
O padrão de formato fff inclui os zeros à direita no valor de milissegundos. O padrão
de formato FFF os suprime. O exemplo a seguir ilustra a diferença:

C#

DateTime dateValue = new DateTime(2008, 7, 16, 8, 32, 45, 180);


Console.WriteLine(dateValue.ToString("fff"));
Console.WriteLine(dateValue.ToString("FFF"));
// The example displays the following output to the console:
// 180
// 18

Um problema ao definir um especificador de formato personalizado completo que inclui


o componente de milissegundos de uma data e hora é que ele define um formato
embutido em código que pode não corresponder à organização de elementos de hora
na cultura atual do aplicativo. Uma alternativa melhor é recuperar um dos padrões de
exibição de data e hora definidos pelo objeto DateTimeFormatInfo da cultura atual e
modificá-lo para incluir milissegundos. O exemplo também ilustra esta abordagem. Ele
recupera o padrão de data e hora completo da cultura atual da propriedade
DateTimeFormatInfo.FullDateTimePattern e insere o padrão personalizado fff junto
com o separador atual de milissegundos da cultura. O exemplo usa uma expressão
regular para executar esta operação em uma única chamada de método.

Você também pode usar um especificador de formato personalizado para exibir uma
parte fracionária dos segundos que não sejam milissegundos. Por exemplo, o
especificador de formato personalizado f ou F exibe décimos de segundo, o
especificador de formato personalizado ff ou FF exibe centésimos de segundo e o
especificador de formato personalizado ffff ou FFFF exibe décimos de milésimos de
segundo. Partes fracionárias de um milissegundo são truncadas em vez de
arredondadas na cadeia de caracteres retornada. Esses especificadores de formato são
usados no exemplo a seguir:

C#

DateTime dateValue = new DateTime(2008, 7, 16, 8, 32, 45, 180);


Console.WriteLine("{0} seconds", dateValue.ToString("s.f"));
Console.WriteLine("{0} seconds", dateValue.ToString("s.ff"));
Console.WriteLine("{0} seconds", dateValue.ToString("s.ffff"));
// The example displays the following output to the console:
// 45.1 seconds
// 45.18 seconds
// 45.1800 seconds

7 Observação
É possível exibir unidades fracionárias muito pequenas de um segundo, como dez
milésimos de segundo ou centésimos de milésimos de segundo. No entanto, esses
valores podem não ser significativos. A precisão dos valores de data e hora
depende da resolução do relação ao relógio do sistema operacional. Para obter
mais informações, consulte a API que seu sistema operacional usa:

Windows 7: GetSystemTimeAsFileTime
Windows 8 e acima: GetSystemTimePreciseAsFileTime
Linux e macOS: clock_gettime

Confira também
DateTimeFormatInfo
Cadeias de caracteres de formato de data e hora personalizado
Como: exibir datas em calendários não
gregorianos
Artigo • 07/04/2023

Os tipos DateTime e DateTimeOffset usam o calendário gregoriano como seu calendário


padrão. Isso significa que chamar o método ToString de um valor de data e hora exibe
a representação de cadeia de caracteres da data e hora no calendário gregoriano,
mesmo que a data e hora tenha sido criada usando outro calendário. Isso é ilustrado no
exemplo a seguir, que usa duas maneiras diferentes para criar um valor de data e hora
com o calendário persa, mas ainda exibe esses valores de data e hora no calendário
gregoriano quando chama o método ToString. Este exemplo reflete duas técnicas
comumente usadas, mas incorretas, para exibir a data em um calendário específico.

C#

PersianCalendar persianCal = new PersianCalendar();

DateTime persianDate = persianCal.ToDateTime(1387, 3, 18, 12, 0, 0, 0);


Console.WriteLine(persianDate.ToString());

persianDate = new DateTime(1387, 3, 18, persianCal);


Console.WriteLine(persianDate.ToString());
// The example displays the following output to the console:
// 6/7/2008 12:00:00 PM
// 6/7/2008 12:00:00 AM

Duas técnicas diferentes podem ser usadas para exibir a data em um calendário
específico. A primeira requer que o calendário seja o calendário padrão de uma
determinada cultura. O segundo pode ser usado com qualquer calendário.

Para exibir a data do calendário padrão de uma cultura


1. Crie uma instância de um objeto de calendário derivado da classe Calendar que
representa o calendário a ser usado.

2. Crie uma instância de um objeto CultureInfo que representa a cultura cuja


formatação será usada para exibir a data.

3. Chame o método Array.Exists para determinar se o objeto de calendário é um


membro da matriz retornada pela propriedade CultureInfo.OptionalCalendars. Isso
indica que o calendário pode servir como calendário padrão para o objeto
CultureInfo. Se ele não for membro da matriz, siga as instruções na seção "Para
exibir a data em qualquer calendário".

4. Atribua o objeto de calendário à propriedade Calendar do objeto


DateTimeFormatInfo retornado pela propriedade CultureInfo.DateTimeFormat.

7 Observação

A classe CultureInfo também tem uma propriedade Calendar. No entanto, ela


é somente leitura e constante; não é alterada para refletir o novo calendário
padrão atribuído à propriedade DateTimeFormatInfo.Calendar.

5. Chame o método ToString ou ToString e passe-o para o objeto CultureInfo cujo


calendário padrão foi modificado na etapa anterior.

Para exibir a data em qualquer calendário


1. Crie uma instância de um objeto de calendário derivado da classe Calendar que
representa o calendário a ser usado.

2. Determine quais elementos de data e hora devem aparecer na representação de


cadeia de caracteres do valor de data e hora.

3. Para cada elemento de data e hora que você deseja exibir, chame o método Get
do objeto de calendário... método. Os métodos a seguir estão disponíveis:

GetYear, para exibir o ano no calendário apropriado.

GetMonth, para exibir o mês no calendário apropriado.

GetDayOfMonth, para exibir o número do dia do mês no calendário


apropriado.

GetHour, para exibir a hora do dia no calendário apropriado.

GetMinute, para exibir os minutos da hora no calendário apropriado.

GetSecond, para exibir os segundos do minuto no calendário apropriado.

GetMilliseconds, para exibir os milissegundos no segundo no calendário


apropriado.

Exemplo
O exemplo exibe uma data usando dois calendários diferentes. Ele exibe a data após
definir o calendário islâmico como calendário padrão para a cultura ar-JO e exibe a data
usando o calendário persa, que não tem suporte como calendário opcional pela cultura
fa-IR.

C#

using System;
using System.Globalization;

public class CalendarDates


{
public static void Main()
{
HijriCalendar hijriCal = new HijriCalendar();
CalendarUtility hijriUtil = new CalendarUtility(hijriCal);
DateTime dateValue1 = new DateTime(1429, 6, 29, hijriCal);
DateTimeOffset dateValue2 = new DateTimeOffset(dateValue1,

TimeZoneInfo.Local.GetUtcOffset(dateValue1));
CultureInfo jc = CultureInfo.CreateSpecificCulture("ar-JO");

// Display the date using the Gregorian calendar.


Console.WriteLine("Using the system default culture: {0}",
dateValue1.ToString("d"));
// Display the date using the ar-JO culture's original default
calendar.
Console.WriteLine("Using the ar-JO culture's original default
calendar: {0}",
dateValue1.ToString("d", jc));
// Display the date using the Hijri calendar.
Console.WriteLine("Using the ar-JO culture with Hijri as the default
calendar:");
// Display a Date value.
Console.WriteLine(hijriUtil.DisplayDate(dateValue1, jc));
// Display a DateTimeOffset value.
Console.WriteLine(hijriUtil.DisplayDate(dateValue2, jc));

Console.WriteLine();

PersianCalendar persianCal = new PersianCalendar();


CalendarUtility persianUtil = new CalendarUtility(persianCal);
CultureInfo ic = CultureInfo.CreateSpecificCulture("fa-IR");

// Display the date using the ir-FA culture's default calendar.


Console.WriteLine("Using the ir-FA culture's default calendar: {0}",
dateValue1.ToString("d", ic));
// Display a Date value.
Console.WriteLine(persianUtil.DisplayDate(dateValue1, ic));
// Display a DateTimeOffset value.
Console.WriteLine(persianUtil.DisplayDate(dateValue2, ic));
}
}
public class CalendarUtility
{
private Calendar thisCalendar;
private CultureInfo targetCulture;

public CalendarUtility(Calendar cal)


{
this.thisCalendar = cal;
}

private bool CalendarExists(CultureInfo culture)


{
this.targetCulture = culture;
return Array.Exists(this.targetCulture.OptionalCalendars,
this.HasSameName);
}

private bool HasSameName(Calendar cal)


{
if (cal.ToString() == thisCalendar.ToString())
return true;
else
return false;
}

public string DisplayDate(DateTime dateToDisplay, CultureInfo culture)


{
DateTimeOffset displayOffsetDate = dateToDisplay;
return DisplayDate(displayOffsetDate, culture);
}

public string DisplayDate(DateTimeOffset dateToDisplay,


CultureInfo culture)
{
string specifier = "yyyy/MM/dd";

if (this.CalendarExists(culture))
{
Console.WriteLine("Displaying date in supported {0} calendar...",
this.thisCalendar.GetType().Name);
culture.DateTimeFormat.Calendar = this.thisCalendar;
return dateToDisplay.ToString(specifier, culture);
}
else
{
Console.WriteLine("Displaying date in unsupported {0} calendar...",
thisCalendar.GetType().Name);

string separator = targetCulture.DateTimeFormat.DateSeparator;

return
thisCalendar.GetYear(dateToDisplay.DateTime).ToString("0000") +
separator +
thisCalendar.GetMonth(dateToDisplay.DateTime).ToString("00")
+
separator +

thisCalendar.GetDayOfMonth(dateToDisplay.DateTime).ToString("00");
}
}
}
// The example displays the following output to the console:
// Using the system default culture: 7/3/2008
// Using the ar-JO culture's original default calendar: 03/07/2008
// Using the ar-JO culture with Hijri as the default calendar:
// Displaying date in supported HijriCalendar calendar...
// 1429/06/29
// Displaying date in supported HijriCalendar calendar...
// 1429/06/29
//
// Using the ir-FA culture's default calendar: 7/3/2008
// Displaying date in unsupported PersianCalendar calendar...
// 1387/04/13
// Displaying date in unsupported PersianCalendar calendar...
// 1387/04/13

Cada objeto CultureInfo pode dar suporte a um ou mais calendários, que são indicados
pela propriedade OptionalCalendars. Um deles é designado como o calendário padrão
da cultura e é retornado pela propriedade somente leitura CultureInfo.Calendar. Outro
dos calendários opcionais pode ser designado como o padrão, atribuindo um objeto
Calendar que representa o calendário para a propriedade DateTimeFormatInfo.Calendar
retornada pela propriedade CultureInfo.DateTimeFormat. No entanto, alguns
calendários, como o calendário persa representado pela classe PersianCalendar, não
servem como calendários opcionais para nenhuma cultura.

O exemplo define uma classe de utilitário de calendário reutilizável, CalendarUtility ,


para manipular muitos dos detalhes de gerar a representação de cadeia de caracteres
de uma data usando um calendário específico. A classe CalendarUtility tem os
seguintes membros:

Um construtor parametrizado cujo único parâmetro é um objeto Calendar em que


uma data deve ser representada. Ele é atribuído a um campo particular da classe.

CalendarExists , um método particular que retorna um valor booliano que indica

se o calendário representado pelo CalendarUtility objeto tem suporte do objeto


CultureInfo passado para o método como um parâmetro. O método ajusta uma
chamada para o método Array.Exists ao qual ele passa a matriz
CultureInfo.OptionalCalendars.

HasSameName , um método particular atribuído ao representante Predicate<T> que

é transmitido como um parâmetro para o método Array.Exists. Cada membro da


matriz é passado ao método até que o método retorne true . O método determina
se o nome de um calendário opcional é o mesmo que o calendário representado
pelo objeto CalendarUtility .

DisplayDate , um método público sobrecarregado a que são passados dois


parâmetros: um valor de DateTime ou DateTimeOffset para expressar no
calendário representado pelo objeto CalendarUtility ; e a cultura cujas regras de
formatação devem ser usadas. Seu comportamento ao retornar a representação de
cadeia de caracteres de uma data depende de o calendário de destino ter suporte
da cultura cujas regras de formatação devem ser usadas.

Independentemente do calendário usado para criar um valor DateTime ou


DateTimeOffset neste exemplo, o valor normalmente é expresso como uma data no
calendário gregoriano. Isso ocorre porque os tipos DateTime e DateTimeOffset não
preservam nenhuma informação de calendário. Internamente, eles são representados
como o número de marcadores decorridos desde a meia-noite de 1º de janeiro de 0001.
A interpretação desse número depende do calendário. Na maioria das culturas, o
calendário padrão é o calendário gregoriano.
Codificação de caracteres no .NET
Artigo • 08/06/2023

Este artigo fornece uma introdução aos sistemas de codificação de caracteres (char) que
são usados pelo .NET. É explicado como os tipos String, Char, Rune e StringInfo
funcionam com Unicode, UTF-16 e UTF-8.

O termo caractere (char) é usado aqui no sentido geral do que um leitor percebe como
um único elemento de exibição. Exemplos comuns são a letra "a", o símbolo "@" e o
emoji "🐂". Às vezes, o que parece um caractere (char) é, na verdade, composto por
diversos elementos de exibição independentes, como explica a seção sobre clusters de
grafema.

Os tipos string e char


Uma instância da classe string representa um texto. Um string é, logicamente, uma
sequência de valores de 16 bits, em que cada um é uma instância do struct char. A
propriedade string.Length retorna o número de instâncias char na instância string .

A função de exemplo a seguir imprime em notação hexadecimal os valores de todas as


instâncias de char em um string :

C#

void PrintChars(string s)
{
Console.WriteLine($"\"{s}\".Length = {s.Length}");
for (int i = 0; i < s.Length; i++)
{
Console.WriteLine($"s[{i}] = '{s[i]}' ('\\u{(int)s[i]:x4}')");
}
Console.WriteLine();
}

Transmita o string "Hello" para esta função e você obterá a seguinte saída:

C#

PrintChars("Hello");

Saída
"Hello".Length = 5
s[0] = 'H' ('\u0048')
s[1] = 'e' ('\u0065')
s[2] = 'l' ('\u006c')
s[3] = 'l' ('\u006c')
s[4] = 'o' ('\u006f')

Cada caractere (char) é representado por um único valor char . Esse padrão vale para a
maioria dos idiomas do mundo. Por exemplo, veja a seguinte saída para dois caracteres
(char) em chinês que soam como nǐ hǎo e significam Olá:

C#

PrintChars("你好");

Saída

"你好".Length = 2
s[0] = '你' ('\u4f60')
s[1] = '好' ('\u597d')

No entanto, para alguns idiomas e para alguns símbolos e emojis, é preciso ter duas
instâncias de char para representar um único caractere (char). Por exemplo, compare os
caracteres (char) e as instâncias de char na palavra que significa Osage na língua Osage:

C#

PrintChars("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟");

Saída

"𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟".Length = 17
s[0] = '�' ('\ud801')
s[1] = '�' ('\udccf')
s[2] = '�' ('\ud801')
s[3] = '�' ('\udcd8')
s[4] = '�' ('\ud801')
s[5] = '�' ('\udcfb')
s[6] = '�' ('\ud801')
s[7] = '�' ('\udcd8')
s[8] = '�' ('\ud801')
s[9] = '�' ('\udcfb')
s[10] = '�' ('\ud801')
s[11] = '�' ('\udcdf')
s[12] = ' ' ('\u0020')
s[13] = '�' ('\ud801')
s[14] = '�' ('\udcbb')
s[15] = '�' ('\ud801')
s[16] = '�' ('\udcdf')

Cada caractere (char) no exemplo anterior, exceto o espaço, é representado por duas
instâncias de char .

Um único emoji Unicode também é representado por dois char s, como visto no
seguinte exemplo que mostra um emoji de boi:

Saída

"🐂".Length = 2
s[0] = '�' ('\ud83d')
s[1] = '�' ('\udc02')

Esses exemplos mostram que o valor de string.Length , que indica o número de


instâncias de char , não indica necessariamente o número de caracteres (char) exibidos.
Uma única instância de char por si só não representa necessariamente um caractere
(char).

Os pares char que são mapeados para um único caractere (char) são chamados de
pares alternativos. Para entender como eles funcionam, é necessário entender as
codificações Unicode e UTF-16.

Pontos de código Unicode


O Unicode é um padrão de codificação internacional para uso em diversas plataformas
com diversas linguagens e scripts.

O padrão Unicode define mais de 1,1 milhão de pontos de código . Um ponto de


código é um valor inteiro que pode variar de 0 a U+10FFFF (1,114,111 decimal). Alguns
pontos de código são atribuídos a letras, símbolos ou emojis. Outros são atribuídos a
ações que controlam como textos ou caracteres (char) são exibidos, como avançar para
uma nova linha. Muitos pontos de código ainda não foram atribuídos.

Veja os seguintes exemplos de atribuições de ponto de código, com links para gráficos
(char) Unicode nos quais eles aparecem:

Decimal Hex Exemplo Descrição

10 U+000A N/D NOVA LINHA


Decimal Hex Exemplo Descrição

97 U+0061 um LETRA A MINÚSCULA DO LATIM

562 U+0232 Ȳ LETRA Y MAIÚSCULA DO LATINA COM MACRO

68.675 U+10C43 𐱃 LETRA TURCA ANTIGA ORKHON AT

127.801 U+1F339 🌹 Emoji de ROSA

Os pontos de código são normalmente referenciados usando a sintaxe U+xxxx , em que


xxxx é o valor inteiro codificado em hexadecimal.

Dentro do intervalo completo de pontos de código, há dois subintervalos:

O BMP (Plano Multilíngue Básico) no intervalo U+0000..U+FFFF . Esse intervalo de


16 bits fornece 65.536 pontos de código, o que é suficiente para cobrir a maioria
dos sistemas de escrita do mundo.
Pontos de código suplementares no intervalo U+10000..U+10FFFF . Esse intervalo
de 21 bits fornece mais de um milhão de pontos de código adicionais que podem
ser usados para idiomas menos conhecidos e outros fins, como emojis.

O diagrama a seguir ilustra a relação entre o BMP e os pontos de código suplementares.

Unidades de código UTF-16


O UTF-16 (Formato de Transformação Unicode de 16 Bits) é um sistema de
codificação de caracteres (char) que usa unidades de código de 16 bits para representar
pontos de código Unicode. O .NET usa UTF-16 para codificar o texto em um string .
Uma instância de char representa uma unidade de código de 16 bits.

Uma única unidade de código de 16 bits pode representar qualquer ponto de código
no intervalo de 16 bits do Plano Multilíngue Básico. Porém, para um ponto de código no
intervalo suplementar, são necessárias duas instâncias de char .

Pares alternativos
A tradução de dois valores de 16 bits em um único valor de 21 bits é facilitada por um
intervalo especial chamado de pontos de código alternativos, que vai de U+D800 a U+DFFF
(55.296 a 57.343 decimal), incluindo esse valores.

O diagrama a seguir ilustra a relação entre o BMP e os pontos de código alternativos.

Quando um ponto de código alternativo de nível alto ( U+D800..U+DBFF ) é imediatamente


seguido por um ponto de código alternativo de nível baixo ( U+DC00..U+DFFF ), o par é
interpretado como um ponto de código suplementar usando a seguinte fórmula:

code point = 0x10000 +


((high surrogate code point - 0xD800) * 0x0400) +
(low surrogate code point - 0xDC00)

Veja a mesma fórmula a seguir usando a notação decimal.

code point = 65,536 +


((high surrogate code point - 55,296) * 1,024) +
(low surrogate code point - 56,320)

Um ponto de código alternativo de nível alto não tem um valor numérico maior do que
um ponto de código alternativo de nível baixo. O ponto de código é chamado de
alternativo “de nível alto” porque é usado para calcular a ordem dos 10 bits superiores
de um intervalo de pontos de código de 20 bits. O ponto de código alternativo de nível
baixo é usado para calcular a ordem dos 10 bits inferiores.

Por exemplo, o ponto de código real que corresponde ao par alternativo 0xD83C e
0xDF39 é calculado da seguinte maneira:
actual = 0x10000 + ((0xD83C - 0xD800) * 0x0400) + (0xDF39 - 0xDC00)
= 0x10000 + ( 0x003C * 0x0400) + 0x0339
= 0x10000 + 0xF000 + 0x0339
= 0x1F339

Veja o mesmo cálculo a seguir usando a notação decimal.

actual = 65,536 + ((55,356 - 55,296) * 1,024) + (57,145 - 56320)


= 65,536 + ( 60 * 1,024) + 825
= 65,536 + 61,440 + 825
= 127,801

O exemplo anterior demonstra que "\ud83c\udf39" é a codificação UTF-16 do ponto de


código U+1F339 ROSE ('🌹') mencionado anteriormente.

Valores escalares Unicode


O termo valor escalar Unicode refere-se a todos os pontos de código diferentes dos
pontos de código alternativos. Em outras palavras, um valor escalar é qualquer ponto de
código atribuído a um charatuador ou pode ser atribuído a um charatuador no futuro.
Aqui, "caractere" se refere a qualquer coisa que possa ser atribuída a um ponto de
código, o que inclui coisas como ações que controlam como o texto ou os caracteres
(char) são exibidos.

O diagrama a seguir ilustra os pontos de código de valor escalar.

O tipo Rune como um valor escalar


A partir do .NET Core 3.0, o tipo System.Text.Rune representa um valor escalar Unicode.
Rune não está disponível no .NET Core 2.x ou no .NET Framework 4.x.
Os construtores Rune validam se a instância resultante é um valor escalar Unicode
válido. Se ela não for, eles gerarão uma exceção. O seguinte exemplo mostra que o
código criou as instâncias de Rune com sucesso porque a entrada representa valores
escalares válidos:

C#

Rune a = new Rune('a');


Rune b = new Rune(0x0061);
Rune c = new Rune('\u0061');
Rune d = new Rune(0x10421);
Rune e = new Rune('\ud801', '\udc21');

O seguinte exemplo gera uma exceção porque o ponto de código está no intervalo
alternativo e não faz parte de um par alternativo:

C#

Rune f = new Rune('\ud801');

O seguinte exemplo gera uma exceção porque o ponto de código está além do
intervalo suplementar:

C#

Rune g = new Rune(0x12345678);

Exemplo de uso de Rune: troca do uso de maiúsculas e


minúsculas em letras
Uma API que usa um char e que pressupõe que ele está funcionando com um ponto de
código que é um valor escalar não funcionará corretamente se o char for de um par
alternativo. Por exemplo, considere o seguinte método que chama
Char.ToUpperInvariant em cada char em um string:

C#

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.


// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string ConvertToUpperBadExample(string input)
{
StringBuilder builder = new StringBuilder(input.Length);
for (int i = 0; i < input.Length; i++) /* or 'foreach' */
{
builder.Append(char.ToUpperInvariant(input[i]));
}
return builder.ToString();
}

Se o input string contiver a letra minúscula Deseret er ( 𐑉 ), este código não a


converterá em maiúscula ( 𐐡 ). O código chama char.ToUpperInvariant separadamente
em cada ponto de código alternativo, U+D801 e U+DC49 . No entanto, como U+D801 não
tem informações suficientes por si só para identificá-la como uma letra minúscula,
char.ToUpperInvariant a ignora. E o mesmo acontece com U+DC49 . O resultado é que '𐑉'

minúsculo no input string não é convertido em '𐐡' maiúsculo.

Veja as seguintes opções para converter corretamente um string em maiúscula:

Chame String.ToUpperInvariant na entrada string em vez de iterar char por char .


O método string.ToUpperInvariant tem acesso a ambas as partes de cada par
alternativo, portanto, pode lidar com todos os pontos de código Unicode
corretamente.

Faça a iteração nos valores escalares Unicode como instâncias de Rune em vez de
como instâncias de char , conforme mostrado no exemplo a seguir. Como uma
instância de Rune é um valor escalar Unicode válido, ela pode ser transmitida às
APIs que esperam operar em um valor escalar. Por exemplo, chamar
Rune.ToUpperInvariant fornece resultados corretos, conforme mostrado no
seguinte exemplo:

C#

static string ConvertToUpper(string input)


{
StringBuilder builder = new StringBuilder(input.Length);
foreach (Rune rune in input.EnumerateRunes())
{
builder.Append(Rune.ToUpperInvariant(rune));
}
return builder.ToString();
}

Outras APIs de Rune


O tipo Rune expõe análogos de muitas das APIs de char . Por exemplo, os seguintes
métodos espelham APIs estáticas no tipo char :

Rune.IsLetter
Rune.IsWhiteSpace
Rune.IsLetterOrDigit
Rune.GetUnicodeCategory

Para obter o valor escalar bruto de uma instância de Rune , use a propriedade
Rune.Value.

Para converter uma instância de Rune novamente em uma sequência de char s, use o
método Rune.ToString ou Rune.EncodeToUtf16.

Como qualquer valor escalar Unicode é representável por um único char ou por um par
alternativo, qualquer instância de Rune pode ser representada no máximo por duas
instâncias de char . Use Rune.Utf16SequenceLength para ver quantas instâncias de char
são necessárias para representar uma instância de Rune .

Para saber mais sobre o tipo Rune de .NET, confira a Referência da API Rune.

Clusters de grafema
Como o que parece ser um único caractere (char) pode resultar de uma combinação de
diversos pontos de código, um termo mais descritivo e que é geralmente usado no
lugar de "caractere” (char) é cluster de grafema . O termo equivalente no .NET é
elemento de texto.

Considere as instâncias de string "a", "á", "á" e " 👩🏽‍🚒 ". Se o seu sistema operacional
lidar com elas conforme especificado pelo padrão Unicode, cada uma dessas instâncias
de string aparecerá como um único elemento de texto ou cluster de grafema. No
entanto, as duas últimas são representadas por mais de um ponto de código de valor
escalar.

O string "a" é representado por um valor escalar e contém uma instância de char .
U+0061 LATIN SMALL LETTER A

O string "á" é representado por um valor escalar e contém uma instância de char .
U+00E1 LATIN SMALL LETTER A WITH ACUTE

O string "á" é igual ao "á", mas é representado por dois valores escalares e contém
duas instâncias de char .
U+0061 LATIN SMALL LETTER A
U+0301 COMBINING ACUTE ACCENT
Por fim, o string " 👩🏽‍🚒 " é representado por quatro valores escalares e contém
sete instâncias de char .
U+1F469 WOMAN (intervalo suplementar, requer um par alternativo)

U+1F3FD EMOJI MODIFIER FITZPATRICK TYPE-4 (intervalo suplementar, requer um


par alternativo)
U+200D ZERO WIDTH JOINER

U+1F692 FIRE ENGINE (intervalo suplementar, requer um par alternativo)

Em alguns dos exemplos anteriores, como o modificador de acento combinado ou o


modificador de tom de pele, o ponto de código não é exibido como um elemento
autônomo na tela. Em vez disso, ele serve para modificar a aparência de um elemento
de texto que veio antes dele. Esses exemplos mostram que podem ser necessários
diversos valores escalares para compor o que compreendemos como um único
"caractere” (char) ou "cluster de grafema".

Para enumerar os clusters de grafema de um string , use a classe StringInfo, conforme


mostrado no exemplo a seguir. Se você estiver familiarizado com Swift, o tipo
StringInfo do .NET será conceitualmente semelhante ao tipo character do Swift .

Exemplo: contagem de instâncias de char, Rune e


elemento de texto
Nas APIs do .NET, um cluster de grafema é chamado de elemento de texto. O método a
seguir demonstra as diferenças entre instâncias de char , Rune e elemento de texto em
um string :

C#

static void PrintTextElementCount(string s)


{
Console.WriteLine(s);
Console.WriteLine($"Number of chars: {s.Length}");
Console.WriteLine($"Number of runes: {s.EnumerateRunes().Count()}");

TextElementEnumerator enumerator =
StringInfo.GetTextElementEnumerator(s);

int textElementCount = 0;
while (enumerator.MoveNext())
{
textElementCount++;
}

Console.WriteLine($"Number of text elements: {textElementCount}");


}
C#

PrintTextElementCount("a");
// Number of chars: 1
// Number of runes: 1
// Number of text elements: 1

PrintTextElementCount("á");
// Number of chars: 2
// Number of runes: 2
// Number of text elements: 1

PrintTextElementCount("👩🏽‍🚒");
// Number of chars: 7
// Number of runes: 4
// Number of text elements: 1

Se você executar esse código no .NET Framework ou no .NET Core 3.1 ou anterior, a
contagem de elementos de texto do emoji mostrará 4 . Isso ocorre devido a um bug na
classe StringInfo que foi corrigido no .NET 5.

Exemplo: divisão de instâncias de string


Ao dividir instâncias de string , evite dividir pares alternativos e clusters de grafema.
Considere o exemplo a seguir de um código incorreto, que pretende inserir quebras de
linha a cada 10 caracteres (char) em um string:

C#

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.


// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static string InsertNewlinesEveryTencharsBadExample(string input)
{
StringBuilder builder = new StringBuilder();

// First, append chunks in multiples of 10 chars


// followed by a newline.
int i = 0;
for (; i < input.Length - 10; i += 10)
{
builder.Append(input, i, 10);
builder.AppendLine(); // newline
}

// Then append any leftover data followed by


// a final newline.
builder.Append(input, i, input.Length - i);
builder.AppendLine(); // newline
return builder.ToString();
}

Como esse código enumera instâncias de char , um par alternativo que acaba se
estendendo por um limite de 10- char será dividido e uma nova linha será injetada entre
as partes. Essa inserção introduz corrupção de dados, pois os pontos de código
alternativos são significativos somente como pares.

O potencial de corrupção de dados não é eliminado ao enumerar instâncias de Rune


(valores escalares) em vez de instâncias de char . Um conjunto de instâncias de Rune
pode criar um cluster de grafema que se estende por um limite de 10- char . Se o
conjunto de clusters de grafema for dividido, ele não poderá ser interpretado
corretamente.

Uma abordagem melhor é dividir string contando clusters de grafema, ou elementos de


texto, como no seguinte exemplo:

C#

static string InsertNewlinesEveryTenTextElements(string input)


{
StringBuilder builder = new StringBuilder();

// Append chunks in multiples of 10 chars

TextElementEnumerator enumerator =
StringInfo.GetTextElementEnumerator(input);

int textElementCount = 1;
while (enumerator.MoveNext())
{
builder.Append(enumerator.Current);
if (textElementCount % 10 == 0 && textElementCount > 0)
{
builder.AppendLine(); // newline
}
textElementCount++;
}

// Add a final newline.


builder.AppendLine(); // newline
return builder.ToString();

Conforme observado anteriormente, antes do .NET 5, a classe StringInfo continha um


bug fazendo com que alguns clusters de grafema fossem tratados incorretamente.
UTF-8 e UTF-32
As seções anteriores se concentraram em UTF-16 porque é isso o que o .NET usa para
codificar as instâncias de string . Há outros sistemas de codificação para Unicode –
UTF-8 e UTF-32 . Essas codificações usam unidades de código de 8 bits e unidades
de código de 32 bits, respectivamente.

Assim como UTF-16, UTF-8 requer diversas unidades de código para representar alguns
valores escalares Unicode. UTF-32 pode representar qualquer valor escalar em uma
única unidade de código de 32 bits.

Veja os seguintes exemplos que mostram como o mesmo ponto de código Unicode é
representado em cada um desses três sistemas de codificação Unicode:

Scalar: U+0061 LATIN SMALL LETTER A ('a')


UTF-8 : [ 61 ] (1x 8-bit code unit = 8 bits total)
UTF-16: [ 0061 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 00000061 ] (1x 32-bit code unit = 32 bits total)

Scalar: U+0429 CYRILLIC CAPITAL LETTER SHCHA ('Щ')


UTF-8 : [ D0 A9 ] (2x 8-bit code units = 16 bits total)
UTF-16: [ 0429 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 00000429 ] (1x 32-bit code unit = 32 bits total)

Scalar: U+A992 JAVANESE LETTER GA ('ꦒ')


UTF-8 : [ EA A6 92 ] (3x 8-bit code units = 24 bits total)
UTF-16: [ A992 ] (1x 16-bit code unit = 16 bits total)
UTF-32: [ 0000A992 ] (1x 32-bit code unit = 32 bits total)

Scalar: U+104CC OSAGE CAPITAL LETTER TSHA ('𐓌')


UTF-8 : [ F0 90 93 8C ] (4x 8-bit code units = 32 bits total)
UTF-16: [ D801 DCCC ] (2x 16-bit code units = 32 bits total)
UTF-32: [ 000104CC ] (1x 32-bit code unit = 32 bits total)

Como observado anteriormente, uma única unidade de código UTF-16 de um par


alternativo não tem sentido sozinha. Da mesma forma, uma única unidade de código
UTF-8 não terá sentido por si só se estiver em uma sequência de duas, três ou quatro
usada para calcular um valor escalar.

7 Observação

A partir do C# 11, você pode representar literais string UTF-8 usando o sufixo "u8"
em um literal string. Para obter mais informações sobre literais string UTF-8, confira
a stringseção "literais" do artigo sobre tipos de referência internos no Guia C#.
Endianness
No .NET, as unidades de código UTF-16 de um string são armazenadas na memória
contígua como uma sequência de inteiros de 16 bits (instâncias de char ). Os bits de
unidades de código individuais são dispostos de acordo com a extremidade da
arquitetura atual.

Em uma arquitetura little-endian, o string composto pelos pontos de código UTF-16 [


D801 DCCC ] seria disposto na memória como os bytes [ 0x01, 0xD8, 0xCC, 0xDC ] . Em
uma arquitetura big-endian, o mesmo string seria colocado na memória como os bytes
[ 0xD8, 0x01, 0xDC, 0xCC ] .

Os sistemas de computador que se comunicam entre si devem concordar sobre a


representação dos dados em movimento. A maioria dos protocolos de rede usa UTF-8
como padrão ao transmitir textos, em parte para evitar problemas que podem resultar
da comunicação de um computador big-endian com um computador little-endian. O
string composto pelos pontos de código UTF-8 [ F0 90 93 8C ] sempre será
representado como os bytes [ 0xF0, 0x90, 0x93, 0x8C ] , independentemente da
extremidade.

Para usar UTF-8 na transmissão de textos, os aplicativos .NET geralmente usam códigos
como o do seguinte exemplo:

C#

string stringToWrite = GetString();


byte[] stringAsUtf8Bytes = Encoding.UTF8.GetBytes(stringToWrite);
await outputStream.WriteAsync(stringAsUtf8Bytes, 0,
stringAsUtf8Bytes.Length);

No exemplo anterior, o método Encoding.UTF8.GetBytes decodifica o UTF-16 string


novamente em uma série de valores escalares Unicode e, em seguida, recodifica esses
valores escalares em UTF-8 e coloca a sequência resultante em uma matriz byte . O
método Encoding.UTF8.GetString executa a transformação oposta, convertendo uma
matriz UTF-8 byte em um UTF-16 string .

2 Aviso

Como o UTF-8 é comum na Internet, pode ser tentador ler os bytes brutos em
movimento e tratar os dados como se fossem UTF-8. No entanto, é necessário
validar que eles estão realmente bem formados. Um cliente mal-intencionado pode
enviar um UTF-8 mal formado ao serviço. Se você operar com esses dados como se
estivessem bem formados, poderá causar erros ou falhas de segurança em seu
aplicativo. Para validar dados UTF-8, é possível usar um método como
Encoding.UTF8.GetString , que executará a validação ao converter os dados de

entrada em um string .

Codificação bem formada


Uma codificação Unicode bem formada é um string de unidades de código que podem
ser decodificadas sem ambiguidade e sem erros em uma sequência de valores escalares
Unicode. Dados bem formados podem ser transcodificados livremente entre UTF-8,
UTF-16 e UTF-32.

A questão sobre se uma sequência de codificação está bem formada não está
relacionada à extremidade da arquitetura de um computador. Uma sequência UTF-8 mal
formada continua nesse estado em computadores big-endian e little-endian.

Veja os seguintes exemplos de codificações mal formadas:

Em UTF-8, a sequência [ 6C C2 61 ] é mal formada porque C2 não pode ser


seguido por 61 .

Em UTF-16, a sequência [ DC00 DD00 ] (ou string "\udc00\udd00" em C#) é mal


formada porque o DC00 alternativo de nível baixo não pode ser seguido por outro
DD00 alternativo de nível baixo.

Em UTF-32, a sequência [ 0011ABCD ] é mal formada porque 0011ABCD está fora


do intervalo de valores escalares Unicode.

No .NET, as instâncias de string quase sempre contêm dados UTF-16 bem formados,
mas isso não é garantido. Os exemplos a seguir mostram um código C# válido que cria
dados UTF-16 mal formados em instâncias de string .

Um literal mal formado:

C#

const string s = "\ud800";

Um substring que divide um par alternativo:

C#
string x = "\ud83e\udd70"; // "🥰"
string y = x.Substring(1, 1); // "\udd70" standalone low surrogate

As APIs como Encoding.UTF8.GetString nunca retornam instâncias de string mal


formadas. Os métodos Encoding.GetString e Encoding.GetBytes detectam sequências
mal formadas na entrada e realizam a substituição de caracteres (char) ao gerar a saída.
Por exemplo, quando Encoding.ASCII.GetString(byte[]) nota um byte que não é ASCII na
entrada (fora do intervalo U+0000..U+007F), ele insere um '?' na instância de string
retornada. O Encoding.UTF8.GetString(byte[]) substitui sequências UTF-8 mal formadas
por U+FFFD REPLACEMENT CHARACTER ('�') na instância de string retornada. Para saber
mais, confira o Padrão Unicode , nas seções 5.22 e 3.9.

Em vez de executar a substituição de caracteres (char) quando sequências mal formadas


são identificadas, também é possível configurar as classes internas Encoding para gerar
uma exceção. Essa abordagem é frequentemente usada em aplicativos confidenciais em
que a substituição de caracteres (char) pode não ser aceitável.

C#

byte[] utf8Bytes = ReadFromNetwork();


UTF8Encoding encoding = new UTF8Encoding(encoderShouldEmitUTF8Identifier:
false, throwOnInvalidBytes: true);
string asString = encoding.GetString(utf8Bytes); // will throw if
'utf8Bytes' is ill-formed

Para saber como usar as classes internas Encoding , confira Como usar classes de
codificação de caracteres (char) no .NET.

Confira também
String
Char
Rune
Globalização e localização
Como usar as classes de codificação de
caracteres no .NET
Artigo • 10/05/2023

Este artigo explica como usar as classes que o .NET fornece para codificação e
decodificação de texto usando vários esquemas de codificação. As instruções
pressupõem que você tenha lido a Introdução à codificação de caracteres no .NET.

Codificadores e decodificadores
O .NET fornece classes de codificação que codificam e decodificam texto usando vários
sistemas de codificação. Por exemplo, a classe UTF8Encoding descreve as regras para
codificação e decodificação de UTF-8. O .NET usa a codificação UTF-16 (representada
pela classe UnicodeEncoding) para instâncias string . Codificadores e decodificadores
estão disponíveis para outros esquemas de codificação.

Codificar e decodificar também podem incluir a validação. Por exemplo, a classe


UnicodeEncoding verifica todas as instâncias char no intervalo alternativo para garantir
que elas estejam em pares alternativos válidos. Uma estratégia de fallback determina
como um codificador lida com caracteres inválidos ou como um decodificador lida com
bytes inválidos.

2 Aviso

As classes de codificação .NET fornecem uma maneira de armazenar e converter


dados de caractere. Elas não devem ser usadas para armazenar dados binários no
formato de cadeia de caracteres. Dependendo da codificação usada, a conversão
de dados binários em formato de cadeia de caracteres com classes de codificação
pode introduzir um comportamento inesperado e produzir dados imprecisos ou
corrompidos. Para converter dados binários em um formulário de cadeia de
caracteres, use o método Convert.ToBase64String.

Todas as classes de codificação de caracteres no .NET são herdadas da classe


System.Text.Encoding, uma classe abstrata que define a funcionalidade comum a todas
as codificações de caracteres. Para acessar os objetos de codificação individuais
implementados no .NET, faça o seguinte:

Usar propriedades estáticas da classe Encoding, que retornam objetos que


representam as codificações de caracteres padrão disponíveis no .NET (ASCII, UTF-
7, UTF-8, UTF-16 e UTF-32). Por exemplo, a propriedade Encoding.Unicode retorna
um objeto UnicodeEncoding. Cada objeto usa o fallback de substituição para lidar
com cadeias de caracteres que ele não consegue codificar e bytes que ele não
consegue decodificar. Para obter mais informações, consulte Fallback de
substituição.

Chame o construtor de classe da codificação. A instância de objetos para as


codificações ASCII, UTF-7, UTF-8, UTF-16 e UTF-32 podem ser criadas dessa forma.
Por padrão, cada objeto usa fallback de substituição para manipular as cadeias de
caracteres que ele não consegue codificar e bytes que ele não consegue
decodificar, mas, em vez disso, você pode especificar que uma exceção seja
gerada. Para obter mais informações, consulte Fallback de substituição e Fallback
de exceção.

Chame o construtor Encoding(Int32) e passe por ele um inteiro que represente a


codificação. Os objetos de codificação padrão usam o fallback de substituição e a
página de código e o DBCS (conjunto de caracteres de dois bytes) que codificam
objetos usam o fallback que melhor se ajusta para manipular cadeias de caracteres
que eles não conseguem codificar e bytes que eles não conseguem decodificar.
Para obter mais informações, consulte Fallback de melhor ajuste.

Chame o método Encoding.GetEncoding, que retorna qualquer padrão, página de


código ou codificação DBCS disponível no .NET. As sobrecargas permitem
especificar um objeto de fallback para o codificador e o decodificador.

Você pode recuperar informações sobre todas as codificações disponíveis no .NET


chamando o método Encoding.GetEncodings. O .NET dá suporte aos esquemas de
codificação de caracteres listados na tabela a seguir.

Classe de Descrição
codificação

ASCII Codifica um intervalo limitado de caracteres usando os menores sete bits de um


byte. Como essa codificação só dá suporte a valores de caracteres de U+0000 a
U+007F , na maioria dos casos, ela é inadequada para aplicativos
internacionalizados.
Classe de Descrição
codificação

UTF-7 Representa caracteres como sequências de caracteres ASCII de 7 bits. Caracteres


Unicode não ASCII são representados por uma sequência de escape de caracteres
ASCII. O UTF-7 dá suporte a protocolos como os de email e de grupos de notícias.
No entanto, o UTF-7 não é particularmente seguro ou robusto. Em alguns casos,
alterar um bit pode alterar radicalmente a interpretação de toda uma cadeia de
caracteres UTF-7. Em outros casos, diferentes cadeias de caracteres UTF-7 podem
codificar o mesmo texto. Para as sequências que incluem caracteres não ASCII, o
UTF-7 requer mais espaço do que o UTF-8 e a codificação/decodificação é mais
lenta. Consequentemente, você deve usar o UTF-8 em vez do UTF-7, se possível.

UTF-8 Representa cada ponto de código Unicode como uma sequência de um a quatro
bytes. O UTF-8 dá suporte a tamanhos de dados de 8 bits e funciona bem com
muitos sistemas operacionais existentes. Para o intervalo de caracteres ASCII, o
UTF-8 é idêntico à codificação ASCII e permite um conjunto mais amplo de
caracteres. No entanto, para scripts CJK (chinês-japonês-coreano), o UTF-8 pode
exigir três bytes para cada caractere e pode gerar tamanhos de dados maiores do
que o UTF-16. Às vezes, a quantidade de dados ASCII, como tags HTML, justifica o
tamanho maior do intervalo CJK.

UTF-16 Representa cada ponto de código Unicode como uma sequência de um a dois
inteiros de 16 bits. Os caracteres Unicode mais comuns exigem apenas um ponto
de código UTF-16, embora os caracteres suplementares Unicode (U+10000 e
maiores) exigem dois pontos de código UTF-16 alternativos. Há suporte para as
ordens de byte little endian e big endian. A codificação UTF-16 é usada pelo
Common Language Runtime para representar os valores Char e String, e é usada
pelo sistema operacional Windows para representar valores WCHAR .

UTF-32 Representa cada ponto de código Unicode como um inteiro de 32 bits. Há suporte
para as ordens de byte little endian e big endian. A codificação UTF-32 é usada
quando aplicativos desejam evitar o comportamento do ponto de código
alternativo de codificação UTF-16 em sistemas operacionais para os quais o
espaço codificado é muito importante. Glifos únicos renderizados em uma tela
ainda podem ser codificados com mais de um caractere UTF-32.
Classe de Descrição
codificação

Codificação Fornece suporte a uma variedade de páginas de código. Em sistemas operacionais


ANSI/ISO Windows, páginas de código são usadas para oferecer suporte a um idioma
específico ou a um grupo de idiomas. Para uma tabela que lista as páginas de
código com suporte pelo .NET, confira a classe Encoding. Você pode recuperar um
objeto de codificação para uma página de código específico chamando o método
Encoding.GetEncoding(Int32). Uma página de código contém 256 pontos de
código e é baseada em zero. Na maioria das páginas de código, os pontos de
código de 0 a 127 representam o conjunto de caracteres ASCII e pontos de código
de 128 a 255 diferem significativamente entre páginas de código. Por exemplo, a
página de código 1252 fornece os caracteres para sistemas com alfabeto latino,
incluindo inglês, alemão e francês. Os últimos 128 pontos de código na página de
código 1252 contêm os caracteres de acentuação. A página de código 1253
fornece códigos de caracteres que são necessários no sistema alfabético grego. Os
últimos 128 pontos de código na página de código 1253 contêm os caracteres
gregos. Como resultado, um aplicativo que se baseia em páginas de código ANSI
não pode armazenar grego e alemão no mesmo fluxo de texto, a menos que
inclua um identificador que indique a página de código referenciada.

Codificações Oferece suporte a idiomas, como chinês, japonês e coreano, que contêm mais de
de conjunto 256 caracteres. Um DBCS, um par de pontos de código (de byte duplo) representa
de cada caractere. A propriedade Encoding.IsSingleByte retorna false para
caracteres codificações DBCS. Você pode recuperar um objeto de codificação para
de byte determinado DBCS chamando o método Encoding.GetEncoding(Int32). Quando
duplo um aplicativo manipula dados DBCS, o primeiro byte de um caractere DBCS (o
(DBCS) byte inicial) é processado em combinação com o byte final que vem
imediatamente a seguir. Como um único par de pontos de código de byte duplo
pode representar caracteres diferentes dependendo da página de código, esse
esquema ainda não permite a combinação dos dois idiomas, como japonês e
chinês no mesmo fluxo de dados.

Essas codificações permitem que você trabalhe com caracteres Unicode, bem como com
codificações mais comumente usadas em aplicativos herdados. Além disso, você pode
criar uma codificação personalizada definindo uma classe que deriva de Encoding e
substituindo seus membros.

Suporte à codificação no .NET Core


Por padrão, o .NET Core não disponibiliza nenhuma codificação de página de código
diferente de página de código 28591 e das codificações Unicode, como UTF-8 e UTF-16.
No entanto, você pode adicionar as codificações de página de código encontradas em
aplicativos do Windows padrão que direcionam o .NET ao seu aplicativo. Para obter
mais informações, consulte o tópico CodePagesEncodingProvider.
Selecionando uma classe de codificação
Se você tiver a oportunidade de escolher a codificação a ser usada pelo seu aplicativo,
você deverá usar a codificação Unicode, preferencialmente UTF8Encoding ou
UnicodeEncoding. (O .NET também dá suporte a uma terceira codificação Unicode,
UTF32Encoding.)

Se você estiver planejando usar uma codificação ASCII (ASCIIEncoding), escolha


UTF8Encoding em vez disso. As duas codificações são idênticas para o conjunto de
caracteres ASCII, mas o UTF8Encoding tem as seguintes vantagens:

Ele pode representar todos os caracteres Unicode, enquanto o ASCIIEncoding dá


suporte apenas aos valores de caracteres Unicode entre U+0000 e U+007F.

Ele fornece detecção de erros e melhor segurança.

Ele foi ajustado para ser o mais rápido possível e deve ser mais rápido do que
qualquer outra codificação. Mesmo para o conteúdo totalmente ASCII, as
operações executadas com o UTF8Encoding são mais rápidas que as operações
executadas com o ASCIIEncoding.

Você deve considerar usar o ASCIIEncoding apenas para aplicativos herdados. No


entanto, mesmo para aplicativos herdados, o UTF8Encoding pode ser uma escolha
melhor pelos seguintes motivos (supondo as configurações padrão):

Se seu aplicativo tiver conteúdo que não é estritamente ASCII e codificá-lo com o
ASCIIEncoding, cada caractere não ASCII codificará como um ponto de
interrogação (?). Se o aplicativo decodificar esses dados, as informações serão
perdidas.

Se seu aplicativo tiver conteúdo que não é estritamente ASCII e codificá-lo com o
UTF8Encoding, o resultado parecerá ininteligível se interpretado como ASCII. No
entanto, se o aplicativo usar um decodificador de UTF-8 para decodificar esses
dados, os dados executarão uma viagem de ida e volta com êxito.

Em um aplicativo Web, os caracteres enviados ao cliente em resposta a uma solicitação


da Web devem refletir a codificação usada no cliente. Na maioria dos casos, você deve
definir a propriedade HttpResponse.ContentEncoding com o valor retornado pela
propriedade HttpRequest.ContentEncoding para exibir o texto na codificação que o
usuário espera.

Usando um objeto de codificação


Um codificador converte uma cadeia de caracteres (mais comumente, caracteres
Unicode) em seu equivalente numérico (byte). Por exemplo, você pode usar um
codificador ASCII para converter caracteres Unicode em ASCII para que eles possam ser
exibidos no console. Para executar a conversão, você deve chamar o método
Encoding.GetBytes. Se você desejar determinar quantos bytes são necessários para
armazenar os caracteres codificados antes de executar a codificação, você poderá
chamar o método GetByteCount.

O exemplo a seguir usa uma matriz de byte único para codificar cadeias de caracteres
em duas operações separadas. Ela mantém um índice que indica a posição inicial na
matriz de bytes para o próximo conjunto de bytes codificados em ASCII. Ela chama o
método ASCIIEncoding.GetByteCount(String) para garantir que a matriz de bytes seja
grande o suficiente para acomodar a cadeia de caracteres codificada. Depois, chama o
método ASCIIEncoding.GetBytes(String, Int32, Int32, Byte[], Int32) para codificar os
caracteres na cadeia de caracteres.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
string[] strings= { "This is the first sentence. ",
"This is the second sentence. " };
Encoding asciiEncoding = Encoding.ASCII;

// Create array of adequate size.


byte[] bytes = new byte[49];
// Create index for current position of array.
int index = 0;

Console.WriteLine("Strings to encode:");
foreach (var stringValue in strings) {
Console.WriteLine(" {0}", stringValue);

int count = asciiEncoding.GetByteCount(stringValue);


if (count + index >= bytes.Length)
Array.Resize(ref bytes, bytes.Length + 50);

int written = asciiEncoding.GetBytes(stringValue, 0,


stringValue.Length,
bytes, index);

index = index + written;


}
Console.WriteLine("\nEncoded bytes:");
Console.WriteLine("{0}", ShowByteValues(bytes, index));
Console.WriteLine();

// Decode Unicode byte array to a string.


string newString = asciiEncoding.GetString(bytes, 0, index);
Console.WriteLine("Decoded: {0}", newString);
}

private static string ShowByteValues(byte[] bytes, int last )


{
string returnString = " ";
for (int ctr = 0; ctr <= last - 1; ctr++) {
if (ctr % 20 == 0)
returnString += "\n ";
returnString += String.Format("{0:X2} ", bytes[ctr]);
}
return returnString;
}
}
// The example displays the following output:
// Strings to encode:
// This is the first sentence.
// This is the second sentence.
//
// Encoded bytes:
//
// 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
// 6E 74 65 6E 63 65 2E 20 54 68 69 73 20 69 73 20 74 68 65 20
// 73 65 63 6F 6E 64 20 73 65 6E 74 65 6E 63 65 2E 20
//
// Decoded: This is the first sentence. This is the second sentence.

Um decodificador converte uma matriz de bytes que reflete uma codificação de


caracteres específica em um conjunto de caracteres, seja em uma matriz de caracteres
ou em uma cadeia de caracteres. Para decodificar uma matriz de bytes em uma matriz
de caracteres, chame o método Encoding.GetChars. Para decodificar uma matriz de
bytes em uma cadeia de caracteres, chame o método GetString. Se você desejar
determinar quantos caracteres são necessários para armazenar os bytes decodificados
antes de executar a decodificação, você poderá chamar o método GetCharCount.

O exemplo a seguir codifica três cadeias de caracteres e, em seguida, as decodifica em


uma única matriz de caracteres. Ela mantém um índice que indica a posição inicial na
matriz de bytes para o próximo conjunto de caracteres codificados. Ela chama o método
GetCharCount para garantir que a matriz de caracteres seja grande o suficiente para
acomodar todos os caracteres decodificados. Depois, chama o método
ASCIIEncoding.GetChars(Byte[], Int32, Int32, Char[], Int32) para decodificar a matriz de
bytes.

C#
using System;
using System.Text;

public class Example


{
public static void Main()
{
string[] strings = { "This is the first sentence. ",
"This is the second sentence. ",
"This is the third sentence. " };
Encoding asciiEncoding = Encoding.ASCII;
// Array to hold encoded bytes.
byte[] bytes;
// Array to hold decoded characters.
char[] chars = new char[50];
// Create index for current position of character array.
int index = 0;

foreach (var stringValue in strings) {


Console.WriteLine("String to Encode: {0}", stringValue);
// Encode the string to a byte array.
bytes = asciiEncoding.GetBytes(stringValue);
// Display the encoded bytes.
Console.Write("Encoded bytes: ");
for (int ctr = 0; ctr < bytes.Length; ctr++)
Console.Write(" {0}{1:X2}",
ctr % 20 == 0 ? Environment.NewLine : "",
bytes[ctr]);
Console.WriteLine();

// Decode the bytes to a single character array.


int count = asciiEncoding.GetCharCount(bytes);
if (count + index >= chars.Length)
Array.Resize(ref chars, chars.Length + 50);

int written = asciiEncoding.GetChars(bytes, 0,


bytes.Length,
chars, index);
index = index + written;
Console.WriteLine();
}

// Instantiate a single string containing the characters.


string decodedString = new string(chars, 0, index - 1);
Console.WriteLine("Decoded string: ");
Console.WriteLine(decodedString);
}
}
// The example displays the following output:
// String to Encode: This is the first sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 66 69 72 73 74 20 73 65
// 6E 74 65 6E 63 65 2E 20
//
// String to Encode: This is the second sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 73 65 63 6F 6E 64 20 73
// 65 6E 74 65 6E 63 65 2E 20
//
// String to Encode: This is the third sentence.
// Encoded bytes:
// 54 68 69 73 20 69 73 20 74 68 65 20 74 68 69 72 64 20 73 65
// 6E 74 65 6E 63 65 2E 20
//
// Decoded string:
// This is the first sentence. This is the second sentence. This is the
third sentence.

Os métodos de codificação e decodificação de uma classe derivada de Encoding foram


criados para funcionar em um conjunto completo de dados; ou seja, todos os dados a
serem codificados ou decodificados são fornecido em uma única chamada de método.
No entanto, em alguns casos, os dados estão disponíveis em um fluxo e os dados a
serem codificados ou decodificados podem estar disponíveis somente em operações de
leitura separadas. Isso requer a operação de codificação ou decodificação para lembrar
qualquer estado salvo em sua invocação anterior. Os métodos de classes derivados de
Encoder e Decoder são capazes de lidar com as operações de codificação e
decodificação que abrangem várias chamadas de método.

Um objeto Encoder para uma codificação específica está disponível por meio da
propriedade Encoding.GetEncoder da codificação. Um objeto Decoder para uma
codificação específica está disponível por meio da propriedade Encoding.GetDecoder da
codificação. Para operações de decodificação, observe que as classes derivadas de
Decoder incluem um método Decoder.GetChars, mas não têm um método que
corresponde a Encoding.GetString.

O exemplo a seguir ilustra a diferença entre usar os métodos Encoding.GetString e


Decoder.GetChars para decodificar uma matriz de bytes Unicode. O exemplo codifica
uma cadeia de caracteres que contém alguns caracteres Unicode em um arquivo e, em
seguida, usa os dois métodos de decodificação para decodificá-los dez bytes por vez.
Como um par alternativo ocorre nos bytes décimo e décimo primeiro, ele é
decodificado em chamadas de método separadas. Como mostra a saída, o método
Encoding.GetString não é capaz de decodificar corretamente os bytes e, em vez disso,
os substitui por U+FFFD (CARACTERE DE SUBSTITUIÇÃO). Por outro lado, o método
Decoder.GetChars é capaz de decodificar com êxito a matriz de bytes para obter a
cadeia de caracteres original.

C#
using System;
using System.IO;
using System.Text;

public class Example


{
public static void Main()
{
// Use default replacement fallback for invalid encoding.
UnicodeEncoding enc = new UnicodeEncoding(true, false, false);

// Define a string with various Unicode characters.


string str1 = "AB YZ 19 \uD800\udc05 \u00e4";
str1 += "Unicode characters. \u00a9 \u010C s \u0062\u0308";
Console.WriteLine("Created original string...\n");

// Convert string to byte array.


byte[] bytes = enc.GetBytes(str1);

FileStream fs = File.Create(@".\characters.bin");
BinaryWriter bw = new BinaryWriter(fs);
bw.Write(bytes);
bw.Close();

// Read bytes from file.


FileStream fsIn = File.OpenRead(@".\characters.bin");
BinaryReader br = new BinaryReader(fsIn);

const int count = 10; // Number of bytes to read at a time.


byte[] bytesRead = new byte[10]; // Buffer (byte array).
int read; // Number of bytes actually read.
string str2 = String.Empty; // Decoded string.

// Try using Encoding object for all operations.


do {
read = br.Read(bytesRead, 0, count);
str2 += enc.GetString(bytesRead, 0, read);
} while (read == count);
br.Close();
Console.WriteLine("Decoded string using
UnicodeEncoding.GetString()...");
CompareForEquality(str1, str2);
Console.WriteLine();

// Use Decoder for all operations.


fsIn = File.OpenRead(@".\characters.bin");
br = new BinaryReader(fsIn);
Decoder decoder = enc.GetDecoder();
char[] chars = new char[50];
int index = 0; // Next character to write in array.
int written = 0; // Number of chars written to array.
do {
read = br.Read(bytesRead, 0, count);
if (index + decoder.GetCharCount(bytesRead, 0, read) - 1 >=
chars.Length)
Array.Resize(ref chars, chars.Length + 50);

written = decoder.GetChars(bytesRead, 0, read, chars, index);


index += written;
} while (read == count);
br.Close();
// Instantiate a string with the decoded characters.
string str3 = new String(chars, 0, index);
Console.WriteLine("Decoded string using
UnicodeEncoding.Decoder.GetString()...");
CompareForEquality(str1, str3);
}

private static void CompareForEquality(string original, string decoded)


{
bool result = original.Equals(decoded);
Console.WriteLine("original = decoded: {0}",
original.Equals(decoded, StringComparison.Ordinal));
if (! result) {
Console.WriteLine("Code points in original string:");
foreach (var ch in original)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();

Console.WriteLine("Code points in decoded string:");


foreach (var ch in decoded)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Created original string...
//
// Decoded string using UnicodeEncoding.GetString()...
// original = decoded: False
// Code points in original string:
// 0041 0042 0020 0059 005A 0020 0031 0039 0020 D800 DC05 0020 00E4 0055
006E 0069 0063 006F
// 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E
0020 00A9 0020 010C
// 0020 0073 0020 0062 0308
// Code points in decoded string:
// 0041 0042 0020 0059 005A 0020 0031 0039 0020 FFFD FFFD 0020 00E4 0055
006E 0069 0063 006F
// 0064 0065 0020 0063 0068 0061 0072 0061 0063 0074 0065 0072 0073 002E
0020 00A9 0020 010C
// 0020 0073 0020 0062 0308
//
// Decoded string using UnicodeEncoding.Decoder.GetString()...
// original = decoded: True
Escolhendo uma estratégia de fallback
Quando um método tenta codificar ou decodificar um caractere, mas não existe
nenhum mapeamento, ele deve implementar uma estratégia de fallback que determine
como o mapeamento com falha deva ser tratado. Há três tipos de estratégias de
fallback:

Fallback de melhor ajuste

Fallback de substituição

Fallback de exceção

) Importante

Os problemas mais comuns em operações de codificação ocorrem quando um


caractere Unicode não pode ser mapeado para uma determinada codificação de
página de código. Os problemas mais comuns em operações de decodificação
ocorrem quando sequências de bytes inválidas não podem ser convertidas em
caracteres Unicode válidos. Por esses motivos, você deve saber qual estratégia de
fallback um objeto de codificação específico usa. Sempre que possível, você deve
especificar a estratégia de fallback usada por um objeto de codificação quando
você criar uma instância do objeto.

Fallback de melhor ajuste


Quando um caractere não tiver uma correspondência exata na codificação de destino, o
codificador pode tentar mapeá-lo para um caractere semelhante. (O fallback de melhor
ajuste é mais um problema de codificação do que de decodificação. Há muito poucas
páginas de código que contêm caracteres que não podem ser mapeados com êxito para
Unicode.) O fallback de melhor ajuste é o padrão para codificações de conjunto de
caracteres de dois bytes e página de código que são recuperadas pelas sobrecargas
Encoding.GetEncoding(Int32) e Encoding.GetEncoding(String).

7 Observação

Na teoria, as classes de codificação Unicode fornecidas no .NET (UTF8Encoding,


UnicodeEncoding e UTF32Encoding) dão suporte a todos os caracteres em cada
conjunto de caracteres para que eles possam ser usados para eliminar problemas
de fallback de melhor ajuste.
Estratégias de melhor ajuste variam para páginas de código diferentes. Por exemplo,
para algumas páginas de código, caracteres latinos de largura inteira mapeiam para os
caracteres latinos de meia largura mais comuns. Para outras páginas de código, esse
mapeamento não é feito. Mesmo em uma estratégia de melhor ajuste agressiva, não há
nenhum ajuste imaginável para alguns caracteres em algumas codificações. Por
exemplo, um ideograma chinês tem nenhum mapeamento razoável para a página de
código 1252. Nesse caso, é usada uma cadeia de caracteres de substituição. Por padrão,
essa cadeia de caracteres é apenas um PONTO DE INTERROGAÇÃO (U+003F).

7 Observação

Estratégias de melhor ajuste não estão documentadas em detalhes. No entanto,


várias páginas de código estão documentadas no site do consórcio Unicode .
Examine o arquivo leiame.txt nessa pasta para obter uma descrição de como
interpretar os arquivos de mapeamento.

O exemplo a seguir usa a página de código 1252 (a página de código do Windows para
idiomas da Europa Ocidental) para ilustrar o mapeamento de melhor ajuste e suas
desvantagens. O método Encoding.GetEncoding(Int32) é usado para recuperar um
objeto de codificação de página de código 1252. Por padrão, ele usa um mapeamento
de melhor ajuste para caracteres Unicode aos quais ele não dá suporte. O exemplo cria
uma instância de uma cadeia de caracteres que contém três caracteres não ASCII –
LETRA MAIÚSCULA LATINA S CIRCULADA (U+24C8), CINCO SOBRESCRITO (U+2075) e
INFINITO (U+221E) – separados por espaços. Como mostra a saída de exemplo, quando
a cadeia de caracteres é codificada, os três caracteres originais sem espaço são
substituídos pelo PONTO DE INTERROGAÇÃO (U+003F), DÍGITO CINCO (U+0035) e
DÍGITO OITO (U+0038). DÍGITO OITO é um substituto especialmente ruim para o
caractere INFINITO sem suporte e o PONTO DE INTERROGAÇÃO indica que nenhum
mapeamento estava disponível para o caractere original.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
// Get an encoding for code page 1252 (Western Europe character set).
Encoding cp1252 = Encoding.GetEncoding(1252);

// Define and display a string.


string str = "\u24c8 \u2075 \u221e";
Console.WriteLine("Original string: " + str);
Console.Write("Code points in string: ");
foreach (var ch in str)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine("\n");

// Encode a Unicode string.


Byte[] bytes = cp1252.GetBytes(str);
Console.Write("Encoded bytes: ");
foreach (byte byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");

// Decode the string.


string str2 = cp1252.GetString(bytes);
Console.WriteLine("String round-tripped: {0}", str.Equals(str2));
if (! str.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));
}
}
}
// The example displays the following output:
// Original string: Ⓢ ⁵ ∞
// Code points in string: 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 35 20 38
//
// String round-tripped: False
// ? 5 8
// 003F 0020 0035 0020 0038

O mapeamento de melhor ajuste é o comportamento padrão para um objeto Encoding


que codifica dados Unicode em dados da página de código e há aplicativos herdados
que se baseiam nesse comportamento. No entanto, a maioria dos novos aplicativos
devem evitar o comportamento de melhor ajuste por motivos de segurança. Por
exemplo, os aplicativos não devem colocar um nome de domínio por meio de uma
codificação de melhor ajuste.

7 Observação

Você também pode implementar um mapeamento de fallback de melhor ajuste


personalizado para uma codificação. Para obter mais informações, consulte a seção
Implementando uma estratégia de fallback personalizada.
Se o fallback mais adequado for o padrão para um objeto de codificação, você poderá
escolher outra estratégia de fallback ao recuperar um objeto Encoding chamando a
sobrecarga Encoding.GetEncoding(Int32, EncoderFallback, DecoderFallback) ou
Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). A seção a seguir inclui
um exemplo que substitui cada caractere que não pode ser mapeado para a página de
código 1252 com um asterisco (*).

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
Encoding cp1252r = Encoding.GetEncoding(1252,
new EncoderReplacementFallback("*"),
new DecoderReplacementFallback("*"));

string str1 = "\u24C8 \u2075 \u221E";


Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();

byte[] bytes = cp1252r.GetBytes(str1);


string str2 = cp1252r.GetString(bytes);
Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A

Fallback de substituição
Quando um caractere não tem uma correspondência exata do esquema de destino, mas
não há nenhum caractere apropriado para o qual ele pode ser mapeado, o aplicativo
pode especificar um caractere ou uma cadeia de caracteres de substituição. Esse é o
comportamento padrão para o decodificador de Unicode, que substitui qualquer
sequência de dois bytes que ele não pode decodificar por REPLACEMENT_CHARACTER
(U+FFFD). Também é o comportamento padrão da classe ASCIIEncoding, que substitui
cada caractere que ela não pode codificar ou decodificar por um ponto de interrogação.
O exemplo a seguir ilustra a substituição de caracteres para a cadeia de caracteres
Unicode do exemplo anterior. Como mostra a saída, cada caractere que não puder ser
decodificado em um valor de bytes ASCII é substituído por 0x3F, que é o código ASCII
para um ponto de interrogação.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
Encoding enc = Encoding.ASCII;

string str1 = "\u24C8 \u2075 \u221E";


Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine("\n");

// Encode the original string using the ASCII encoder.


byte[] bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);
Console.WriteLine("\n");

// Decode the ASCII bytes.


string str2 = enc.GetString(bytes);
Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Encoded bytes: 3F 20 3F 20 3F
//
// Round-trip: False
// ? ? ?
// 003F 0020 003F 0020 003F

O .NET inclui as classes EncoderReplacementFallback e DecoderReplacementFallback,


que substituem uma cadeia de caracteres de substituição se um caractere não é
mapeado exatamente em uma operação de codificação ou decodificação. Por padrão,
essa cadeia de caracteres de substituição é um ponto de interrogação, mas você pode
chamar uma sobrecarga do construtor de classes para escolher uma cadeia de
caracteres diferente. Normalmente, a cadeia de caracteres de substituição é um único
caractere, embora isso não seja um requisito. O exemplo a seguir altera o
comportamento do codificador da página de código 1252 criando uma instância de um
objeto EncoderReplacementFallback que usa um asterisco (*) como uma cadeia de
caracteres de substituição.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
Encoding cp1252r = Encoding.GetEncoding(1252,
new EncoderReplacementFallback("*"),
new DecoderReplacementFallback("*"));

string str1 = "\u24C8 \u2075 \u221E";


Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();

byte[] bytes = cp1252r.GetBytes(str1);


string str2 = cp1252r.GetString(bytes);
Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();
}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
// Round-trip: False
// * * *
// 002A 0020 002A 0020 002A

7 Observação

Você também pode implementar uma classe de substituição para uma codificação.
Para obter mais informações, consulte a seção Implementando uma estratégia de
fallback personalizada.

Além de PONTO DE INTERROGAÇÃO (U+003F), o CARACTERE DE SUBSTITUIÇÃO


Unicode (U+FFFD) normalmente é usado como uma cadeia de caracteres de
substituição, especialmente ao decodificar sequências de bytes que não podem ser
convertidas com êxito em caracteres Unicode. No entanto, você está livre para escolher
qualquer cadeia de caracteres de substituição e ela pode conter vários caracteres.

Fallback de exceção
Em vez de oferecer um fallback de melhor ajuste ou uma cadeia de caracteres de
substituição, um codificador pode lançar uma EncoderFallbackException se não for
possível codificar um conjunto de caracteres e um decodificador pode lançar uma
DecoderFallbackException se não for possível decodificar uma matriz de bytes. Para
gerar uma exceção em operações de codificação e decodificação, você deve fornecer
um objeto EncoderExceptionFallback e um objeto DecoderExceptionFallback,
respectivamente, para o método Encoding.GetEncoding(String, EncoderFallback,
DecoderFallback). O exemplo a seguir ilustra a o fallback de exceção com a classe
ASCIIEncoding.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
Encoding enc = Encoding.GetEncoding("us-ascii",
new EncoderExceptionFallback(),
new DecoderExceptionFallback());
string str1 = "\u24C8 \u2075 \u221E";
Console.WriteLine(str1);
foreach (var ch in str1)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine("\n");

// Encode the original string using the ASCII encoder.


byte[] bytes = {};
try {
bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);

Console.WriteLine();
}
catch (EncoderFallbackException e) {
Console.Write("Exception: ");
if (e.IsUnknownSurrogate())
Console.WriteLine("Unable to encode surrogate pair 0x{0:X4}
0x{1:X3} at index {2}.",
Convert.ToUInt16(e.CharUnknownHigh),
Convert.ToUInt16(e.CharUnknownLow),
e.Index);
else
Console.WriteLine("Unable to encode 0x{0:X4} at index {1}.",
Convert.ToUInt16(e.CharUnknown),
e.Index);
return;
}
Console.WriteLine();

// Decode the ASCII bytes.


try {
string str2 = enc.GetString(bytes);
Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();
}
}
catch (DecoderFallbackException e) {
Console.Write("Unable to decode byte(s) ");
foreach (byte unknown in e.BytesUnknown)
Console.Write("0x{0:X2} ");

Console.WriteLine("at index {0}", e.Index);


}
}
}
// The example displays the following output:
// Ⓢ ⁵ ∞
// 24C8 0020 2075 0020 221E
//
// Exception: Unable to encode 0x24C8 at index 0.

7 Observação

Você também pode implementar um manipulador de exceção personalizado para


uma operação de codificação. Para obter mais informações, consulte a seção
Implementando uma estratégia de fallback personalizada.

Os objetos EncoderFallbackException e DecoderFallbackException fornecem as


seguintes informações sobre a condição que causou a exceção:

O objeto EncoderFallbackException inclui um método IsUnknownSurrogate, que


indica se o caractere ou caracteres que não podem ser codificados representam
um par alternativo desconhecido (nesse caso, o método retorna true ) ou um
único caractere desconhecido (nesse caso, o método retorna false ). Os caracteres
do par substituto estão disponíveis nas propriedades
EncoderFallbackException.CharUnknownHigh e
EncoderFallbackException.CharUnknownLow. O único caractere desconhecido é
proveniente da propriedade EncoderFallbackException.CharUnknown. A
propriedade EncoderFallbackException.Index indica a posição na cadeia de
caracteres na qual o primeiro caractere que não pôde ser codificado foi
encontrado.

O objeto DecoderFallbackException inclui uma propriedade BytesUnknown que


retorna uma matriz de bytes que não pode ser decodificada. A propriedade
DecoderFallbackException.Index indica a posição inicial dos bytes desconhecidos.

Embora os objetos EncoderFallbackException e DecoderFallbackException forneçam


informações de diagnóstico adequadas sobre a exceção, eles não fornecem acesso ao
buffer de codificação ou decodificação. Portanto, eles não permitem que dados
inválidos sejam substituídos ou corrigidos dentro do método de codificação ou de
decodificação.

Implementando uma estratégia de fallback


personalizada
Além do mapeamento de melhor ajuste implementado internamente por páginas de
código, o .NET inclui as seguintes classes para implementar uma estratégia de fallback:
Use EncoderReplacementFallback e EncoderReplacementFallbackBuffer para
substituir caracteres em operações de codificação.

Use DecoderReplacementFallback e DecoderReplacementFallbackBuffer para


substituir caracteres em operações de decodificação.

Use EncoderExceptionFallback e EncoderExceptionFallbackBuffer para lançar uma


EncoderFallbackException quando um caractere não puder ser codificado.

Use DecoderExceptionFallback e DecoderExceptionFallbackBuffer para lançar uma


DecoderFallbackException quando um caractere não puder ser decodificado.

Além disso, você pode implementar uma solução personalizada que usa o fallback de
melhor ajuste, fallback de substituição ou fallback de exceção seguindo estas etapas:

1. Derive uma classe de EncoderFallback para operações de codificação e de


DecoderFallback para operações de decodificação.

2. Derive uma classe de EncoderFallbackBuffer para operações de codificação e de


DecoderFallbackBuffer para operações de decodificação.

3. Para o fallback de exceção, se as classes predefinidas EncoderFallbackException e


DecoderFallbackException não atenderem às suas necessidades, derive uma classe
de um objeto de exceção como Exception ou ArgumentException.

Derivando de EncoderFallback ou DecoderFallback


Para implementar uma solução de fallback personalizada, você deverá criar uma classe
herdada de EncoderFallback para operações de codificação e de DecoderFallback para
operações de decodificação. As instâncias dessas classes são passadas para o método
Encoding.GetEncoding(String, EncoderFallback, DecoderFallback) e servem como um
intermediário entre a classe de codificação e a implementação de fallback.

Quando você cria uma solução de fallback personalizada para um codificador ou um


decodificador, você deve implementar os seguintes membros:

A propriedade EncoderFallback.MaxCharCount ou DecoderFallback.MaxCharCount,


que retorna o número máximo possível de caracteres que o fallback de melhor
ajuste, de substituição ou de exceção pode retornar para substituir um único
caractere. Para um fallback de exceção personalizado, seu valor é zero.

O método EncoderFallback.CreateFallbackBuffer ou
DecoderFallback.CreateFallbackBuffer, que retorna a implementação personalizada
EncoderFallbackBuffer ou DecoderFallbackBuffer. O método é chamado pelo
codificador quando ele encontra o primeiro caractere que ele não consegue
codificar com êxito ou pelo decodificador quando ele encontra o primeiro byte
que ele não consegue decodificar com êxito.

Derivando de EncoderFallbackBuffer ou
DecoderFallbackBuffer
Para implementar uma solução de fallback personalizada, você deverá criar também
uma classe herdada de EncoderFallbackBuffer para operações de codificação e de
DecoderFallbackBuffer para operações de decodificação. As instâncias dessas classes
são retornadas pelo método CreateFallbackBuffer das classes EncoderFallback e
DecoderFallback. O método EncoderFallback.CreateFallbackBuffer é chamado pelo
codificador quando ele encontra o primeiro caractere que ele não consegue codificar e
o método DecoderFallback.CreateFallbackBuffer é chamado pelo decodificador quando
ele encontra um ou mais bytes que ele não consegue decodificar. As classes
EncoderFallbackBuffer e DecoderFallbackBuffer fornecem a implementação de fallback.
Cada instância representa um buffer que contém os caracteres de fallback que
substituirão o caractere que não pode ser codificado ou a sequência de bytes que não
pode ser decodificada.

Quando você cria uma solução de fallback personalizada para um codificador ou um


decodificador, você deve implementar os seguintes membros:

O método EncoderFallbackBuffer.Fallback ou DecoderFallbackBuffer.Fallback.


EncoderFallbackBuffer.Fallback é chamado pelo codificador para fornecer ao buffer
de fallback informações sobre o caractere que ele não consegue codificar. Como o
caractere a ser codificado pode ser um par alternativo, esse método está
sobrecarregado. Uma sobrecarga é passada para o caractere a ser codificado e
para o seu índice na cadeia de caracteres. A segunda sobrecarga é passada para os
substitutos altos e baixos juntamente com seu índice na cadeia de caracteres. O
método DecoderFallbackBuffer.Fallback é chamado pelo decodificador para
fornecer ao buffer de fallback informações sobre os bytes que ele não consegue
decodificar. Esse método recebe uma matriz de bytes que ele não consegue
decodificar, juntamente com o índice do primeiro byte. O método de fallback
deverá retornar true se o buffer de fallback conseguir fornecer caracteres de
melhor ajuste ou de substituição; caso contrário, ele deverá retornar false . Para
um fallback de exceção, o método de fallback deve lançar uma exceção.

O método EncoderFallbackBuffer.GetNextChar ou
DecoderFallbackBuffer.GetNextChar, que é chamado repetidamente pelo
codificador ou decodificador para obter o próximo caractere do buffer de fallback.
Quando todos os caracteres de fallback tiverem sido retornados, o método deverá
retornar U+0000.

A propriedade EncoderFallbackBuffer.Remaining ou
DecoderFallbackBuffer.Remaining, que retorna o número de caracteres restantes
no buffer de fallback.

O método EncoderFallbackBuffer.MovePrevious ou
DecoderFallbackBuffer.MovePrevious, que move a posição atual no buffer de
fallback para o caractere anterior.

O método EncoderFallbackBuffer.Reset ou DecoderFallbackBuffer.Reset, que


reinicializa o buffer de fallback.

Se a implementação de fallback for um fallback de melhor ajuste ou de substituição, as


classes derivadas de EncoderFallbackBuffer e DecoderFallbackBuffer também mantêm
dois campos de instância privados: o número exato de caracteres no buffer e o índice
do próximo caractere no buffer a ser retornado.

Um exemplo de EncoderFallback
Um exemplo anterior usava o fallback de substituição para substituir caracteres Unicode
que não correspondiam aos caracteres ASCII por um asterisco (*). O exemplo a seguir
usa uma implementação de fallback de melhor ajuste personalizada em vez de fornecer
um melhor mapeamento de caracteres não ASCII.

O código a seguir define uma classe chamada CustomMapper derivada de


EncoderFallback para lidar com o mapeamento de melhor ajuste de caracteres não
ASCII. Seu método CreateFallbackBuffer retorna um objeto
CustomMapperFallbackBuffer , que fornece a implementação EncoderFallbackBuffer. A

classe CustomMapper usa um objeto Dictionary<TKey,TValue> para armazenar os


mapeamentos de caracteres Unicode sem suporte (o valor da chave) e seus caracteres
de 8 bits correspondentes (armazenados em dois bytes consecutivos em um inteiro de
64 bits). Para disponibilizar esse mapeamento ao buffer de fallback, a instância
CustomMapper é passada como um parâmetro para o construtor de classe

CustomMapperFallbackBuffer . Como o mapeamento mais longo é a cadeia de caracteres

"INF" para o caractere Unicode U+221E, a propriedade MaxCharCount retorna 3.

C#

public class CustomMapper : EncoderFallback


{
public string DefaultString;
internal Dictionary<ushort, ulong> mapping;

public CustomMapper() : this("*")


{
}

public CustomMapper(string defaultString)


{
this.DefaultString = defaultString;

// Create table of mappings


mapping = new Dictionary<ushort, ulong>();
mapping.Add(0x24C8, 0x53);
mapping.Add(0x2075, 0x35);
mapping.Add(0x221E, 0x49004E0046);
}

public override EncoderFallbackBuffer CreateFallbackBuffer()


{
return new CustomMapperFallbackBuffer(this);
}

public override int MaxCharCount


{
get { return 3; }
}
}

O código a seguir define a classe CustomMapperFallbackBuffer , derivada de


EncoderFallbackBuffer. O dicionário que contém mapeamentos de melhor ajuste e
definido na instância CustomMapper está disponível no seu construtor de classe. Seu
método Fallback retornará true se qualquer um dos caracteres Unicode que o
codificador ASCII não conseguir codificar forem definidos no dicionário de
mapeamento; caso contrário, retornará false . Para cada fallback, a variável count
privada indica o número de caracteres que continuam sendo retornados e a variável
index privada indica a posição no buffer de cadeia de caracteres, charsToReturn , do
próximo caractere a ser retornado.

C#

public class CustomMapperFallbackBuffer : EncoderFallbackBuffer


{
int count = -1; // Number of characters to return
int index = -1; // Index of character to return
CustomMapper fb;
string charsToReturn;

public CustomMapperFallbackBuffer(CustomMapper fallback)


{
this.fb = fallback;
}

public override bool Fallback(char charUnknownHigh, char charUnknownLow,


int index)
{
// Do not try to map surrogates to ASCII.
return false;
}

public override bool Fallback(char charUnknown, int index)


{
// Return false if there are already characters to map.
if (count >= 1) return false;

// Determine number of characters to return.


charsToReturn = String.Empty;

ushort key = Convert.ToUInt16(charUnknown);


if (fb.mapping.ContainsKey(key)) {
byte[] bytes = BitConverter.GetBytes(fb.mapping[key]);
int ctr = 0;
foreach (var byt in bytes) {
if (byt > 0) {
ctr++;
charsToReturn += (char) byt;
}
}
count = ctr;
}
else {
// Return default.
charsToReturn = fb.DefaultString;
count = 1;
}
this.index = charsToReturn.Length - 1;

return true;
}

public override char GetNextChar()


{
// We'll return a character if possible, so subtract from the count of
chars to return.
count--;
// If count is less than zero, we've returned all characters.
if (count < 0)
return '\u0000';

this.index--;
return charsToReturn[this.index + 1];
}

public override bool MovePrevious()


{
// Original: if count >= -1 and pos >= 0
if (count >= -1) {
count++;
return true;
}
else {
return false;
}
}

public override int Remaining


{
get { return count < 0 ? 0 : count; }
}

public override void Reset()


{
count = -1;
index = -1;
}
}

O código a seguir instancia o objeto CustomMapper e passa uma instância dele para o
método Encoding.GetEncoding(String, EncoderFallback, DecoderFallback). A saída indica
que a implementação de fallback de melhor ajuste lida com êxito com os três caracteres
não ASCII na cadeia de caracteres original.

C#

using System;
using System.Collections.Generic;
using System.Text;

class Program
{
static void Main()
{
Encoding enc = Encoding.GetEncoding("us-ascii", new CustomMapper(),
new DecoderExceptionFallback());

string str1 = "\u24C8 \u2075 \u221E";


Console.WriteLine(str1);
for (int ctr = 0; ctr <= str1.Length - 1; ctr++) {
Console.Write("{0} ", Convert.ToUInt16(str1[ctr]).ToString("X4"));
if (ctr == str1.Length - 1)
Console.WriteLine();
}
Console.WriteLine();

// Encode the original string using the ASCII encoder.


byte[] bytes = enc.GetBytes(str1);
Console.Write("Encoded bytes: ");
foreach (var byt in bytes)
Console.Write("{0:X2} ", byt);

Console.WriteLine("\n");

// Decode the ASCII bytes.


string str2 = enc.GetString(bytes);
Console.WriteLine("Round-trip: {0}", str1.Equals(str2));
if (! str1.Equals(str2)) {
Console.WriteLine(str2);
foreach (var ch in str2)
Console.Write("{0} ", Convert.ToUInt16(ch).ToString("X4"));

Console.WriteLine();
}
}
}

Confira também
Introdução à codificação de caracteres no .NET
Encoder
Decoder
DecoderFallback
Encoding
EncoderFallback
Globalização e localização
Práticas recomendadas para comparar
cadeias de caracteres no .NET
Artigo • 10/03/2023

O .NET dá um amplo suporte para desenvolvimento de aplicativos localizados e


globalizados e torna mais fácil aplicar as convenções da cultura atual ou de uma cultura
específica ao executar operações comuns, como a classificação e a exibição cadeias de
caracteres. Mas classificar ou comparar cadeias de caracteres nem sempre é uma
operação sensível à cultura. Por exemplo, cadeias de caracteres que são usadas
internamente por um aplicativo normalmente devem ser manipuladas de maneira
idêntica em todas as culturas. Quando os dados de cadeias de caracteres culturalmente
independentes, como marcações XML, marcações HTML, nomes de usuário, caminhos
de arquivo e nomes de objetos do sistema, são interpretados como se levassem em
conta a cultura, o código do aplicativo pode estar sujeito a bugs sutis, desempenho
ruim e, em alguns casos, problemas de segurança.

Este artigo examina os métodos de classificação, comparação e o uso de maiúsculas e


minúsculas de cadeias de caracteres no .NET, apresenta recomendações para a seleção
de um método de manipulação de cadeia de caracteres adequado e fornece
informações adicionais sobre os métodos de manipulação de cadeia de caracteres.

Recomendações para uso da cadeia de


caracteres
Ao desenvolver com o .NET, siga estas recomendações ao comparar cadeias de
caracteres:

Use sobrecargas que especificam explicitamente as regras de comparação de


cadeias de caracteres para operações de cadeia de caracteres. Normalmente, isso
envolve chamar uma sobrecarga de método que tem um parâmetro do tipo
StringComparison.
Use StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase para
comparações como segurança padrão para correspondência de cadeia de
caracteres independente de cultura.
Use as comparações com StringComparison.Ordinal ou
StringComparison.OrdinalIgnoreCase para melhorar o desempenho.
Usar operações de cadeia de caracteres com base em
StringComparison.CurrentCulture quando você exibir a saída para o usuário.
Use os valores não linguísticos StringComparison.Ordinal ou
StringComparison.OrdinalIgnoreCase em vez de operações de cadeia de caracteres
com base em CultureInfo.InvariantCulture quando a comparação for irrelevante
linguisticamente (simbólica, por exemplo).
Use o método String.ToUpperInvariant em vez do método String.ToLowerInvariant
ao normalizar cadeias de caracteres para comparação.
Use uma sobrecarga do método String.Equals para testar se duas cadeias de
caracteres são iguais.
Use os métodos String.Compare e String.CompareTo para classificar cadeias de
caracteres, não para verificar a igualdade.
Use a formatação que leva em conta a cultura para exibir dados que não são de
cadeias de caracteres, como números e datas, em uma interface do usuário. Use a
formatação com a cultura invariável para persistir os dados que não são de cadeias
de caracteres no formato de cadeia de caracteres.

Evite as práticas a seguir ao comparar cadeias de caracteres:

Não use sobrecargas que não especifiquem explicitamente ou implicitamente as


regras de comparação de cadeia de caracteres para operações de cadeia de
caracteres.
Não use operações de cadeia de caracteres com base na
StringComparison.InvariantCulture maioria dos casos. Uma das poucas exceções é
quando você mantém dados linguisticamente significativos, mas culturalmente
independentes.
Não use uma sobrecarga do String.Compare método ou CompareTo e teste um
valor retornado de zero para determinar se duas cadeias de caracteres são iguais.

Especificação explícita de comparações da


cadeia de caracteres
A maioria dos métodos de manipulação de cadeia de caracteres no .NET é
sobrecarregada. Normalmente, uma ou mais sobrecargas aceitam as configurações
padrão, enquanto outras não aceitam nenhum padrão e definem a maneira exata em
que as cadeias de caracteres devem ser comparadas ou manipuladas. A maioria dos
métodos que não dependem de padrões inclui um parâmetro do tipo
StringComparison, que é uma enumeração que especifica explicitamente regras para
comparação de cadeias de caracteres por cultura e caso. A tabela a seguir descreve os
membros de enumeração StringComparison.

Membro de Descrição
StringComparison
Membro de Descrição
StringComparison

CurrentCulture Executa uma comparação que diferencia maiúsculas de minúsculas


usando a cultura atual.

CurrentCultureIgnoreCase Executa uma comparação que não diferencia maiúsculas de


minúsculas usando a cultura atual.

InvariantCulture Executa uma comparação que diferencia maiúsculas de minúsculas


usando a cultura invariável.

InvariantCultureIgnoreCase Executa uma comparação que não diferencia maiúsculas de


minúsculas usando a cultura invariável.

Ordinal Executa uma comparação ordinal.

OrdinalIgnoreCase Executa uma comparação ordinal que não diferencia maiúsculas de


minúsculas.

Por exemplo, o método IndexOf, que retorna um índice de uma subcadeia de caracteres
em um objeto String que corresponde a um caractere ou cadeia de caracteres, tem nove
sobrecargas:

IndexOf(Char), IndexOf(Char, Int32)e IndexOf(Char, Int32, Int32), que, por padrão,


executa uma pesquisa ordinal (diferencia maiúsculas de minúsculas e sem
diferenciação de cultura) por um caractere na cadeia de caracteres.
IndexOf(String), IndexOf(String, Int32)e IndexOf(String, Int32, Int32), que, por
padrão, executa uma pesquisa que diferencia maiúsculas de minúsculas e
diferencia a cultura de uma subcadeia de caracteres na cadeia de caracteres.
IndexOf(String, StringComparison), IndexOf(String, Int32, StringComparison) e
IndexOf(String, Int32, Int32, StringComparison), que incluem um parâmetro de tipo
StringComparison que permite que o formato da comparação seja especificado.

Recomendamos que você selecione uma sobrecarga que não use valores padrão, pelos
seguintes motivos:

Algumas sobrecargas com parâmetros padrão (aqueles que pesquisam um Char na


instância de cadeia de caracteres) realizam uma comparação ordinal, enquanto
outras (aquelas que pesquisam uma cadeia de caracteres na instância de cadeia de
caracteres) levam em consideração a cultura. É difícil lembrar qual método usa
qual valor padrão e fácil confundir as sobrecargas.

A intenção do código que depende de valores padrão para chamadas de método


não está clara. No exemplo a seguir, que depende de padrões, é difícil saber se o
desenvolvedor realmente pretendia uma comparação ordinal ou linguística de
duas cadeias de caracteres ou se uma diferença de caso entre url.Scheme e "https"
pode fazer com que o teste de igualdade retorne false .

C#

Uri url = new("https://learn.microsoft.com/");

// Incorrect
if (string.Equals(url.Scheme, "https"))
{
// ...Code to handle HTTPS protocol.
}

Em geral, recomendamos que você chame um método que não depende de padrões,
pois ele torna a intenção do código inequívoca. Isso, por sua vez, torna o código mais
legível e fácil de depurar e manter. O exemplo a seguir aborda as questões levantadas
sobre o exemplo anterior. Ele deixa claro que a comparação ordinal é usada e que as
diferenças de maiúsculas e minúsculas são ignoradas.

C#

Uri url = new("https://learn.microsoft.com/");

// Correct
if (string.Equals(url.Scheme, "https", StringComparison.OrdinalIgnoreCase))
{
// ...Code to handle HTTPS protocol.
}

Os detalhes da comparação de cadeia de


caracteres
A comparação de cadeia de caracteres é o centro de muitas operações relacionadas à
cadeia de caracteres, especialmente a classificação e teste de igualdade. As cadeias de
caracteres são classificadas em uma determinada ordem: se “my” aparece antes de
“string” em uma lista classificada de cadeias de caracteres, “my” deve comparar menos
que ou igual a “string”. Além disso, a comparação define implicitamente a igualdade. A
operação de comparação retorna zero para cadeias de caracteres que considerar iguais.
Uma boa interpretação é que nenhuma cadeia de caracteres é menor que a outra.
Operações mais significativas envolvendo cadeias de caracteres incluem um ou ambos
destes procedimentos: comparação com outra cadeia de caracteres e execução de uma
operação de classificação bem definida.
7 Observação

Você pode baixar as Tabelas de peso de classificação , um conjunto de arquivos


de texto que contêm informações sobre os pesos de caracteres usados em
operações de classificação e comparação dos sistemas operacionais Windows, e a
Tabela de elemento de ordenação Unicode padrão , a versão mais recente da
tabela de peso de classificação para Linux e macOS. A versão específica da tabela
de peso de classificação do Linux e macOS depende da versão das bibliotecas de
Componentes internacionais para Unicode instaladas no sistema. Para obter
informações sobre versões de ICU e as versões Unicode que elas implementam,
veja Baixar ICU .

No entanto, avaliar duas cadeias de caracteres para igualdade ou ordem de classificação


não gera um único resultado correto; o resultado depende dos critérios usados para
comparar as cadeias de caracteres. Em particular, as comparações de cadeia de
caracteres ordinais ou baseadas no uso de maiúsculas e minúsculas e convenções
classificação da cultura atual ou da cultura invariável (uma cultura independente de
localidade com base no idioma inglês) podem gerar resultados diferentes.

Além disso, comparações de cadeia de caracteres usando versões diferentes do .NET ou


usando o .NET em diferentes sistemas operacionais ou versões do sistema operacional
podem retornar resultados diferentes. Para obter mais informações, veja Cadeias de
caracteres e o padrão Unicode.

Comparações de cadeia de caracteres que usam a cultura


atual
Um critério envolve o uso das convenções da cultura atual ao comparar cadeias de
caracteres. As comparações que se baseiam na cultura atual usam a localidade ou a
cultura atual do thread. Se a cultura não for definida pelo usuário, ela usará como
padrão a configuração do sistema operacional. Você deve sempre usar comparações
que se baseiam na cultura atual quando os dados forem linguisticamente relevantes e
quando eles refletirem a interação do usuário que leva em conta a cultura.

No entanto, o comportamento da comparação e do uso de maiúsculas e minúsculas no


.NET muda quando a cultura muda. Isso acontece quando um aplicativo é executado em
um computador que tem uma cultura diferente do computador em que o aplicativo foi
desenvolvido ou quando o thread em execução muda sua cultura. Esse comportamento
é intencional, mas permanece não óbvio para muitos desenvolvedores. O exemplo a
seguir ilustra diferenças na ordem de classificação entre as culturas de inglês dos EUA
("en-US") e sueco ("sv-SE"). Observe que as palavras "ångström", "Windows" e "Visual
Studio" aparecem em posições diferentes nas matrizes de cadeias de caracteres
classificadas.

C#

using System.Globalization;

// Words to sort
string[] values= { "able", "ångström", "apple", "Æble",
"Windows", "Visual Studio" };

// Current culture
Array.Sort(values);
DisplayArray(values);

// Change culture to Swedish (Sweden)


string originalCulture = CultureInfo.CurrentCulture.Name;
Thread.CurrentThread.CurrentCulture = new CultureInfo("sv-SE");
Array.Sort(values);
DisplayArray(values);

// Restore the original culture


Thread.CurrentThread.CurrentCulture = new CultureInfo(originalCulture);

static void DisplayArray(string[] values)


{
Console.WriteLine($"Sorting using the {CultureInfo.CurrentCulture.Name}
culture:");

foreach (string value in values)


Console.WriteLine($" {value}");

Console.WriteLine();
}

// The example displays the following output:


// Sorting using the en-US culture:
// able
// Æble
// ångström
// apple
// Visual Studio
// Windows
//
// Sorting using the sv-SE culture:
// able
// apple
// Visual Studio
// Windows
// ångström
// Æble
As comparações que não diferenciam maiúsculas de minúsculas que usam a cultura
atual são iguais às comparações que levam em conta a cultura, exceto que elas ignoram
as maiúsculas e minúsculas conforme determinado pela cultura atual do thread. Esse
comportamento pode se manifestar em ordens de classificação também.

As comparações que usam a semântica de cultura atual são o padrão para os seguintes
métodos:

String.Compare sobrecargas que não incluem um StringComparison parâmetro.


As sobrecargas de String.CompareTo.
O método padrão String.StartsWith(String) e o método String.StartsWith(String,
Boolean, CultureInfo) com um parâmetro null CultureInfo.
O método padrão String.EndsWith(String) e o método String.EndsWith(String,
Boolean, CultureInfo) com um parâmetro null CultureInfo.
String.IndexOf sobrecargas que aceitam um String como parâmetro de pesquisa e
que não têm um StringComparison parâmetro.
String.LastIndexOf sobrecargas que aceitam um String como parâmetro de
pesquisa e que não têm um StringComparison parâmetro.

Em qualquer caso, é recomendável que você chame uma sobrecarga que tenha um
parâmetro StringComparison para deixar a intenção da chamada do método clara.

Bugs sutis e não tão sutis podem surgir quando dados de cadeias de caracteres não
linguísticas são interpretados linguisticamente ou quando os dados da cadeia de
caracteres de uma cultura específica são interpretados usando as convenções de outra
cultura. O exemplo canônico é o problema do I turco.

Para quase todos os alfabetos latinos, incluindo o inglês dos EUA, o caractere "i"
(\u0069) é a versão minúscula do caractere "I" (\u0049). Essa regra de maiúsculas e
minúsculas rapidamente se torna o padrão para alguém programando em tal cultura.
No entanto, o alfabeto turco ("tr-TR") inclui um caractere "I com um ponto", "İ" (\u0130),
que é a versão maiúscula de "i". O turco também inclui um caractere minúsculo "i sem
um ponto", "ı" (\u0131), que em maiúscula é “I”. Esse comportamento também ocorre
na cultura azerbaijana ("az").

Portanto, as suposições feitas sobre a maiúscula "i" ou a letra minúscula "I" não são
válidas entre todas as culturas. Se você usar as sobrecargas padrão para rotinas de
comparação de cadeias de caracteres, elas estarão sujeitas à variação entre culturas. Se
os dados a serem comparados não forem linguísticos, o uso das sobrecargas padrão
poderá produzir resultados indesejáveis, como ilustra a tentativa a seguir de executar
uma comparação que não diferencia maiúsculas de minúsculas das cadeias de
caracteres "bill" e "BILL".
C#

using System.Globalization;

string name = "Bill";

Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");


Console.WriteLine($"Culture =
{Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL",
StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'?
{name.StartsWith("BILL", true, null)}");
Console.WriteLine();

Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");


Console.WriteLine($"Culture =
{Thread.CurrentThread.CurrentCulture.DisplayName}");
Console.WriteLine($" Is 'Bill' the same as 'BILL'? {name.Equals("BILL",
StringComparison.OrdinalIgnoreCase)}");
Console.WriteLine($" Does 'Bill' start with 'BILL'?
{name.StartsWith("BILL", true, null)}");

//' The example displays the following output:


//'
//' Culture = English (United States)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? True
//'
//' Culture = Turkish (Türkiye)
//' Is 'Bill' the same as 'BILL'? True
//' Does 'Bill' start with 'BILL'? False

Essa comparação pode causar problemas significativos se a cultura for usada


inadvertidamente nas configurações sensíveis à segurança, como no exemplo a seguir.
Uma chamada de método como IsFileURI("file:") retorna true se a cultura atual for
o inglês dos EUA, mas false se a cultura atual for o turco. Assim, em sistemas turcos,
alguém poderia driblar as medidas de segurança que bloqueiam o acesso a URIs que
não diferenciam maiúsculas de minúsculas que começam com “FILE:”.

C#

public static bool IsFileURI(string path) =>


path.StartsWith("FILE:", true, null);

Nesse caso, como "file:" deve ser interpretado como um identificador não linguístico
que não leva em conta a cultura, o código deveria ser escrito como mostrado no
exemplo a seguir:
C#

public static bool IsFileURI(string path) =>


path.StartsWith("FILE:", StringComparison.OrdinalIgnoreCase);

Operações ordinais da cadeia de caracteres


Especificar o valor StringComparison.Ordinal ou StringComparison.OrdinalIgnoreCase
em uma chamada de método significa uma comparação não linguística em que os
recursos de linguagens naturais são ignorados. Os métodos que são invocados com
esses valores de StringComparison baseiam as decisões de operação da cadeia de
caracteres em comparações de byte simples em vez de no uso de maiúsculas e
minúsculas ou tabelas de equivalência que são parametrizadas pela cultura. Na maioria
dos casos, essa abordagem se adapta melhor à interpretação pretendida de cadeias de
caracteres, enquanto torna o código mais rápido e confiável.

Comparações ordinais são comparações de cadeia de caracteres nas quais cada byte de
cada cadeia de caracteres é comparado sem interpretação linguística; por exemplo,
"windows" não corresponde a "Windows". Isso é basicamente uma chamada para a
função strcmp de runtime de C. Use essa comparação quando o contexto determinar
que as cadeias de caracteres devem corresponder exatamente ou exigir uma política de
correspondência conservadora. Além disso, a comparação ordinal é a operação de
comparação mais rápida porque ela não aplica nenhuma regra linguística ao determinar
um resultado.

As cadeias de caracteres no .NET podem conter caracteres nulos inseridos. Uma das
diferenças mais clara entre a comparação ordinal e a que leva em conta a cultura
(incluindo comparações que usam a cultura invariável) diz respeito à manipulação de
caracteres nulos inseridos em uma cadeia de caracteres. Esses caracteres são ignorados
quando você usa os métodos String.Compare e String.Equals para realizar comparações
que levam em conta a cultura (incluindo comparações que usam a cultura invariável).
Como resultado, em comparações sensíveis à cultura, as cadeias de caracteres que
contêm caracteres nulos inseridos podem ser consideradas iguais às cadeias de
caracteres que não contêm.

) Importante

Embora os métodos de comparação de cadeia de caracteres ignorem caracteres


nulos inseridos, os métodos de pesquisa de cadeia de caracteres, como
String.Contains, String.EndsWith, String.IndexOf, String.LastIndexOf e
String.StartsWith, não o fazem.
O exemplo a seguir executa uma comparação que leva em conta a cultura da cadeia de
caracteres "Aa" com uma cadeia de caracteres semelhante que contém vários caracteres
nulos inseridos entre "A" e "a" e mostra como as duas cadeias de caracteres são
consideradas iguais:

C#

string str1 = "Aa";


string str2 = "A" + new string('\u0000', 3) + "a";

Thread.CurrentThread.CurrentCulture =
System.Globalization.CultureInfo.GetCultureInfo("en-us");

Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}'


({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Current Culture: {string.Compare(str1, str2,
StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Compare(str1, str2,
StringComparison.InvariantCulture)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Current Culture: {string.Equals(str1, str2,
StringComparison.CurrentCulture)}");
Console.WriteLine($" Invariant Culture: {string.Equals(str1, str2,
StringComparison.InvariantCulture)}");

string ShowBytes(string value)


{
string hexString = string.Empty;
for (int index = 0; index < value.Length; index++)
{
string result = Convert.ToInt32(value[index]).ToString("X4");
result = string.Concat(" ", result.Substring(0,2), " ",
result.Substring(2, 2));
hexString += result;
}
return hexString.Trim();
}

// The example displays the following output:


// Comparing 'Aa' (00 41 00 61) and 'Aa' (00 41 00 00 00 00 00 00 00
61):
// With String.Compare:
// Current Culture: 0
// Invariant Culture: 0
// With String.Equals:
// Current Culture: True
// Invariant Culture: True

No entanto, as cadeias de caracteres não são consideradas iguais quando você usa
comparação ordinal, como mostra o exemplo a seguir:
C#

string str1 = "Aa";


string str2 = "A" + new String('\u0000', 3) + "a";

Console.WriteLine($"Comparing '{str1}' ({ShowBytes(str1)}) and '{str2}'


({ShowBytes(str2)}):");
Console.WriteLine(" With String.Compare:");
Console.WriteLine($" Ordinal: {string.Compare(str1, str2,
StringComparison.Ordinal)}");
Console.WriteLine(" With String.Equals:");
Console.WriteLine($" Ordinal: {string.Equals(str1, str2,
StringComparison.Ordinal)}");

string ShowBytes(string str)


{
string hexString = string.Empty;
for (int ctr = 0; ctr < str.Length; ctr++)
{
string result = Convert.ToInt32(str[ctr]).ToString("X4");
result = " " + result.Substring(0, 2) + " " + result.Substring(2,
2);
hexString += result;
}
return hexString.Trim();
}

// The example displays the following output:


// Comparing 'Aa' (00 41 00 61) and 'A a' (00 41 00 00 00 00 00 00 00
61):
// With String.Compare:
// Ordinal: 97
// With String.Equals:
// Ordinal: False

As comparações ordinais que não diferenciam maiúsculas de minúsculas são a próxima


abordagem conservadora. Essas comparações ignoram a maioria das maiúsculas e
minúsculas, por exemplo, "windows" corresponde a "Windows". Ao lidar com caracteres
ASCII, essa política é equivalente a StringComparison.Ordinal, exceto que ela ignora as
maiúsculas e minúsculas de ASCII normais. Portanto, qualquer caractere em [A, Z]
(\u0041-\u005A) corresponde ao caractere correspondente em [a,z] (\u0061-\007A). As
maiúsculas e minúsculas fora do intervalo de ASCII usam as tabelas de cultura invariável.
Portanto, a comparação a seguir:

C#

string.Compare(strA, strB, StringComparison.OrdinalIgnoreCase);

é equivalente a (mas mais rápida do que) esta comparação:


C#

string.Compare(strA.ToUpperInvariant(), strB.ToUpperInvariant(),
StringComparison.Ordinal);

Essas comparações ainda são muito rápidas.

StringComparison.Ordinal e StringComparison.OrdinalIgnoreCase usam os valores


binários diretamente e são mais adequados para correspondência. Quando você não
tiver certeza sobre suas configurações de comparação, use um desses dois valores. No
entanto, como eles executam uma comparação byte por byte, eles não classificam por
uma ordem de classificação linguística (como um dicionário de inglês), mas por uma
ordem de classificação binária. Os resultados podem parecer estranhos na maioria dos
contextos se exibido aos usuários.

A semântica ordinal é o padrão para String.Equals sobrecargas que não incluem um


StringComparison argumento (incluindo o operador de igualdade). Em qualquer caso, é
recomendável que você chame uma sobrecarga que tenha um parâmetro
StringComparison.

Operações da cadeia de caracteres que usam a cultura


invariável
As comparações com a cultura invariável usam a propriedade CompareInfo retornada
pela propriedade estática CultureInfo.InvariantCulture. Esse comportamento é o mesmo
em todos os sistemas, ele converte qualquer caractere fora de seu intervalo no que ele
acredita que sejam caracteres invariáveis equivalentes. Essa política pode ser útil para
manter um conjunto de comportamentos de cadeia de caracteres entre culturas, mas
geralmente fornece resultados inesperados.

As comparações que não diferenciam maiúsculas de minúsculas com a cultura invariável


usam a propriedade CompareInfo estática retornada pela propriedade
CultureInfo.InvariantCulture estática para informações de comparação também. As
diferenças de maiúsculas e minúsculas entre esses caracteres convertidos são ignoradas.

As comparações que usam StringComparison.InvariantCulture e


StringComparison.Ordinal funcionam de forma idêntica em cadeias de caracteres ASCII.
No entanto, StringComparison.InvariantCulture toma decisões linguísticas que podem
não ser apropriadas para cadeias de caracteres que devem ser interpretadas como um
conjunto de bytes. O objeto CultureInfo.InvariantCulture.CompareInfo faz o método
Compare interpretar determinados conjuntos de caracteres como equivalentes. Por
exemplo, a equivalência a seguir é válida na cultura invariável:
InvariantCulture: a + ̊ = å

A LETRA PEQUENA LATINA Um caractere "a" (\u0061), quando está ao lado do caractere
COMBINING RING ABOVE "+ " ̊" (\u030a), é interpretado como a LETRA LATINA
PEQUENA A COM ANEL ACIMA caractere "å" (\u00e5). Como mostra o exemplo a
seguir, esse comportamento é diferente da comparação ordinal.

C#

string separated = "\u0061\u030a";


string combined = "\u00e5";

Console.WriteLine("Equal sort weight of {0} and {1} using InvariantCulture:


{2}",
separated, combined,
string.Compare(separated, combined,
StringComparison.InvariantCulture) == 0);

Console.WriteLine("Equal sort weight of {0} and {1} using Ordinal: {2}",


separated, combined,
string.Compare(separated, combined,
StringComparison.Ordinal) == 0);

// The example displays the following output:


// Equal sort weight of a° and å using InvariantCulture: True
// Equal sort weight of a° and å using Ordinal: False

Ao interpretar nomes de arquivo, cookies ou qualquer outro elemento em que uma


combinação como "å" pode aparecer, as comparações ordinais ainda oferecem o
comportamento mais transparente e adequado.

Em equilíbrio, a cultura invariável tem poucas propriedades que a tornam útil para
comparação. Ele faz a comparação de maneira linguisticamente relevante, o que o
impede de garantir a equivalência simbólica total, mas não é a escolha para exibição em
nenhuma cultura. Um dos motivos para usar StringComparison.InvariantCulture para
comparação é persistir dados ordenados, para uma exibição idêntica entre culturas. Por
exemplo, se um arquivo de dados grande que contém uma lista de identificadores
classificados para exibição acompanha um aplicativo, a adição a essa lista exige uma
inserção com a inserção de estilo invariável.

Escolha de um membro StringComparison para


a chamada de método
A tabela a seguir descreve o mapeamento do contexto semântico da cadeia de
caracteres para um membro de enumeração StringComparison:
Dados Comportamento System.StringComparison
correspondente

value

Identificadores internos que Um identificador não Ordinal


diferenciam maiúsculas de linguístico, em que bytes
minúsculas. correspondem exatamente.

Identificadores que diferenciam


maiúsculas e minúsculas nos
padrões como XML e HTTP.

Configurações relacionadas à
segurança que diferenciam
maiúsculas de minúsculas.

Identificadores internos que não Um identificador não OrdinalIgnoreCase


diferenciam maiúsculas de linguístico, em que as
minúsculas. maiúsculas e minúsculas são
irrelevantes.
Identificadores que não
diferenciam maiúsculas e
minúsculas em padrões como
XML e HTTP.

Caminhos de arquivo.

Chaves do Registro e valores.

Variáveis de ambiente.

Identificadores de recurso (por


exemplo, nomes de
identificador).

Configurações relacionadas à
segurança que não diferenciam
maiúsculas de minúsculas.

Alguns dados persistentes, Dados independentes de InvariantCulture


linguisticamente relevantes. cultura que ainda são
linguisticamente relevantes. -ou-
Exibição de dados linguísticos
que requer uma ordem de InvariantCultureIgnoreCase
classificação fixa.
Dados Comportamento System.StringComparison
correspondente

value

Dados exibidos para o usuário. Dados que exigem os CurrentCulture


costumes linguísticos locais.
A maioria das entradas do -ou-
usuário.
CurrentCultureIgnoreCase

Métodos comuns de comparação da cadeia de


caracteres no .NET
As seções a seguir descrevem os métodos que são mais comumente usados para a
comparação de cadeias de caracteres.

String.Compare
Interpretação padrão: StringComparison.CurrentCulture.

Como a operação mais central da interpretação de cadeia de caracteres, todas as


instâncias de chamadas desse método devem ser examinadas para determinar se as
cadeias de caracteres devem ser interpretadas de acordo com a cultura atual ou
dissociadas da cultura (simbolicamente). Normalmente, é o último e uma
StringComparison.Ordinal comparação deve ser usada.

A classe System.Globalization.CompareInfo, que é retornada pelo


CultureInfo.CompareInfo também inclui uma propriedade, um método Compare que
fornece um grande número de opções correspondentes (ordinal, ignorando espaço em
branco, ignorando tipo kana e assim por diante) por meio da enumeração de sinalizador
CompareOptions.

String.CompareTo
Interpretação padrão: StringComparison.CurrentCulture.

Atualmente, esse método não oferece uma sobrecarga que especifica um


StringComparison tipo. Geralmente, é possível converter esse método no formulário
recomendado String.Compare(String, String, StringComparison) .
Os tipos que implementam as interfaces IComparable e IComparable<T> implementam
este método. Como ele não oferece a opção de um StringComparison parâmetro,
implementar tipos geralmente permite que o usuário especifique um StringComparer
em seu construtor. O exemplo a seguir define uma classe FileName cujo construtor de
classe inclui um parâmetro StringComparer. Esse objeto StringComparer é usado no
método FileName.CompareTo .

C#

class FileName : IComparable


{
private readonly StringComparer _comparer;

public string Name { get; }

public FileName(string name, StringComparer? comparer)


{
if (string.IsNullOrEmpty(name)) throw new
ArgumentNullException(nameof(name));

Name = name;

if (comparer != null)
_comparer = comparer;
else
_comparer = StringComparer.OrdinalIgnoreCase;
}

public int CompareTo(object? obj)


{
if (obj == null) return 1;

if (obj is not FileName)


return _comparer.Compare(Name, obj.ToString());
else
return _comparer.Compare(Name, ((FileName)obj).Name);
}
}

String.Equals
Interpretação padrão: StringComparison.Ordinal.

A classe String permite que você teste a igualdade chamando as sobrecargas do


método Equals ou estáticas ou usando o operador de igualdade estático. O operador e
as sobrecargas utilizam a comparação ordinal por padrão. No entanto, ainda é
recomendável que você chame uma sobrecarga que especifique explicitamente o tipo
StringComparison mesmo se você desejar executar uma comparação ordinal. Isso
facilita a pesquisa de código para uma determinada interpretação da cadeia de
caracteres.

String.ToUpper e String.ToLower
Interpretação padrão: StringComparison.CurrentCulture.

Tenha cuidado ao usar os String.ToUpper() métodos e String.ToLower() , pois forçar uma


cadeia de caracteres em letras maiúsculas ou minúsculas geralmente é usado como uma
pequena normalização para comparar cadeias de caracteres, independentemente do
caso. Nesse caso, considere o uso de uma comparação que não diferencie maiúsculas
de minúsculas.

Os métodos String.ToUpperInvariant e String.ToLowerInvariant também estão


disponíveis. ToUpperInvariant é o modo padrão para normalizar maiúsculas e
minúsculas. As comparações feitas usando StringComparison.OrdinalIgnoreCase são a
composição de duas chamadas de maneira comportamental: chamar ToUpperInvariant
em ambos os argumentos de cadeia de caracteres e fazer uma comparação usando
StringComparison.Ordinal.

Também há sobrecargas disponíveis para converter para maiúsculas e minúsculas em


uma cultura específica, passando um objeto CultureInfo que representa aquela cultura
para o método.

Char.ToUpper e Char.ToLower
Interpretação padrão: StringComparison.CurrentCulture.

Os métodos Char.ToUpper(Char) e Char.ToLower(Char) funcionam da mesma forma que


os métodos String.ToUpper() e String.ToLower() descritos na seção anterior.

String.StartsWith e String.EndsWith
Interpretação padrão: StringComparison.CurrentCulture.

Por padrão, esses dois métodos executam uma comparação que leva em conta a
cultura.

String.IndexOf e String.LastIndexOf
Interpretação padrão: StringComparison.CurrentCulture.
Há uma falta de consistência em como as sobrecargas padrão desses métodos
executam comparações. Todos os métodos String.IndexOf e String.LastIndexOf que
incluem um parâmetro Char realizam uma comparação ordinal, mas os métodos padrão
String.IndexOf e String.LastIndexOf que incluem um parâmetro String executam uma
comparação que diferencia a cultura.

Se você chamar o método String.IndexOf(String) ou String.LastIndexOf(String) e passar a


ele uma cadeia de caracteres para localizar na instância atual, é recomendável que você
chame uma sobrecarga que especifique explicitamente o tipo StringComparison. As
sobrecargas que incluem um Char argumento não permitem que você especifique um
StringComparison tipo.

Métodos que realizam comparação indireta de


cadeia de caracteres
Alguns métodos que não de cadeias de caracteres que têm a comparação de cadeia de
caracteres como uma operação central usam o tipo StringComparer. A classe
StringComparer inclui quatro propriedades estáticas que retornam instâncias
StringComparer cujos métodos StringComparer.Compare realizam os seguintes tipos de
comparações de cadeias de caracteres:

Comparações de cadeias de caracteres que levam em conta a cultura usando a


cultura atual. Este objeto StringComparer é retornado pela propriedade
StringComparer.CurrentCulture.
Comparações que não diferenciam maiúsculas de minúsculas usando a cultura
atual. Este objeto StringComparer é retornado pela propriedade
StringComparer.CurrentCultureIgnoreCase.
Comparações sem diferenciação de cultura usando as regras de comparação de
palavras da cultura invariável. Este objeto StringComparer é retornado pela
propriedade StringComparer.InvariantCulture.
Comparações que não diferenciam maiúsculas e minúsculas e a cultura usando as
regras de comparação de palavras da cultura invariável. Este objeto
StringComparer é retornado pela propriedade
StringComparer.InvariantCultureIgnoreCase.
Comparação ordinal. Este objeto StringComparer é retornado pela propriedade
StringComparer.Ordinal.
Comparação ordinal que não diferencia maiúsculas de minúsculas. Este objeto
StringComparer é retornado pela propriedade StringComparer.OrdinalIgnoreCase.

Array.Sort e Array.BinarySearch
Interpretação padrão: StringComparison.CurrentCulture.

Ao armazenar quaisquer dados em uma coleção ou ler dados persistentes de um


arquivo ou banco de dados em uma coleção, alternar a cultura atual pode invalidar as
invariáveis na coleção. O método Array.BinarySearch presume que os elementos na
matriz a serem pesquisados já estão classificados. Para classificar qualquer elemento de
cadeia de caracteres na matriz, o método Array.Sort chama o método String.Compare
para ordenar os elementos individuais. Usar um comparador que leva em conta a
cultura pode ser perigoso se a cultura mudar entre o momento em que a matriz é
ordenada e que seu conteúdo é pesquisado. Por exemplo, no código a seguir, o
armazenamento e a recuperação operam no comparador que é fornecido
implicitamente pela propriedade Thread.CurrentThread.CurrentCulture . Se a cultura
puder mudar entre as chamadas para StoreNames e DoesNameExist , e especialmente se
os conteúdos da matriz forem mantidos em algum lugar entre as duas chamadas de
método, a pesquisa binária poderá falhar.

C#

// Incorrect
string[] _storedNames;

public void StoreNames(string[] names)


{
_storedNames = new string[names.Length];

// Copy the array contents into a new array


Array.Copy(names, _storedNames, names.Length);

Array.Sort(_storedNames); // Line A
}

public bool DoesNameExist(string name) =>


Array.BinarySearch(_storedNames, name) >= 0; // Line B

Uma variação recomendada é exibida no exemplo a seguir, que usa o mesmo método
de comparação (que não leva em conta a cultura) ordinal para classificar e pesquisar a
matriz. O código de alteração é refletido nas linhas rotuladas como Line A e Line B
nos dois exemplos.

C#

// Correct
string[] _storedNames;

public void StoreNames(string[] names)


{
_storedNames = new string[names.Length];
// Copy the array contents into a new array
Array.Copy(names, _storedNames, names.Length);

Array.Sort(_storedNames, StringComparer.Ordinal); // Line A


}

public bool DoesNameExist(string name) =>


Array.BinarySearch(_storedNames, name, StringComparer.Ordinal) >= 0; //
Line B

Se esses dados forem mantidos e movidos entre as culturas e a classificação for usada
para apresentar esses dados para o usuário, você pode considerar usar
StringComparison.InvariantCulture, que opera linguisticamente para a melhor saída do
usuário, mas não é afetado pelas alterações na cultura. O exemplo a seguir modifica os
dois exemplos anteriores para usar a cultura invariável para classificar e pesquisar a
matriz.

C#

// Correct
string[] _storedNames;

public void StoreNames(string[] names)


{
_storedNames = new string[names.Length];

// Copy the array contents into a new array


Array.Copy(names, _storedNames, names.Length);

Array.Sort(_storedNames, StringComparer.InvariantCulture); // Line A


}

public bool DoesNameExist(string name) =>


Array.BinarySearch(_storedNames, name, StringComparer.InvariantCulture)
>= 0; // Line B

Exemplo de Coleções: Construtor da Tabela de Hash


As cadeias de caracteres de hash fornecem um segundo exemplo de uma operação que
é afetada pela maneira como as cadeias de caracteres são comparadas.

O exemplo a seguir cria um objeto Hashtable passando-o para o objeto StringComparer


que é retornado pela propriedade StringComparer.OrdinalIgnoreCase. Como uma classe
StringComparer que é derivada de StringComparer implementa a interface
IEqualityComparer, seu método GetHashCode é usado para calcular o código hash de
cadeias de caracteres na tabela de hash.
C#

using System.IO;
using System.Collections;

const int InitialCapacity = 100;

Hashtable creationTimeByFile = new(InitialCapacity,


StringComparer.OrdinalIgnoreCase);
string directoryToProcess = Directory.GetCurrentDirectory();

// Fill the hash table


PopulateFileTable(directoryToProcess);

// Get some of the files and try to find them with upper cased names
foreach (var file in Directory.GetFiles(directoryToProcess))
PrintCreationTime(file.ToUpper());

void PopulateFileTable(string directory)


{
foreach (string file in Directory.GetFiles(directory))
creationTimeByFile.Add(file, File.GetCreationTime(file));
}

void PrintCreationTime(string targetFile)


{
object? dt = creationTimeByFile[targetFile];

if (dt is DateTime value)


Console.WriteLine($"File {targetFile} was created at time
{value}.");
else
Console.WriteLine($"File {targetFile} does not exist.");
}

Confira também
Globalização em aplicativos .NET
Práticas recomendadas para exibir e
persistir dados formatados
Artigo • 24/07/2023

Este artigo examina como dados formatados, como dados numéricos e dados de data e
hora, são manipulados para exibição e armazenamento.

Ao desenvolver com .NET, use a formatação que leva em conta a cultura para exibir
dados que não são de cadeias de caracteres, como números e datas, em uma interface
do usuário. Use a formatação com a cultura invariável para persistir os dados que não
são de cadeias de caracteres no formato de cadeia de caracteres. Não use a formatação
que leva em conta a cultura para persistir dados numéricos ou dados de data e hora no
formato de cadeia de caracteres.

Exibir dados formatados


Quando você exibir dados que não são de cadeias de caracteres como números e datas
e horas para os usuários, formate-os usando as configurações culturais do usuário. Por
padrão, todos os seguintes itens usam a cultura atual em operações de formatação:

Cadeias de caracteres interpoladas compatíveis com os compiladores C# e Visual


Basic.
Operações de concatenação de cadeias de caracteres que usam os operadores de
concatenação do C# ou Visual Basic, ou que chamam o método String.Concat
diretamente.
O método String.Format.
Os métodos ToString dos tipos numéricos e dos tipos de data e hora.

Para especificar explicitamente que uma cadeia de caracteres deve ser formatada
usando as convenções de uma cultura específica ou a cultura invariável, você pode fazer
o seguinte:

Ao usar os métodos String.Format e ToString , chame uma sobrecarga que tenha


um parâmetro provider , tal como String.Format(IFormatProvider, String, Object[])
ou DateTime.ToString(IFormatProvider), e passe a ela a propriedade
CultureInfo.CurrentCulture, a propriedade CultureInfo.InvariantCulture ou uma
instância de CultureInfo que represente a cultura desejada.

Para concatenação de cadeias de caracteres, não permita que o compilador


execute nenhuma conversão implícita. Em vez disso, execute uma conversão
explícita, chamando uma sobrecarga ToString que tenha um parâmetro provider .
Por exemplo, o compilador usa implicitamente a cultura atual ao converter um
valor Double em uma cadeia de caracteres no seguinte código C#:

C#

string concat1 = "The amount is " + 126.03 + ".";


Console.WriteLine(concat1);

Em vez disso, é possível especificar explicitamente a cultura cujas convenções de


formatação são usadas na conversão ao chamar o método
Double.ToString(IFormatProvider), assim como ocorre com o seguinte código:

C#

string concat2 = "The amount is " +


126.03.ToString(CultureInfo.InvariantCulture) + ".";
Console.WriteLine(concat2);

Para a interpolação de cadeia de caracteres, em vez de atribuir uma cadeia de


caracteres interpolada a uma instância de String, atribua-a a um FormattableString.
Em seguida, você pode chamar seu método FormattableString.ToString() para
produzir uma cadeia de caracteres de resultados que reflita as convenções da
cultura atual ou pode chamar o método
FormattableString.ToString(IFormatProvider) para produzir uma cadeia de
caracteres de resultados que reflita as convenções de uma cultura específica.

Você também pode passar a cadeia de caracteres formatável para o método


FormattableString.Invariant estático para produzir uma cadeia de caracteres de
resultado que reflete as convenções da cultura invariável. O exemplo a seguir
ilustra esta abordagem. (A saída do exemplo reflete uma cultura atual de en-US .)

C#

using System;
using System.Globalization;

class Program
{
static void Main()
{
Decimal value = 126.03m;
FormattableString amount = $"The amount is {value:C}";
Console.WriteLine(amount.ToString());
Console.WriteLine(amount.ToString(new CultureInfo("fr-FR")));
Console.WriteLine(FormattableString.Invariant(amount));
}
}
// The example displays the following output:
// The amount is $126.03
// The amount is 126,03 €
// The amount is ¤126.03

7 Observação

Se você estiver usando C# e formatando usando a cultura invariante, é mais


eficiente chamar String.Create(IFormatProvider,
DefaultInterpolatedStringHandler) e passar CultureInfo.InvariantCulture
para o primeiro parâmetro. Para obter mais informações, confira Interpolação
de cadeias de caracteres em C# 10 e .NET 6 .

Manter dados formatados


Você pode manter os dados que não são de cadeias de caracteres como dados binários
ou como dados formatados. Se optar por salvá-los como dados formatados, você
deverá chamar uma sobrecarga de método de formatação que inclua um parâmetro
provider e passar para ele a propriedade CultureInfo.InvariantCulture. A cultura

invariável fornece um formato consistente para os dados formatados que é


independente da cultura e do computador. Em contraste, dados persistentes que são
formatados usando culturas diferentes da cultura invariável têm várias limitações:

É provável que os dados não sejam utilizáveis se forem recuperados em um


sistema que tem uma cultura diferente ou se o usuário do sistema atual alterar a
cultura atual e tentar recuperar os dados.
As propriedades de uma cultura em um computador específico podem ser
diferentes dos valores padrão. A qualquer momento, um usuário pode personalizar
as configurações de exibição que levam em conta a cultura. Devido a isso, os
dados formatados que são salvos em um sistema podem não ser legíveis após o
usuário personalizar as configurações culturais. É provável que a portabilidade dos
dados formatados entre computadores seja ainda mais limitada.
Os padrões internacionais, regionais ou nacionais que controlam a formatação de
números ou datas e horas são alterados ao longo do tempo e essas alterações são
incorporadas nas atualizações do sistema operacional Windows. Quando as
convenções de formatação mudam, os dados que foram formatados usando as
convenções anteriores podem se tornar ilegíveis.
O exemplo a seguir ilustra a portabilidade limitada resultante do uso da formatação que
leva em conta a cultura para manter os dados. O exemplo salva uma matriz de valores
de data e hora em um arquivo. Eles são formatados usando as convenções da cultura do
inglês (Estados Unidos). Depois que o aplicativo altera a cultura atual para francês
(Suíça), ele tenta ler os valores salvos usando as convenções de formatação da cultura
atual. A tentativa de ler dois dos itens de dados gera uma exceção FormatException e a
matriz de datas agora contém dois elementos incorretos que são iguais a MinValue.

C#

using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Threading;

public class Example


{
private static string filename = @".\dates.dat";

public static void Main()


{
DateTime[] dates = { new DateTime(1758, 5, 6, 21, 26, 0),
new DateTime(1818, 5, 5, 7, 19, 0),
new DateTime(1870, 4, 22, 23, 54, 0),
new DateTime(1890, 9, 8, 6, 47, 0),
new DateTime(1905, 2, 18, 15, 12, 0) };
// Write the data to a file using the current culture.
WriteData(dates);
// Change the current culture.
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("fr-CH");
// Read the data using the current culture.
DateTime[] newDates = ReadData();
foreach (var newDate in newDates)
Console.WriteLine(newDate.ToString("g"));
}

private static void WriteData(DateTime[] dates)


{
StreamWriter sw = new StreamWriter(filename, false, Encoding.UTF8);
for (int ctr = 0; ctr < dates.Length; ctr++) {
sw.Write("{0}", dates[ctr].ToString("g",
CultureInfo.CurrentCulture));
if (ctr < dates.Length - 1) sw.Write("|");
}
sw.Close();
}

private static DateTime[] ReadData()


{
bool exceptionOccurred = false;
// Read file contents as a single string, then split it.
StreamReader sr = new StreamReader(filename, Encoding.UTF8);
string output = sr.ReadToEnd();
sr.Close();

string[] values = output.Split( new char[] { '|' } );


DateTime[] newDates = new DateTime[values.Length];
for (int ctr = 0; ctr < values.Length; ctr++) {
try {
newDates[ctr] = DateTime.Parse(values[ctr],
CultureInfo.CurrentCulture);
}
catch (FormatException) {
Console.WriteLine("Failed to parse {0}", values[ctr]);
exceptionOccurred = true;
}
}
if (exceptionOccurred) Console.WriteLine();
return newDates;
}
}
// The example displays the following output:
// Failed to parse 4/22/1870 11:54 PM
// Failed to parse 2/18/1905 3:12 PM
//
// 05.06.1758 21:26
// 05.05.1818 07:19
// 01.01.0001 00:00
// 09.08.1890 06:47
// 01.01.0001 00:00
// 01.01.0001 00:00

No entanto, se você substituir a propriedade CultureInfo.CurrentCulture por


CultureInfo.InvariantCulture em chamadas para DateTime.ToString(String,
IFormatProvider) e DateTime.Parse(String, IFormatProvider), os dados persistentes de
data e hora serão restaurados com êxito, como na seguinte saída:

Console

06.05.1758 21:26
05.05.1818 07:19
22.04.1870 23:54
08.09.1890 06:47
18.02.1905 15:12
Mudanças de comportamento ao
comparar strings no .NET 5+
Artigo • 27/04/2023

O .NET 5 apresenta uma alteração comportamental em runtime em que as APIs de


globalização usam a ICU por padrão em todas as plataformas com suporte. Essa é uma
saída das versões anteriores do .NET Core e do .NET Framework, que utilizam a
funcionalidade nls (suporte à linguagem nacional) do sistema operacional ao ser
executado no Windows. Para obter mais informações sobre essas alterações, incluindo
comutadores de compatibilidade que podem reverter a alteração de comportamento,
consulte a globalização e ICU do .NET.

Motivo da alteração
Essa alteração foi introduzida para unificar o comportamento de globalização do .NET
em todos os sistemas operacionais com suporte. Ele também fornece a capacidade dos
aplicativos de agrupar suas próprias bibliotecas de globalização em vez de depender
das bibliotecas internas do sistema operacional. Para obter mais informações, confira a
notificação de alteração interruptiva.

Diferenças de comportamento
Se você usar funções como string.IndexOf(string) sem chamar a sobrecarga que usa
um argumento StringComparison, talvez você pretenda executar uma pesquisa ordinal,
mas, em vez disso, usa inadvertidamente uma dependência do comportamento
específico da cultura. Como o NLS e a ICU implementam uma lógica diferente em seus
comparadores linguísticos, os resultados de métodos como string.IndexOf(string)
podem retornar valores inesperados.

Isso pode se manifestar mesmo em locais onde você nem sempre espera que as
instalações de globalização estejam ativas. Por exemplo, o código a seguir pode
produzir uma resposta diferente dependendo do runtime atual.

C#

const string greeting = "Hel\0lo";


Console.WriteLine($"{greeting.IndexOf("\0")}");

// The snippet prints:


//
// '3' when running on .NET Core 2.x - 3.x (Windows)
// '0' when running on .NET 5 or later (Windows)
// '0' when running on .NET Core 2.x - 3.x or .NET 5 (non-Windows)
// '3' when running on .NET Core 2.x or .NET 5+ (in invariant mode)

string s = "Hello\r\nworld!";
int idx = s.IndexOf("\n");
Console.WriteLine(idx);

// The snippet prints:


//
// '6' when running on .NET Core 3.1
// '-1' when running on .NET 5 or .NET Core 3.1 (non-Windows OS)
// '-1' when running on .NET 5 (Windows 10 May 2019 Update or later)
// '6' when running on .NET 6+ (all Windows and non-Windows OSs)

Para obter mais informações, consulte As APIs de globalização usam bibliotecas de ICU
no Windows.

Proteger contra comportamento inesperado


Esta seção fornece duas opções para lidar com alterações de comportamento
inesperadas no .NET 5.

Habilitar analisadores de código


Os analisadores de código podem detectar sites de chamadas com possíveis bugs. Para
ajudar a proteger contra quaisquer comportamentos surpreendentes, recomendamos
habilitar analisadores da plataforma de compilador .NET (Roslyn) em seu projeto. Os
analisadores ajudam a sinalizar o código que pode estar usando inadvertidamente um
comparador linguístico quando um comparador ordinal provavelmente foi pretendido.
As regras a seguir devem ajudar a sinalizar esses problemas:

CA1307: Especificar StringComparison para garantir a clareza


CA1309: Usar StringComparison ordinal
CA1310: Especificar StringComparison para garantir a exatidão

Essas regras específicas não são habilitadas por padrão. Para habilitá-las e mostrar
quaisquer violações como erros de build, defina as seguintes propriedades em seu
arquivo de projeto:

XML

<PropertyGroup>
<AnalysisMode>All</AnalysisMode>
<WarningsAsErrors>$(WarningsAsErrors);CA1307;CA1309;CA1310</WarningsAsErrors
>
</PropertyGroup>

O snippet a seguir mostra exemplos de código que produzem os avisos ou erros


relevantes do analisador de código.

C#

//
// Potentially incorrect code - answer might vary based on locale.
//
string s = GetString();
// Produces analyzer warning CA1310 for string; CA1307 matches on char ','
int idx = s.IndexOf(",");
Console.WriteLine(idx);

//
// Corrected code - matches the literal substring ",".
//
string s = GetString();
int idx = s.IndexOf(",", StringComparison.Ordinal);
Console.WriteLine(idx);

//
// Corrected code (alternative) - searches for the literal ',' character.
//
string s = GetString();
int idx = s.IndexOf(',');
Console.WriteLine(idx);

Da mesma forma, ao instanciar uma coleção classificada de cadeias de caracteres ou


classificar uma coleção baseada em cadeia de caracteres existente, especifique um
comparador explícito.

C#

//
// Potentially incorrect code - behavior might vary based on locale.
//
SortedSet<string> mySet = new SortedSet<string>();
List<string> list = GetListOfStrings();
list.Sort();

//
// Corrected code - uses ordinal sorting; doesn't vary by locale.
//
SortedSet<string> mySet = new SortedSet<string>(StringComparer.Ordinal);
List<string> list = GetListOfStrings();
list.Sort(StringComparer.Ordinal);
Reverter para comportamentos nls
Para reverter os aplicativos .NET 5+ de volta para comportamentos mais antigos do NLS
durante a execução no Windows, siga as etapas na Globalização de .NET e ICU. Essa
opção de compatibilidade em todo o aplicativo deve ser definida no nível do aplicativo.
Bibliotecas individuais não podem aceitar ou recusar esse comportamento.

 Dica

É altamente recomendável habilitar as regras de análise de código CA1307, CA1309


e CA1310 para ajudar a melhorar a higiene do código e descobrir quaisquer bugs
latentes existentes. Para obter mais informações, consulte Habilitar analisadores de
código.

APIs afetadas
A maioria dos aplicativos .NET não encontrará nenhum comportamento inesperado
devido às alterações no .NET 5. No entanto, devido ao número de APIs afetadas e à
importância dessas APIs para o ecossistema .NET mais amplo, você deve estar ciente do
potencial do .NET 5 para introduzir comportamentos indesejados ou expor bugs
latentes que já existem em seu aplicativo.

As APIs afetadas incluem:

System.String.Compare
System.String.EndsWith
System.String.IndexOf
System.String.StartsWith
System.String.ToLower
System.String.ToLowerInvariant
System.String.ToUpper
System.String.ToUpperInvariant
System.Globalization.TextInfo (a maioria dos membros)
System.Globalization.CompareInfo (a maioria dos membros)
System.Array.Sort (ao classificar matrizes de cadeias de caracteres)
System.Collections.Generic.List<T>.Sort() (quando os elementos da lista são
cadeias de caracteres)
System.Collections.Generic.SortedDictionary<TKey,TValue> (quando as chaves são
cadeias de caracteres)
System.Collections.Generic.SortedList<TKey,TValue> (quando as chaves são
cadeias de caracteres)
System.Collections.Generic.SortedSet<T> (quando o conjunto contém cadeias de
caracteres)

7 Observação

Esta não é uma lista completa de APIs afetadas.

Todas as APIs acima usam pesquisa e comparação de cadeias de caracteres linguísticas


usando a cultura atual do thread, por padrão. As diferenças entre pesquisa linguística e
ordinal e comparação são citadas na Pesquisa ordinal vs. linguística e comparação.

Como o ICU implementa comparações de cadeia de caracteres linguísticas de maneira


diferente do NLS, os aplicativos baseados no Windows que atualizam para o .NET 5 de
uma versão anterior do .NET Core ou .NET Framework e que chamam uma das APIs
afetadas podem perceber que as APIs começam a exibir comportamentos diferentes.

Exceções
Se uma API aceitar um parâmetro StringComparison ou CultureInfo explícito, esse
parâmetro substituirá o comportamento padrão da API.
Os membros System.String em que o primeiro parâmetro é do tipo char (por
exemplo, String.IndexOf(Char)) usam pesquisa ordinal, a menos que o chamador
passe um argumento explícito StringComparison que especifica
CurrentCulture[IgnoreCase] ou InvariantCulture[IgnoreCase] .

Para obter uma análise mais detalhada do comportamento padrão de cada API String,
consulte a seção Padrão de tipos de pesquisa e comparação.

Pesquisa e comparação ordinal vs. linguística


A pesquisa e comparação ordinal (também conhecida como não linguística) decompõe
uma cadeia de caracteres em seus elementos individuais char e executa uma pesquisa
ou comparação entre cada caractere. Por exemplo, as cadeias de caracteres "dog" e
"dog" são comparadas como igual em um comparador Ordinal , pois as duas cadeias
de caracteres consistem exatamente na mesma sequência de caracteres. No entanto,
"dog" e "Dog" são comparadas como diferentes em um comparador Ordinal , porque
elas não consistem exatamente na mesma sequência de caracteres. Ou seja, o ponto de
código U+0044 de 'D' maiúsculo ocorre antes do ponto de código U+0064 de 'd'
minúsculo, resultando na classificação de "Dog" antes de "dog" .

Um comparador OrdinalIgnoreCase ainda opera caractere por caractere, mas elimina


diferenças de maiúsculas e minúsculas durante a execução da operação. Em um
comparador OrdinalIgnoreCase , os pares de caracteres 'd' e 'D' são comparados
como iguais, assim como os pares de caracteres 'á' e 'Á' . Mas o caractere não
acentuado 'a' é comparado como diferente do caractere acentuado 'á' .

Alguns exemplos disso são fornecidos na tabela a seguir:

Cadeia de Cadeia de Comparação Comparação


caracteres 1 caracteres 2 Ordinal OrdinalIgnoreCase

"dog" "dog" equal equal

"dog" "Dog" diferente de equal

"resume" "résumé" diferente de diferente de

O Unicode também permite que as cadeias de caracteres tenham várias representações


diferentes na memória. Por exemplo, um e-agudo (é) pode ser representado de duas
maneiras possíveis:

Um único caractere literal 'é' (também escrito como '\u00E9' ).


Um caractere literal não acentuado 'e' seguido por um caractere modificador
'\u0301' de acento correspondente.

Isso significa que as quatro cadeias de caracteres a seguir são exibidas como "résumé" ,
mesmo que suas partes constituintes sejam diferentes. As cadeias de caracteres usam
uma combinação de caracteres literais 'é' ou caracteres literais não codificados 'e' ,
além do modificador '\u0301' de acento correspondente.

"r\u00E9sum\u00E9"
"r\u00E9sume\u0301"

"re\u0301sum\u00E9"
"re\u0301sume\u0301"

Em um comparador ordinal, nenhuma dessas cadeias de caracteres é comparada como


igual à outra. Isso ocorre porque todas elas contêm sequências de caracteres
subjacentes diferentes, embora quando são renderizadas para a tela, todas elas têm a
mesma aparência.
Ao executar uma operação string.IndexOf(..., StringComparison.Ordinal) , o runtime
procura uma correspondência de substring exata. Os resultados são os seguintes.

C#

Console.WriteLine("resume".IndexOf("e", StringComparison.Ordinal)); //
prints '1'
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("e",
StringComparison.Ordinal)); // prints '-1'
Console.WriteLine("r\u00E9sume\u0301".IndexOf("e",
StringComparison.Ordinal)); // prints '5'
Console.WriteLine("re\u0301sum\u00E9".IndexOf("e",
StringComparison.Ordinal)); // prints '1'
Console.WriteLine("re\u0301sume\u0301".IndexOf("e",
StringComparison.Ordinal)); // prints '1'
Console.WriteLine("resume".IndexOf("E",
StringComparison.OrdinalIgnoreCase)); // prints '1'
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("E",
StringComparison.OrdinalIgnoreCase)); // prints '-1'
Console.WriteLine("r\u00E9sume\u0301".IndexOf("E",
StringComparison.OrdinalIgnoreCase)); // prints '5'
Console.WriteLine("re\u0301sum\u00E9".IndexOf("E",
StringComparison.OrdinalIgnoreCase)); // prints '1'
Console.WriteLine("re\u0301sume\u0301".IndexOf("E",
StringComparison.OrdinalIgnoreCase)); // prints '1'

Rotinas de pesquisa e comparação ordinais nunca são afetadas pela configuração de


cultura do thread atual.

Rotinas de pesquisa linguística e comparação decompõem uma cadeia de caracteres em


elementos de ordenação e executam pesquisas ou comparações nesses elementos. Não
há necessariamente um mapeamento individual entre os caracteres de uma cadeia de
caracteres e seus elementos de ordenação constituintes. Por exemplo, uma cadeia de
caracteres de comprimento 2 pode consistir em apenas um único elemento de
ordenação. Quando duas cadeias de caracteres são comparadas de forma linguística, o
comparador verifica se os elementos de ordenação das duas cadeias de caracteres têm
o mesmo significado semântico, mesmo que os caracteres literais da cadeia de
caracteres sejam diferentes.

Considere novamente a cadeia de caracteres "résumé" e suas quatro representações


diferentes. A tabela a seguir mostra cada representação dividida em seus elementos de
ordenação.

String Como elementos de ordenação

"r\u00E9sum\u00E9" "r" + "\u00E9" + "s" + "u" + "m" + "\u00E9"


String Como elementos de ordenação

"r\u00E9sume\u0301" "r" + "\u00E9" + "s" + "u" + "m" + "e\u0301"

"re\u0301sum\u00E9" "r" + "e\u0301" + "s" + "u" + "m" + "\u00E9"

"re\u0301sume\u0301" "r" + "e\u0301" + "s" + "u" + "m" + "e\u0301"

Um elemento de ordenação corresponde vagamente ao que os leitores pensariam


como um único caractere ou cluster de caracteres. Ele é conceitualmente semelhante a
um cluster de grafema, mas abrange um conjunto um pouco maior.

Em um comparador linguístico, correspondências exatas não são necessárias. Em vez


disso, os elementos de ordenação são comparados com base em seu significado
semântico. Por exemplo, um comparador linguístico trata as substrings "\u00E9" e
"e\u0301" como iguais, pois ambas significam semanticamente "um e minúsculo com
um modificador de acento agudo". Isso permite que o método IndexOf corresponda à
substring "e\u0301" em uma cadeia de caracteres maior que contém a substring
semanticamente equivalente "\u00E9" , conforme mostrado no exemplo de código a
seguir.

C#

Console.WriteLine("r\u00E9sum\u00E9".IndexOf("e")); // prints '-1' (not


found)
Console.WriteLine("r\u00E9sum\u00E9".IndexOf("\u00E9")); // prints '1'
Console.WriteLine("\u00E9".IndexOf("e\u0301")); // prints '0'

Como consequência disso, duas cadeias de caracteres de comprimentos diferentes


podem ser comparadas como iguais se uma comparação linguística for usada. Os
chamadores devem ter cuidado para não usar a lógica de caso especial que lida com o
comprimento da cadeia de caracteres nesses cenários.

As rotinas de pesquisa e comparação com reconhecimento de cultura são uma forma


especial de rotinas de pesquisa e comparação linguísticas. Em um comparador com
reconhecimento de cultura, o conceito de um elemento de ordenação é estendido para
incluir informações específicas à cultura especificada.

Por exemplo, no alfabeto húngaro , quando os dois caracteres <dz> aparecem


consecutivos, eles são considerados a própria letra exclusiva distinta de <d> ou <z>.
Isso significa que quando <dz> é visto em uma cadeia de caracteres, um comparador
com reconhecimento de cultura húngaro o trata como um único elemento de
ordenação.
String Como elementos de Comentários
ordenação

"endz" "e" + "n" + "d" + "z" (usando um comparador linguístico padrão)

"endz" "e" + "n" + "dz" (usando um comparador húngaro com reconhecimento


de cultura)

Ao usar um comparador com reconhecimento de cultura húngaro, isso significa que a


cadeia de caracteres "endz" não termina com a substring "z" , já que <dz> e <z> são
considerados elementos de ordenação com significado semântico diferente.

C#

// Set thread culture to Hungarian


CultureInfo.CurrentCulture = CultureInfo.GetCultureInfo("hu-HU");
Console.WriteLine("endz".EndsWith("z")); // Prints 'False'

// Set thread culture to invariant culture


CultureInfo.CurrentCulture = CultureInfo.InvariantCulture;
Console.WriteLine("endz".EndsWith("z")); // Prints 'True'

7 Observação

Comportamento: comparadores com reconhecimento linguístico e cultural


podem sofrer ajustes comportamentais de tempos em tempos. A UTI e a
instalação mais antiga do Windows NLS são atualizadas para considerar como
os idiomas do mundo mudam. Para obter mais informações, consulte a
rotatividade de dados de localidade (cultura) da postagem do blog. O
comportamento do comparador Ordinal nunca será alterado, pois ele executa
a pesquisa e a comparação bit a bit exatas. No entanto, o comportamento do
comparador OrdinalIgnoreCase pode mudar à medida que o Unicode cresce
para abranger mais conjuntos de caracteres e correções de funções em dados
de comparação e semelhanças que podem incluir medidas.
Uso: os comparadores StringComparison.InvariantCulture e
StringComparison.InvariantCultureIgnoreCase são comparadores linguísticos

que não reconhecem a cultura. Ou seja, esses comparadores entendem


conceitos como o caractere acentuado que tem várias representações
subjacentes possíveis e que todas essas representações devem ser tratadas
como iguais. Mas os comparadores linguísticos sem reconhecimento de
cultura não conterão tratamento especial para <dz> tão distinto de <d> ou
<z>, como mostrado acima. Eles também não vão diferenciar caracteres
como o alemão Eszett (ß).

O .NET também oferece o modo de globalização invariável. Esse modo de aceitação


desabilita caminhos de código que lidam com rotinas de pesquisa linguística e
comparação. Nesse modo, todas as operações usam comportamentos Ordinal ou
OrdinalIgnoreCase, independentemente do argumento CultureInfo ou
StringComparison fornecido pelo chamador. Para obter mais informações, consulte as

Opções de configuração do Runtime para globalização e Modo invariável de


globalização do .NET Core .

Para obter mais informações, consulte Práticas recomendadas para a comparação de


cadeias de caracteres no .NET.

Implicações de segurança
Se seu aplicativo usar uma API afetada para filtragem, recomendamos habilitar as regras
de análise de código ca1307 e CA1309 para ajudar a localizar locais onde uma pesquisa
linguística pode ter sido usada inadvertidamente em vez de uma pesquisa ordinal.
Padrões de código como o seguinte podem ser suscetíveis a explorações de segurança.

C#

//
// THIS SAMPLE CODE IS INCORRECT.
// DO NOT USE IT IN PRODUCTION.
//
public bool ContainsHtmlSensitiveCharacters(string input)
{
if (input.IndexOf("<") >= 0) { return true; }
if (input.IndexOf("&") >= 0) { return true; }
return false;
}

Como o método string.IndexOf(string) usa uma pesquisa linguística por padrão, é


possível que uma string contenha um caractere literal '<' ou '&' e que a rotina
string.IndexOf(string) retorne -1 , indicando que a substring de pesquisa não foi
encontrado. As regras de análise de código CA1307 e CA1309 sinalizam esses sites de
chamadas e alertam o desenvolvedor de que há um problema em potencial.

Tipos de pesquisa e comparação padrão


A tabela a seguir lista os tipos de pesquisa e comparação padrão para várias APIs
semelhantes a cadeias de caracteres e cadeias de caracteres. Se o chamador fornecer
um parâmetro CultureInfo ou StringComparison explícito, esse parâmetro será
respeitado em relação a qualquer padrão.

API Comportamento padrão Comentários

string.Compare CurrentCulture

string.CompareTo CurrentCulture

string.Contains Ordinal

string.EndsWith Ordinal (quando o primeiro parâmetro é char )

string.EndsWith CurrentCulture (quando o primeiro parâmetro é string )

string.Equals Ordinal

string.GetHashCode Ordinal

string.IndexOf Ordinal (quando o primeiro parâmetro é char )

string.IndexOf CurrentCulture (quando o primeiro parâmetro é string )

string.IndexOfAny Ordinal

string.LastIndexOf Ordinal (quando o primeiro parâmetro é char )

string.LastIndexOf CurrentCulture (quando o primeiro parâmetro é string )

string.LastIndexOfAny Ordinal

string.Replace Ordinal

string.Split Ordinal

string.StartsWith Ordinal (quando o primeiro parâmetro é char )

string.StartsWith CurrentCulture (quando o primeiro parâmetro é string )

string.ToLower CurrentCulture

string.ToLowerInvariant InvariantCulture

string.ToUpper CurrentCulture

string.ToUpperInvariant InvariantCulture

string.Trim Ordinal
API Comportamento padrão Comentários

string.TrimEnd Ordinal

string.TrimStart Ordinal

string == string Ordinal

string != string Ordinal

Ao contrário das APIs string , todas as APIs MemoryExtensions executam pesquisas


ordinais e comparações por padrão, com as seguintes exceções.

API Comportamento Comentários


padrão

MemoryExtensions.ToLower CurrentCulture (quando passado um argumento nulo


CultureInfo )

MemoryExtensions.ToLowerInvariant InvariantCulture

MemoryExtensions.ToUpper CurrentCulture (quando passado um argumento nulo


CultureInfo )

MemoryExtensions.ToUpperInvariant InvariantCulture

Uma consequência é que, ao converter o código de consumo string em consumo


ReadOnlySpan<char> , alterações comportamentais podem ser inadvertidamente

introduzidas. Abaixo, um exemplo disso.

C#

string str = GetString();


if (str.StartsWith("Hello")) { /* do something */ } // this is a CULTURE-
AWARE (linguistic) comparison

ReadOnlySpan<char> span = s.AsSpan();


if (span.StartsWith("Hello")) { /* do something */ } // this is an ORDINAL
(non-linguistic) comparison

A maneira recomendada de resolver isso é passar um parâmetro explícito


StringComparison para essas APIs. As regras de análise de código CA1307 e CA1309
podem ajudar com isso.

C#

string str = GetString();


if (str.StartsWith("Hello", StringComparison.Ordinal)) { /* do something */
} // ordinal comparison

ReadOnlySpan<char> span = s.AsSpan();


if (span.StartsWith("Hello", StringComparison.Ordinal)) { /* do something */
} // ordinal comparison

Confira também
Alterações interruptivas de globalização
Práticas recomendadas para comparar cadeias de caracteres no .NET
Como comparar cadeias de caracteres no C#
Globalização e ICU do .NET
Ordinal x operações sensíveis à cultura
Visão geral da análise do código-fonte do .NET
Operações básicas de cadeias de
caracteres no .NET
Artigo • 09/05/2023

Muitas vezes, os aplicativos respondem aos usuários criando mensagens com base na
entrada do usuário. Por exemplo, não é incomum para sites responderem a um usuário
recém-conectado com uma saudação especializada que inclui o nome do usuário.

Vários métodos nas classes System.String e System.Text.StringBuilder permitem que você


construa dinamicamente cadeias de caracteres personalizadas para exibir na interface
do usuário. Esses métodos também ajudam a realizar várias operações de cadeias de
caracteres básicas como criar novas cadeias de caracteres em matrizes de bytes,
comparar os valores das cadeias de caracteres e modificar cadeias de caracteres
existentes.

Seções relacionadas
Conversão de tipo no .NET
Descreve como converter de um tipo para outro.

Formatar tipos
Descreve como formatar cadeias de caracteres usando especificadores de formato.
Criação de novas cadeias de caracteres
no .NET
Artigo • 09/05/2023

O .NET permite que cadeias de caracteres sejam criadas usando atribuição simples e
sobrecarrega um construtor de classe para dar suporte à criação de cadeias de
caracteres usando um número de parâmetros diferentes. O .NET também fornece vários
métodos na classe System.String que criam novos objetos de cadeia de caracteres
combinando várias cadeias de caracteres, matrizes de cadeias de caracteres ou objetos.

Criando cadeias de caracteres usando


atribuição
A maneira mais fácil para criar um novo objeto String é simplesmente atribuir uma
cadeia de caracteres literal a um objeto String.

Criando cadeias de caracteres usando um


construtor de classe
Você pode usar sobrecargas do constructo de classe String para criar cadeias de
caracteres de matrizes de caracteres. Você também pode criar uma nova cadeia de
caracteres duplicando um caractere específico, m número de vezes especificado.

Métodos que retornam cadeias de caracteres


A tabela a seguir lista vários métodos úteis que retornam novos objetos de cadeia de
caracteres.

Nome do Use
método

String.Format Cria uma cadeia de caracteres formatada de um conjunto de objetos de entrada.

String.Concat Cria cadeias de caracteres de duas ou mais cadeias de caracteres.

String.Join Cria uma nova cadeia de caracteres combinando uma matriz de cadeias de
caracteres.
Nome do Use
método

String.Insert Cria uma nova cadeia de caracteres inserindo uma cadeia de caracteres no índice
especificado de uma cadeia de caracteres existente.

String.CopyTo Copia caracteres especificados de uma cadeia de caracteres para uma posição
especificada em uma matriz de caracteres.

Formatar
Você pode usar o método String.Format para criar cadeias de caracteres formatadas e
concatenar cadeias de caracteres que representam vários objetos. Este método converte
automaticamente qualquer objeto passado em uma cadeia de caracteres. Por exemplo,
se seu aplicativo precisar exibir um valor Int32 e um valor DateTime para o usuário,
você pode facilmente criar uma cadeia de caracteres para representar esses valores
usando o método Format. Para obter informações sobre as convenções de formatação
usadas com esse método, consulte a seção sobre formatação de composição.

O exemplo a seguir usa o método Format para criar uma cadeia de caracteres que usa
uma variável de inteiro.

C#

int numberOfFleas = 12;


string miscInfo = String.Format("Your dog has {0} fleas. " +
"It is time to get a flea collar. " +
"The current universal date is: {1:u}.",
numberOfFleas, DateTime.Now);
Console.WriteLine(miscInfo);
// The example displays the following output:
// Your dog has 12 fleas. It is time to get a flea collar.
// The current universal date is: 2008-03-28 13:31:40Z.

Neste exemplo, DateTime.Now exibe a data e hora atuais da maneira especificada pela
cultura associada ao thread atual.

Concat
O método String.Concat pode ser usado para criar facilmente um novo objeto de
cadeia de caracteres de dois ou mais objetos existentes. Ele fornece uma maneira
independente da linguagem para concatenar cadeias de caracteres. Este método aceita
qualquer classe derivada de System.Object. O exemplo a seguir cria uma cadeia de
caracteres de dois objetos de cadeia de caracteres existentes e um caractere de
separação.

C#

string helloString1 = "Hello";


string helloString2 = "World!";
Console.WriteLine(String.Concat(helloString1, ' ', helloString2));
// The example displays the following output:
// Hello World!

Join
O método String.Join cria uma nova cadeia de caracteres a partir de uma matriz de
cadeias de caracteres e uma cadeia de caracteres de separador. Esse método é útil se
você quiser concatenar várias cadeias de caracteres fazendo uma lista, talvez separada
por vírgula.

O exemplo a seguir usa um espaço para associar uma matriz de cadeias de caracteres.

C#

string[] words = {"Hello", "and", "welcome", "to", "my" , "world!"};


Console.WriteLine(String.Join(" ", words));
// The example displays the following output:
// Hello and welcome to my world!

Inserir
O método String.Insert cria uma nova cadeia de caracteres inserindo uma cadeia de
caracteres em uma posição especificada em outra cadeia de caracteres. Este método usa
um índice baseado em zero. O exemplo a seguir insere uma cadeia de caracteres na
quinta posição do índice de MyString e cria uma nova cadeia de caracteres com esse
valor.

C#

string sentence = "Once a time.";


Console.WriteLine(sentence.Insert(4, " upon"));
// The example displays the following output:
// Once upon a time.

CopyTo
O método String.CopyTo copia partes de uma cadeia de caracteres em uma matriz de
caracteres. Você pode especificar o índice inicial da cadeia de caracteres e o número de
caracteres a serem copiados. Este método usa o índice de origem, uma matriz de
caracteres, o índice de destino e o número de caracteres a serem copiados. Todos os
índices são baseados em zero.

O exemplo a seguir usa o método CopyTo para copiar os caracteres da palavra "Hello"
de um objeto de cadeia de caracteres para a primeira posição de índice de uma matriz
de caracteres.

C#

string greeting = "Hello World!";


char[] charArray = {'W','h','e','r','e'};
Console.WriteLine("The original character array: {0}", new
string(charArray));
greeting.CopyTo(0, charArray,0 ,5);
Console.WriteLine("The new character array: {0}", new string(charArray));
// The example displays the following output:
// The original character array: Where
// The new character array: Hello

Confira também
Operações básicas de cadeias de caracteres
Formatação composta
Cortar e remover caracteres de cadeias
de caracteres no .NET
Artigo • 10/05/2023

Se estiver analisando as palavras individuais de uma sentença, você poderá encontrar


palavras com espaços em branco em ambas as extremidades da palavra. Nessa situação,
você pode usar um dos métodos de corte na classe System.String para remover
qualquer número de espaços ou de outros caracteres de uma posição especificada na
cadeia de caracteres. A tabela a seguir descreve os métodos de corte disponíveis:

Nome do Use
método

String.Trim Remove os espaços em branco ou caracteres especificados em uma matriz de


caracteres do início e do final de uma cadeia de caracteres.

String.TrimEnd Remove os caracteres especificados em uma matriz de caracteres do final de


uma cadeia de caracteres.

String.TrimStart Remove os caracteres especificados em uma matriz de caracteres do início de


uma cadeia de caracteres.

String.Remove Remove um número especificado de caracteres de uma posição de índice


especificada em uma cadeia de caracteres.

Trim
Você pode remover espaços em branco com facilidade de ambas as extremidades de
uma cadeia de caracteres usando o método String.Trim, conforme é mostrado no
exemplo a seguir:

C#

string MyString = " Big ";


Console.WriteLine("Hello{0}World!", MyString);
string TrimString = MyString.Trim();
Console.WriteLine("Hello{0}World!", TrimString);
// The example displays the following output:
// Hello Big World!
// HelloBigWorld!

Você também poderá remover os caracteres que especificar em uma matriz de


caracteres do início e do final de uma cadeia de caracteres. O exemplo a seguir remove
caracteres de espaço em branco, pontos e asteriscos:

C#

using System;

public class Example


{
public static void Main()
{
String header = "* A Short String. *";
Console.WriteLine(header);
Console.WriteLine(header.Trim( new Char[] { ' ', '*', '.' } ));
}
}
// The example displays the following output:
// * A Short String. *
// A Short String

TrimEnd
O método String.TrimEnd remove os caracteres do final de uma cadeia de caracteres,
criando um novo objeto de cadeia de caracteres. Uma matriz de caracteres é passada
para este método para especificar os caracteres a seres removidos. A ordem dos
elementos na matriz de caracteres não afeta a operação de corte. O corte é
interrompido quando um caractere não especificado na matriz é encontrado.

O exemplo a seguir remove as últimas letras de uma cadeia de caracteres usando o


método TrimEnd . Neste exemplo, as posições do caractere 'r' e do caractere 'W' estão
invertidas para ilustrar que a ordem dos caracteres na matriz não importa. Observe que
este código remove a última palavra de MyString mais uma parte da primeira.

C#

string MyString = "Hello World!";


char[] MyChar = {'r','o','W','l','d','!',' '};
string NewString = MyString.TrimEnd(MyChar);
Console.WriteLine(NewString);

Esse código exibe He no console.

O exemplo a seguir remove as últimas palavras de uma cadeia de caracteres usando o


método TrimEnd . Nesse código, há uma vírgula após a palavra Hello e, como a vírgula
não está especificada na matriz de caracteres para cortar, o corte termina na vírgula.
C#

string MyString = "Hello, World!";


char[] MyChar = {'r','o','W','l','d','!',' '};
string NewString = MyString.TrimEnd(MyChar);
Console.WriteLine(NewString);

Esse código exibe Hello, no console.

TrimStart
O método String.TrimStart é semelhante ao método String.TrimEnd , exceto que ele
cria uma nova cadeia de caracteres removendo caracteres do início de um objeto de
cadeia de caracteres existente. Uma matriz de caracteres é passada para o método
TrimStart para especificar os caracteres a serem removidos. Assim como acontece com

o método TrimEnd , a ordem dos elementos na matriz de caracteres não afeta a


operação de corte. O corte é interrompido quando um caractere não especificado na
matriz é encontrado.

O exemplo a seguir remove a primeira palavra de uma cadeia de caracteres. Neste


exemplo, as posições do caractere 'l' e do caractere 'H' estão invertidas para ilustrar
que a ordem dos caracteres na matriz não importa.

C#

string MyString = "Hello World!";


char[] MyChar = {'e', 'H','l','o',' ' };
string NewString = MyString.TrimStart(MyChar);
Console.WriteLine(NewString);

Esse código exibe World! no console.

Remover
O método String.Remove remove um número especificado de caracteres que começam
em uma posição especificada em uma cadeia de caracteres existente. Este método
assume um índice baseado em zero.

O exemplo a seguir remove dez caracteres de uma cadeia de caracteres começando na


posição cinco de um índice baseado em zero da cadeia de caracteres.

C#
string MyString = "Hello Beautiful World!";
Console.WriteLine(MyString.Remove(5,10));
// The example displays the following output:
// Hello World!

Substitua
Você também pode remover um caractere ou uma subcadeia de caracteres especificada
de uma cadeia de caracteres chamando o método String.Replace(String, String) e
especificando uma cadeia de caracteres vazia (String.Empty) como a substituição. O
exemplo a seguir remove todas as vírgulas de uma cadeia de caracteres:

C#

using System;

public class Example


{
public static void Main()
{
String phrase = "a cold, dark night";
Console.WriteLine("Before: {0}", phrase);
phrase = phrase.Replace(",", "");
Console.WriteLine("After: {0}", phrase);
}
}
// The example displays the following output:
// Before: a cold, dark night
// After: a cold dark night

Confira também
Operações básicas de cadeias de caracteres
Como preencher cadeias de caracteres
no .NET
Artigo • 10/05/2023

Use um dos métodos String a seguir para criar uma nova cadeia de caracteres que
consista em uma cadeia de caracteres original preenchida com caracteres à esquerda ou
à direita até um comprimento total especificado. O caractere de preenchimento pode
ser um espaço ou um caractere especificado. A cadeia de caracteres resultante parece
estar alinhada à direita ou à esquerda. Se o tamanho da cadeia de caracteres original já
for igual ou maior que o tamanho total desejado, os métodos de preenchimento
retornarão a cadeia de caracteres original inalterada. Para obter mais informações,
confira as seções Retornos das duas sobrecargas dos métodos String.PadLeft e
String.PadRight.

Nome do Use
método

String.PadLeft Acrescenta uma cadeia de caracteres com caracteres à esquerda até um


comprimento total especificado.

String.PadRight Acrescenta uma cadeia de caracteres com caracteres à direita até um


comprimento total especificado.

PadLeft
O método String.PadLeft cria uma nova cadeia de caracteres concatenando caracteres
de preenchimento à esquerda suficientes para uma cadeia de caracteres original atingir
um comprimento total especificado. O método String.PadLeft(Int32) usa o espaço em
branco como o caractere de preenchimento, e o método String.PadLeft(Int32, Char)
permite que você especifique seus próprios caracteres de preenchimento.

O exemplo de código a seguir usa o método PadLeft para criar uma nova cadeia de
caracteres com vinte caracteres de comprimento. O exemplo exibe “ --------Hello
World! ” no console.

C#

string MyString = "Hello World!";


Console.WriteLine(MyString.PadLeft(20, '-'));
PadRight
O método String.PadRight cria uma nova cadeia de caracteres concatenando caracteres
de preenchimento à esquerda suficientes para uma cadeia de caracteres original atingir
um comprimento total especificado. O método String.PadRight(Int32) usa o espaço em
branco como o caractere de preenchimento, e o método String.PadRight(Int32, Char)
permite que você especifique seus próprios caracteres de preenchimento.

O exemplo de código a seguir usa o método PadRight para criar uma nova cadeia de
caracteres com vinte caracteres de comprimento. O exemplo exibe “ Hello World!------
-- ” no console.

C#

string MyString = "Hello World!";


Console.WriteLine(MyString.PadRight(20, '-'));

Confira também
Operações básicas de cadeias de caracteres
Comparar cadeias de caracteres no .NET
Artigo • 10/05/2023

O .NET fornece vários métodos para comparar os valores de cadeias de caracteres. A


tabela a seguir lista e descreve os métodos de comparação de valores.

Nome do método Use

String.Compare Compara os valores das duas cadeias de caracteres. Retorna um valor


inteiro.

String.CompareOrdinal Compara duas cadeias de caracteres sem considerar a cultura local.


Retorna um valor inteiro.

String.CompareTo Compara o objeto atual de cadeia de caracteres a outra cadeia de


caracteres. Retorna um valor inteiro.

String.StartsWith Determina se uma cadeia de caracteres começa com a cadeia de


caracteres passada. Retorna um valor booliano.

String.EndsWith Determina se uma cadeia de caracteres termina com a cadeia de


caracteres passada. Retorna um valor booliano.

String.Contains Determina se um caractere ou cadeia de caracteres ocorre dentro de


outra cadeia de caracteres. Retorna um valor booliano.

String.Equals Determina se duas cadeias de caracteres são iguais. Retorna um valor


booliano.

String.IndexOf Retorna a posição do índice de um caractere ou uma cadeia de


caracteres, começando do início da cadeia de caracteres que você está
examinando. Retorna um valor inteiro.

String.LastIndexOf Retorna a posição do índice de um caractere ou uma cadeia de


caracteres, começando do fim da cadeia de caracteres que você está
examinando. Retorna um valor inteiro.

Método Compare
O método estático String.Compare fornece uma maneira completa de comparar duas
cadeias de caracteres. Esse método é cultural. Você pode usar essa função para
comparar duas cadeias de caracteres ou subcadeias de duas cadeias de caracteres. Além
disso, sobrecargas são fornecidas para considerar ou ignorar a variação cultural ou de
caso. A tabela a seguir mostra os três valores inteiros que esse método pode retornar.
Valor Condição
retornado

Um inteiro A primeira cadeia de caracteres precede a segunda cadeia de caracteres na


negativo ordem de classificação.

-ou-

A primeira cadeia de caracteres é null .

0 A primeira cadeia de caracteres e a segunda cadeia de caracteres são iguais.

-ou-

Ambas as cadeias de caracteres são null .

Um inteiro A primeira cadeia de caracteres segue a segunda cadeia de caracteres na


positivo ordem de classificação.

-ou- -ou-

1 A segunda cadeia de caracteres é null .

) Importante

O método String.Compare destina-se principalmente para uso em ordenação ou


classificação de cadeias de caracteres. Você não deve usar o método
String.Compare para testar a igualdade (ou seja, para procurar explicitamente um
valor retornado de 0 sem considerar se uma cadeia de caracteres é menor que ou
maior que a outra). Em vez disso, para determinar se duas cadeias de caracteres
são iguais, use o método String.Equals(String, String, StringComparison).

O exemplo a seguir usa o método String.Compare para determinar os valores relativos


das duas cadeias de caracteres.

C#

string string1 = "Hello World!";


Console.WriteLine(String.Compare(string1, "Hello World?"));

Este exemplo exibe -1 no console.

O exemplo anterior diferencia a cultura por padrão. Para realizar uma comparação de
cadeia de caracteres que não diferencia a cultura, use uma sobrecarga do método
String.Compare que permite que você especifique a cultura a ser usada, fornecendo um
parâmetro culture. Confira um exemplo que demonstra como usar o método
String.Compare para realizar uma comparação de cadeia de caracteres que não fazem
distinção entre culturas em Comparações de cadeias de caracteres que não fazem
distinção entre culturas.

Método CompareOrdinal
O método String.CompareOrdinal compara dois objetos de cadeia de caracteres sem
considerar a cultura local. Os valores de retorno desse método são idênticos aos valores
retornados pelo método Compare na tabela anterior.

) Importante

O método String.CompareOrdinal destina-se principalmente para uso em


ordenação ou classificação de cadeias de caracteres. Você não deve usar o método
String.CompareOrdinal para testar a igualdade (ou seja, para procurar
explicitamente um valor retornado de 0 sem considerar se uma cadeia de
caracteres é menor que ou maior que a outra). Em vez disso, para determinar se
duas cadeias de caracteres são iguais, use o método String.Equals(String, String,
StringComparison).

O exemplo a seguir usa o método CompareOrdinal para comparar os valores de duas


cadeias de caracteres.

C#

string string1 = "Hello World!";


Console.WriteLine(String.CompareOrdinal(string1, "hello world!"));

Este exemplo exibe -32 no console.

Método CompareTo
O método String.CompareTo compara a cadeia de caracteres que o objeto atual de
cadeia de caracteres encapsula com outro objeto ou cadeia de caracteres. Os valores de
retorno desse método são idênticos aos valores retornados pelo método
String.Compare na tabela anterior.

) Importante
O método String.CompareTo destina-se principalmente para uso em ordenação ou
classificação de cadeias de caracteres. Você não deve usar o método
String.CompareTo para testar a igualdade (ou seja, para procurar explicitamente
um valor retornado de 0 sem considerar se uma cadeia de caracteres é menor que
ou maior que a outra). Em vez disso, para determinar se duas cadeias de caracteres
são iguais, use o método String.Equals(String, String, StringComparison).

O exemplo a seguir usa o método String.CompareTo para comparar o objeto string1


ao objeto string2 .

C#

string string1 = "Hello World";


string string2 = "Hello World!";
int MyInt = string1.CompareTo(string2);
Console.WriteLine( MyInt );

Este exemplo exibe -1 no console.

Todas as sobrecargas do método String.CompareTo executam comparações que


diferenciam a cultura e com diferenciação de maiúsculas e minúsculas por padrão.
Nenhuma sobrecarga desse método é fornecida que permite que você execute uma
comparação sem diferenciação de cultura. Em vez disso, para ter maior clareza do
código, é recomendável usar o método String.Compare , especificando
CultureInfo.CurrentCulture para operações que fazem distinção entre culturas ou
CultureInfo.InvariantCulture para operações que não fazem distinção entre culturas.
Confira exemplos que demonstram como usar o método String.Compare para realizar
comparações de cadeia de caracteres que fazem e não fazem distinção entre culturas
em Executar comparações de cadeias de caracteres que não fazem distinção entre
culturas.

Método Equals
O método String.Equals pode facilmente determinar se duas cadeias de caracteres são
as mesmas. Esse método que diferencia maiúsculas de minúsculas retorna um valor
Booliano true ou false . Ele pode ser usado de uma classe existente, conforme
ilustrado no exemplo a seguir. O exemplo a seguir usa o método Equals para
determinar se um objeto de cadeia de caracteres contém a frase "Hello World".

C#
string string1 = "Hello World";
Console.WriteLine(string1.Equals("Hello World"));

Este exemplo exibe True no console.

Esse método também pode ser usado como um método estático. O exemplo a seguir
compara dois objetos de cadeia de caracteres usando um método estático.

C#

string string1 = "Hello World";


string string2 = "Hello World";
Console.WriteLine(String.Equals(string1, string2));

Este exemplo exibe True no console.

Métodos StartsWith e EndsWith


Você pode usar o método String.StartsWith para determinar se um objeto de cadeia de
caracteres começa com os mesmos caracteres que englobam outra cadeia de
caracteres. Esse método que diferencia maiúsculas de minúsculas retorna true se o
objeto atual de cadeia de caracteres começa com a cadeia de caracteres passada e
false se não existir. O exemplo a seguir usa esse método para determinar se um objeto
de cadeia de caracteres começa com "Hello".

C#

string string1 = "Hello World";


Console.WriteLine(string1.StartsWith("Hello"));

Este exemplo exibe True no console.

O método String.EndsWith compara uma cadeia de caracteres passada com os


caracteres que existem no final do objeto de cadeia de caracteres atual. Ele também
retorna um valor Booliano. O exemplo a seguir verifica o fim de uma cadeia de
caracteres usando o método EndsWith .

C#

string string1 = "Hello World";


Console.WriteLine(string1.EndsWith("Hello"));
Este exemplo exibe False no console.

Métodos IndexOf e LastIndexOf


É possível o método String.IndexOf para determinar a posição da primeira ocorrência de
um determinado caractere dentro de uma cadeia de caracteres. Esse método que
diferencia maiúsculas de minúsculas inicia a contagem do início de uma cadeia de
caracteres e retorna a posição de um caractere passado usando um índice baseado em
zero. Se o caractere não for encontrado, um valor de -1 será retornado.

O exemplo a seguir usa o método IndexOf para pesquisar a primeira ocorrência do


caractere ' l ' em uma cadeia de caracteres.

C#

string string1 = "Hello World";


Console.WriteLine(string1.IndexOf('l'));

Este exemplo exibe 2 no console.

O método String.LastIndexOf é semelhante ao método String.IndexOf , exceto que ele


retorna a posição da última ocorrência de um determinado caractere dentro de uma
cadeia de caracteres. Ele diferencia maiúsculas de minúsculas e usa um índice baseado
em zero.

O exemplo a seguir usa o método LastIndexOf para pesquisar a última ocorrência do


caractere ' l ' em uma cadeia de caracteres.

C#

string string1 = "Hello World";


Console.WriteLine(string1.LastIndexOf('l'));

Este exemplo exibe 9 no console.

Ambos os métodos são úteis quando usados em conjunto com o método


String.Remove. Você pode usar tanto o método IndexOf quanto o LastIndexOf para
recuperar a posição de um caractere e, em seguida, fornecer essa posição para o
método Remove para remover um caractere ou uma palavra que comece com esse
caractere.
Confira também
Práticas recomendadas para o uso de cadeias de caracteres no .NET
Operações básicas de cadeias de caracteres
Executar operações de cadeia de caracteres que não levam em conta a cultura
Classificar tabelas de peso – usado pelo .NET Framework e .NET Core 1.0-3.1 no
Windows
Tabela de elementos de ordenação Unicode padrão – usada pelo .NET 5 em
todas as plataformas e pelo .NET Core no Linux e no macOS
Alterar maiúsculas e minúsculas no .NET
Artigo • 10/05/2023

Se você gravar um aplicativo que aceita a inserção de informações por um usuário,


talvez você nunca tenha certeza se ele ou ela usará maiúsculas ou minúsculas para
inserir os dados. Muitas vezes, você quer que as cadeias de caracteres tenham a grafia
de maiúsculas e minúsculas consistente, especialmente se você estiver exibindo-as na
interface do usuário. A tabela seguinte descreve três métodos de alteração de
capitalização. Os primeiros dois métodos fornecem uma sobrecarga que aceita uma
cultura.

Nome do método Use

String.ToUpper Converte todos os caracteres em uma cadeia de caracteres para maiúsculas.

String.ToLower Converte todos os caracteres em uma cadeia de caracteres para


minúsculas.

TextInfo.ToTitleCase Converte uma cadeia de caracteres para capitalização de título.

2 Aviso

Os métodos String.ToUpper e String.ToLower não devem ser usados para


converter cadeias de caracteres a fim de compará-las ou testá-las quanto à
igualdade. Confira mais informações na seção Comparar cadeias de caracteres em
maiúsculas e minúsculas.

Comparar cadeias de caracteres em maiúsculas


e minúsculas
Para comparar cadeias de caracteres em maiúsculas e minúsculas e determinar a ordem
delas, chame uma das sobrecargas do método String.CompareTo com um parâmetro
comparisonType e forneça um valor de StringComparison.CurrentCultureIgnoreCase,

StringComparison.InvariantCultureIgnoreCase ou StringComparison.OrdinalIgnoreCase
para o argumento comparisonType . Para fazer uma comparação usando uma cultura
específica que não seja a atual, chame uma sobrecarga do método String.CompareTo
com um parâmetro culture e um options e forneça um valor de
CompareOptions.IgnoreCase como o argumento options .
Para comparar cadeias de caracteres em maiúsculas e minúsculas e determinar se são
iguais, chame uma das sobrecargas do método String.Equals com um parâmetro
comparisonType e forneça um valor de StringComparison.CurrentCultureIgnoreCase,

StringComparison.InvariantCultureIgnoreCase ou StringComparison.OrdinalIgnoreCase
para o argumento comparisonType .

Para obter mais informações, consulte Práticas recomendadas para o uso de cadeias de
caracteres.

Método ToUpper
O método String.ToUpper altera todos os caracteres em uma cadeia para maiúsculas. O
exemplo a seguir converte a cadeia de caracteres "Olá, Mundo!" de maiúsculas e
minúsculas misturadas.

C#

string properString = "Hello World!";


Console.WriteLine(properString.ToUpper());
// This example displays the following output:
// HELLO WORLD!

O exemplo anterior diferencia culturas por padrão e aplica as convenções de


capitalização da cultura atual. Para realizar uma alteração de capitalização que não
diferencia a cultura ou para aplicar as convenções de capitalização de uma cultura
específica, use a sobrecarga do método String.ToUpper(CultureInfo) e forneça um valor
de CultureInfo.InvariantCulture ou um objeto System.Globalization.CultureInfo que
representa a cultura especificada para o parâmetro culture . Confira um exemplo que
demonstra como usar o método ToUpper para realizar uma Alteração de capitalização
que não diferencia a cultura.

Método ToLower
O método String.ToLower é semelhante ao anterior, porém ele converte todos os
caracteres de uma cadeia para minúsculas. O exemplo a seguir converte a cadeia de
caracteres "Hello, World!" para letras minúsculas.

C#

string properString = "Hello World!";


Console.WriteLine(properString.ToLower());
// This example displays the following output:
// hello world!
O exemplo anterior diferencia culturas por padrão e aplica as convenções de
capitalização da cultura atual. Para realizar uma alteração de capitalização que não
diferencia a cultura ou para aplicar as convenções de capitalização de uma cultura
específica, use a sobrecarga do método String.ToLower(CultureInfo) e forneça um valor
de CultureInfo.InvariantCulture ou um objeto System.Globalization.CultureInfo que
representa a cultura especificada para o parâmetro culture . Confira um exemplo que
demonstra como usar o método ToLower(CultureInfo) para realizar uma Alteração de
capitalização que não diferencia a cultura.

Método ToTitleCase
O TextInfo.ToTitleCase converte o primeiro caractere de cada palavra em maiúscula e os
restantes em minúsculas. No entanto, as palavras que são totalmente maiúsculas são
consideradas acrônimos e não são convertidas.

O método TextInfo.ToTitleCase diferencia a cultura, ou seja, ele usa as convenções de


capitalização de uma determinada cultura. Para chamar o método, é preciso antes
recuperar o objeto TextInfo que representa as convenções de capitalização da cultura
específica da propriedade CultureInfo.TextInfo de uma determinada cultura.

O exemplo a seguir passa todas as cadeias de caracteres de uma matriz para o método
TextInfo.ToTitleCase. As cadeias de caracteres incluem cadeias de caracteres de títulos
próprios assim como acrônimos. As cadeias de caracteres são convertidas para a
capitalização de título usando as convenções de capitalização da cultura do inglês
(Estados Unidos).

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
string[] values = { "a tale of two cities", "gROWL to the rescue",
"inside the US government", "sports and MLB
baseball",
"The Return of Sherlock Holmes", "UNICEF and
children"};

TextInfo ti = CultureInfo.CurrentCulture.TextInfo;
foreach (var value in values)
Console.WriteLine("{0} --> {1}", value, ti.ToTitleCase(value));
}
}
// The example displays the following output:
// a tale of two cities --> A Tale Of Two Cities
// gROWL to the rescue --> Growl To The Rescue
// inside the US government --> Inside The US Government
// sports and MLB baseball --> Sports And MLB Baseball
// The Return of Sherlock Holmes --> The Return Of Sherlock Holmes
// UNICEF and children --> UNICEF And Children

Embora diferencie a cultura, o método TextInfo.ToTitleCase não fornece regras


linguisticamente corretas de capitalização. No exemplo anterior, o método converte "um
conto de duas cidades" para "Um Conto De Duas Cidades". No entanto, a capitalização
linguisticamente correta para a cultura en-US seria "Um Conto de Duas Cidades."

Confira também
Operações básicas de cadeias de caracteres
Executar operações de cadeia de caracteres que não levam em conta a cultura
Extrair substrings de uma cadeia de
caracteres
Artigo • 10/05/2023

Este artigo aborda algumas técnicas diferentes para extrair partes de uma cadeia de
caracteres.

Use o método de divisão quando as substrings forem separadas por um ou mais


caracteres delimitadores conhecidos.
Expressões regulares são úteis quando a cadeia de caracteres está em
conformidade com um padrão fixo.
Use os métodos IndexOf e Substring em conjunto quando não quiser extrair todas
as substrings em uma cadeia de caracteres.

Método String.Split
String.Split fornece diversas sobrecargas para ajudar você a dividir uma cadeia de
caracteres em um grupo de substrings com base em um ou mais caracteres
delimitadores especificados. É possível optar por limitar o número total de substrings no
resultado final, cortar caracteres de espaço em branco de substrings ou excluir
substrings vazias.

Os exemplos a seguir mostram três sobrecargas diferentes de String.Split() . O


primeiro chama a sobrecarga Split(Char[]) sem transmitir caracteres separadores.
Quando nenhum caractere delimitador é especificado, String.Split() usa
delimitadores padrão (caracteres de espaço em branco) para dividir a cadeia de
caracteres.

C#

string s = "You win some. You lose some.";

string[] subs = s.Split();

foreach (string sub in subs)


{
Console.WriteLine($"Substring: {sub}");
}

// This example produces the following output:


//
// Substring: You
// Substring: win
// Substring: some.
// Substring: You
// Substring: lose
// Substring: some.

Os caracteres de ponto ( . ) estão incluídos em duas das substrings. Para excluir


caracteres de ponto, adicione-os como caracteres delimitadores adicionais. O próximo
exemplo mostra como fazer isso.

C#

string s = "You win some. You lose some.";

string[] subs = s.Split(' ', '.');

foreach (string sub in subs)


{
Console.WriteLine($"Substring: {sub}");
}

// This example produces the following output:


//
// Substring: You
// Substring: win
// Substring: some
// Substring:
// Substring: You
// Substring: lose
// Substring: some
// Substring:

Os pontos sumiram das substrings, mas duas substrings vazias extras foram incluídas
agora. Essas substrings vazias representam a substring entre a palavra e o ponto que a
segue. Para omitir substrings vazias da matriz resultante, é possível chamar a sobrecarga
Split(Char[], StringSplitOptions) e especificar StringSplitOptions.RemoveEmptyEntries
para o parâmetro options .

C#

string s = "You win some. You lose some.";


char[] separators = new char[] { ' ', '.' };

string[] subs = s.Split(separators, StringSplitOptions.RemoveEmptyEntries);

foreach (string sub in subs)


{
Console.WriteLine($"Substring: {sub}");
}
// This example produces the following output:
//
// Substring: You
// Substring: win
// Substring: some
// Substring: You
// Substring: lose
// Substring: some

Expressões regulares
Se a cadeia de caracteres estiver em conformidade com um padrão fixo, será possível
usar uma expressão regular para extrair e manipular seus elementos. Por exemplo, se as
cadeias de caracteres tomarem a forma "númerooperandonúmero", será possível usar
uma expressão regular para extrair e manipular os elementos delas. Veja um exemplo:

C#

String[] expressions = { "16 + 21", "31 * 3", "28 / 3",


"42 - 18", "12 * 7",
"2, 4, 6, 8" };
String pattern = @"(\d+)\s+([-+*/])\s+(\d+)";

foreach (string expression in expressions)


{
foreach (System.Text.RegularExpressions.Match m in
System.Text.RegularExpressions.Regex.Matches(expression, pattern))
{
int value1 = Int32.Parse(m.Groups[1].Value);
int value2 = Int32.Parse(m.Groups[3].Value);
switch (m.Groups[2].Value)
{
case "+":
Console.WriteLine("{0} = {1}", m.Value, value1 + value2);
break;
case "-":
Console.WriteLine("{0} = {1}", m.Value, value1 - value2);
break;
case "*":
Console.WriteLine("{0} = {1}", m.Value, value1 * value2);
break;
case "/":
Console.WriteLine("{0} = {1:N2}", m.Value, value1 / value2);
break;
}
}
}

// The example displays the following output:


// 16 + 21 = 37
// 31 * 3 = 93
// 28 / 3 = 9.33
// 42 - 18 = 24
// 12 * 7 = 84

O padrão de expressão regular (\d+)\s+([-+*/])\s+(\d+) é definido da seguinte forma:

Padrão Descrição

(\d+) Corresponde a um ou mais dígitos decimais. Este é o primeiro grupo de captura.

\s+ Corresponde a um ou mais caracteres de espaço em branco.

([- Corresponde a um sinal de operador aritmético (+, -, * ou /). Este é o segundo grupo
+*/]) de captura.

\s+ Corresponde a um ou mais caracteres de espaço em branco.

(\d+) Corresponde a um ou mais dígitos decimais. Este é o terceiro grupo de captura.

Também é possível usar uma expressão regular para extrair substrings de uma cadeia de
caracteres com base em um padrão em vez de em um conjunto fixo de caracteres. Este
é um cenário comum em qualquer uma destas condições:

Um ou mais dos caracteres delimitadores nem sempre servem como delimitadores


na instância String.

A sequência e o número de caracteres delimitadores são variáveis ou


desconhecidos.

Por exemplo, o método Split não pode ser usado para dividir a cadeia de caracteres a
seguir, pois o número de caracteres \n (nova linha) é variável e eles nem sempre
servem como delimitadores.

text

[This is captured\ntext.]\n\n[\n[This is more captured text.]\n]


\n[Some more captured text:\n Option1\n Option2][Terse text.]

Uma expressão regular pode dividir essa cadeia de caracteres facilmente, como mostra
o exemplo a seguir.

C#

String input = "[This is captured\ntext.]\n\n[\n" +


"[This is more captured text.]\n]\n" +
"[Some more captured text:\n Option1" +
"\n Option2][Terse text.]";
String pattern = @"\[([^\[\]]+)\]";
int ctr = 0;

foreach (System.Text.RegularExpressions.Match m in
System.Text.RegularExpressions.Regex.Matches(input, pattern))
{
Console.WriteLine("{0}: {1}", ++ctr, m.Groups[1].Value);
}

// The example displays the following output:


// 1: This is captured
// text.
// 2: This is more captured text.
// 3: Some more captured text:
// Option1
// Option2
// 4: Terse text.

O padrão de expressão regular \[([^\[\]]+)\] é definido da seguinte forma:

Padrão Descrição

\[ Corresponde a um colchete de abertura.

([^\ Corresponde a qualquer caractere que não seja um colchete de abertura ou


[\]]+) fechamento uma ou mais vezes. Este é o primeiro grupo de captura.

\] Corresponde a um colchete de fechamento.

O método Regex.Split é quase idêntico a String.Split, mas divide uma cadeia de


caracteres com base em um padrão de expressão regular em vez de em um conjunto de
caracteres fixo. Por exemplo, o exemplo a seguir usa o método Regex.Split para dividir
uma cadeia de caracteres que contém substrings delimitadas por diversas combinações
de hifens e outros caracteres.

C#

String input = "abacus -- alabaster - * - atrium -+- " +


"any -*- actual - + - armoire - - alarm";
String pattern = @"\s-\s?[+*]?\s?-\s";
String[] elements = System.Text.RegularExpressions.Regex.Split(input,
pattern);

foreach (string element in elements)


Console.WriteLine(element);

// The example displays the following output:


// abacus
// alabaster
// atrium
// any
// actual
// armoire
// alarm

O padrão de expressão regular \s-\s?[+*]?\s?-\s é definido da seguinte forma:

Padrão Descrição

\s- Corresponde a um caractere de espaço em branco seguido por um hífen.

\s? Corresponder a zero ou a um caractere de espaço em branco.

[+*]? Corresponde a nenhuma ou uma ocorrência do caractere + ou *.

\s? Corresponder a zero ou a um caractere de espaço em branco.

-\s Corresponde a um hífen seguido por um caractere de espaço em branco.

Métodos String.IndexOf e String.Substring


Se você não estiver interessado em todas as substrings em uma cadeia de caracteres,
talvez prefira trabalhar com um dos métodos de comparação de cadeia de caracteres
que retornam o índice que inicia a correspondência. Em seguida, é possível chamar o
método Substring para extrair a substring desejada. Os métodos de comparação de
cadeia de caracteres incluem:

IndexOf, que retorna o índice baseado em zero da primeira ocorrência de um


caractere ou cadeia de caracteres em uma instância da cadeia.

IndexOfAny, que retorna o índice baseado em zero na instância de cadeia de


caracteres atual da primeira ocorrência de qualquer caractere em uma matriz de
caracteres.

LastIndexOf, que retorna o índice baseado em zero da última ocorrência de um


caractere ou cadeia de caracteres em uma instância da cadeia.

LastIndexOfAny, que retorna um índice baseado em zero na instância de cadeia de


caracteres atual da última ocorrência de qualquer caractere em uma matriz de
caracteres.

O exemplo a seguir usa o método IndexOf para localizar os pontos em uma cadeia de
caracteres. Em seguida, ele usa o método Substring para retornar frases completas.
C#

String s = "This is the first sentence in a string. " +


"More sentences will follow. For example, " +
"this is the third sentence. This is the " +
"fourth. And this is the fifth and final " +
"sentence.";
var sentences = new List<String>();
int start = 0;
int position;

// Extract sentences from the string.


do
{
position = s.IndexOf('.', start);
if (position >= 0)
{
sentences.Add(s.Substring(start, position - start + 1).Trim());
start = position + 1;
}
} while (position > 0);

// Display the sentences.


foreach (var sentence in sentences)
Console.WriteLine(sentence);

// The example displays the following output:


// This is the first sentence in a string.
// More sentences will follow.
// For example, this is the third sentence.
// This is the fourth.
// And this is the fifth and final sentence.

Confira também
Operações básicas de cadeia de caracteres no .NET
Expressões regulares do .NET
Como analisar cadeias de caracteres usando String.Split em C#
Uso da classe StringBuilder no .NET
Artigo • 10/05/2023

O objeto String é imutável. Sempre que usa um dos métodos na classe System.String,
você cria um novo objeto de cadeia de caracteres na memória, o que requer uma nova
alocação de espaço para esse novo objeto. Em situações em que você precisa realizar
repetidas modificações em uma cadeia de caracteres, a sobrecarga associada à criação
de um novo objeto String pode ser dispendiosa. A classe System.Text.StringBuilder pode
ser usada quando você deseja modificar uma cadeia de caracteres sem criar um novo
objeto. Por exemplo, o uso da classe StringBuilder pode melhorar o desempenho ao
concatenar várias cadeias de caracteres em um loop.

Importando o namespace System.Text


A classe StringBuilder é encontrada no namespace System.Text. Para evitar ter que
fornecer um nome de tipo totalmente qualificado no código, você pode importar o
namespace System.Text:

C#

using System;
using System.Text;

Criando uma instância de um objeto


StringBuilder
Você pode criar uma nova instância da classe StringBuilder inicializando sua variável
com um dos métodos do construtor sobrecarregados, conforme ilustrado no exemplo a
seguir.

C#

StringBuilder myStringBuilder = new StringBuilder("Hello World!");

Definindo a capacidade e o comprimento


Embora o StringBuilder seja um objeto dinâmico que permite que você expanda o
número de caracteres na cadeia de caracteres que ele encapsula, você pode especificar
um valor para o número máximo de caracteres que ele pode conter. Esse valor é
chamado de capacidade do objeto e não deve ser confundido com o comprimento da
cadeia de caracteres que o StringBuilder atual contém. Por exemplo, você pode criar
uma nova instância da classe StringBuilder com a cadeia de caracteres "Hello", que tem
um comprimento de 5 caracteres e você pode especificar que o objeto tenha uma
capacidade máxima de 25. Quando você modifica o StringBuilder, ele não realoca
tamanho para si mesmo até que a capacidade seja atingida. Quando isso ocorre, o novo
espaço é alocado automaticamente e a capacidade é dobrada. Você pode especificar a
capacidade da classe StringBuilder usando um dos construtores sobrecarregados. O
exemplo a seguir especifica que o objeto myStringBuilder pode ser expandido para um
máximo de 25 espaços.

C#

StringBuilder myStringBuilder = new StringBuilder("Hello World!", 25);

Além disso, você pode usar a propriedade Capacity de leitura/gravação para definir o
comprimento máximo do objeto. O exemplo a seguir usa a propriedade Capacidade
para definir o comprimento máximo de objeto.

C#

myStringBuilder.Capacity = 25;

O método EnsureCapacity pode ser usado para verificar a capacidade do StringBuilder


atual. Se a capacidade for maior que o valor transmitido, nenhuma alteração é feita; no
entanto, se a capacidade for menor do que o valor transmitido, a capacidade atual é
alterada para corresponder ao valor transmitido.

A propriedade Length também pode ser exibida ou definida. Se você definir a


propriedade Comprimento para um valor maior que a propriedade Capacidade, a
propriedade Capacidade será alterada automaticamente para o mesmo valor que a
propriedade Comprimento. Definir a propriedade Comprimento para um valor menor
que o comprimento da cadeia de caracteres no StringBuilder atual, diminui a cadeia de
caracteres.

Modificando a cadeia de caracteres do


StringBuilder
A tabela a seguir lista os métodos que você pode usar para modificar o conteúdo de um
StringBuilder.
Nome do método Use

StringBuilder.Append Acrescenta informações ao final do StringBuilder atual.

StringBuilder.AppendFormat Substitui um especificador de formato transmitido em uma cadeia


de caracteres com texto formatado.

StringBuilder.Insert Insere uma cadeia de caracteres ou um objeto no índice


especificado do StringBuilder atual.

StringBuilder.Remove Remove um número especificado de caracteres do StringBuilder


atual.

StringBuilder.Replace Substitui todas as ocorrências de um caractere ou string


especificado no StringBuilder atual por outro caractere ou string
especificado.

Acrescentar
O método Append pode ser usado para adicionar texto ou uma representação de
cadeia de caracteres de um objeto ao final de uma cadeia de caracteres representada
pelo StringBuilder atual. O exemplo a seguir inicializa um StringBuilder para "Hello
World" e, em seguida, acrescenta algum texto ao final do objeto. Espaço é alocado
automaticamente conforme necessário.

C#

StringBuilder myStringBuilder = new StringBuilder("Hello World!");


myStringBuilder.Append(" What a beautiful day.");
Console.WriteLine(myStringBuilder);
// The example displays the following output:
// Hello World! What a beautiful day.

AppendFormat
O método StringBuilder.AppendFormat adiciona texto ao final do objeto StringBuilder.
Ele dá suporte ao recurso de formatação de composição (para obter mais informações,
confira Formatação de composição) chamando a implementação IFormattable do
objeto ou objetos a serem formatados. Portanto, aceita as cadeias de caracteres de
formato padrão para valores numéricos, data e hora e enumeração, cadeias de
caracteres de formato personalizado para valores numéricos e de data e hora e cadeias
de caracteres de formato definidas para tipos personalizados. (Para obter informações
sobre formatação, consulte Tipos de formatação.) Você pode usar esse método para
personalizar o formato das variáveis e anexar esses valores a um StringBuilder. O
exemplo a seguir usa o método AppendFormat para colocar um valor inteiro, formatado
como um valor de moeda, no final de um objeto StringBuilder.

C#

int MyInt = 25;


StringBuilder myStringBuilder = new StringBuilder("Your total is ");
myStringBuilder.AppendFormat("{0:C} ", MyInt);
Console.WriteLine(myStringBuilder);
// The example displays the following output:
// Your total is $25.00

Inserir
O método Insert adiciona uma cadeia de caracteres ou um objeto a uma posição
especificada no objeto StringBuilder atual. O exemplo a seguir usa esse método para
inserir uma palavra na sexta posição de um objeto StringBuilder.

C#

StringBuilder myStringBuilder = new StringBuilder("Hello World!");


myStringBuilder.Insert(6,"Beautiful ");
Console.WriteLine(myStringBuilder);
// The example displays the following output:
// Hello Beautiful World!

Remover
Você pode usar o método Remove para remover um número especificado de caracteres
do objeto StringBuilder atual, começando em um índice com base zero especificado. O
exemplo a seguir usa o método Remove para reduzir um objeto StringBuilder.

C#

StringBuilder myStringBuilder = new StringBuilder("Hello World!");


myStringBuilder.Remove(5,7);
Console.WriteLine(myStringBuilder);
// The example displays the following output:
// Hello

Substitua
O método Replace pode ser usado para substituir caracteres dentro do objeto
StringBuilder com outro caractere especificado. O exemplo a seguir usa o método
Replace para pesquisar, em um objeto StringBuilder, todas as instâncias do caractere de
ponto de exclamação (!) e substituí-los com o caractere de ponto de interrogação (?).

C#

StringBuilder myStringBuilder = new StringBuilder("Hello World!");


myStringBuilder.Replace('!', '?');
Console.WriteLine(myStringBuilder);
// The example displays the following output:
// Hello World?

Convertendo um objeto StringBuilder em uma


cadeia de caracteres
Você deve converter o objeto StringBuilder em um objeto Stringpara transmitir a cadeia
de caracteres representada pelo objeto StringBuilder para um método que tem um
parâmetro String ou exibi-lo na interface do usuário. Você faz essa conversão chamando
o método StringBuilder.ToString. O exemplo a seguir chama vários métodos
StringBuilder e chama o método StringBuilder.ToString() para exibir a cadeia de
caracteres.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
StringBuilder sb = new StringBuilder();
bool flag = true;
string[] spellings = { "recieve", "receeve", "receive" };
sb.AppendFormat("Which of the following spellings is {0}:", flag);
sb.AppendLine();
for (int ctr = 0; ctr <= spellings.GetUpperBound(0); ctr++) {
sb.AppendFormat(" {0}. {1}", ctr, spellings[ctr]);
sb.AppendLine();
}
sb.AppendLine();
Console.WriteLine(sb.ToString());
}
}
// The example displays the following output:
// Which of the following spellings is True:
// 0. recieve
// 1. receeve
// 2. receive
Confira também
System.Text.StringBuilder
Operações básicas de cadeias de caracteres
Formatar tipos
Como: executar manipulações de
cadeias de caracteres básicas no .NET
Artigo • 10/05/2023

O exemplo a seguir usa alguns dos métodos discutidos nos tópicos de Operações de
cadeias de caracteres básicas para construir uma classe que realiza manipulações de
cadeia de caracteres de uma maneira que pode ser encontrada em um aplicativo real. A
classe MailToData armazena o nome e endereço de um indivíduo em propriedades
separadas e fornece uma maneira de combinar os campos City , State e Zip em uma
única cadeia de caracteres para exibição para o usuário. Além disso, a classe permite
que o usuário insira as informações de cidade, estado e CEP como uma única cadeia de
caracteres. O aplicativo analisa automaticamente a única cadeia de caracteres e insere as
informações adequadas na propriedade correspondente.

Para simplificar, este exemplo usa um aplicativo de console com uma interface de linha
de comando.

Exemplo
C#

using System;

class MainClass
{
static void Main()
{
MailToData MyData = new MailToData();

Console.Write("Enter Your Name: ");


MyData.Name = Console.ReadLine();
Console.Write("Enter Your Address: ");
MyData.Address = Console.ReadLine();
Console.Write("Enter Your City, State, and ZIP Code separated by
spaces: ");
MyData.CityStateZip = Console.ReadLine();
Console.WriteLine();

if (MyData.Validated) {
Console.WriteLine("Name: {0}", MyData.Name);
Console.WriteLine("Address: {0}", MyData.Address);
Console.WriteLine("City: {0}", MyData.City);
Console.WriteLine("State: {0}", MyData.State);
Console.WriteLine("Zip: {0}", MyData.Zip);
Console.WriteLine("\nThe following address will be used:");
Console.WriteLine(MyData.Address);
Console.WriteLine(MyData.CityStateZip);
}
}
}

public class MailToData


{
string name = "";
string address = "";
string citystatezip = "";
string city = "";
string state = "";
string zip = "";
bool parseSucceeded = false;

public string Name


{
get{return name;}
set{name = value;}
}

public string Address


{
get{return address;}
set{address = value;}
}

public string CityStateZip


{
get {
return String.Format("{0}, {1} {2}", city, state, zip);
}
set {
citystatezip = value.Trim();
ParseCityStateZip();
}
}

public string City


{
get{return city;}
set{city = value;}
}

public string State


{
get{return state;}
set{state = value;}
}

public string Zip


{
get{return zip;}
set{zip = value;}
}

public bool Validated


{
get { return parseSucceeded; }
}

private void ParseCityStateZip()


{
string msg = "";
const string msgEnd = "\nYou must enter spaces between city, state,
and zip code.\n";

// Throw a FormatException if the user did not enter the necessary


spaces
// between elements.
try
{
// City may consist of multiple words, so we'll have to parse the
// string from right to left starting with the zip code.
int zipIndex = citystatezip.LastIndexOf(" ");
if (zipIndex == -1) {
msg = "\nCannot identify a zip code." + msgEnd;
throw new FormatException(msg);
}
zip = citystatezip.Substring(zipIndex + 1);

int stateIndex = citystatezip.LastIndexOf(" ", zipIndex - 1);


if (stateIndex == -1) {
msg = "\nCannot identify a state." + msgEnd;
throw new FormatException(msg);
}
state = citystatezip.Substring(stateIndex + 1, zipIndex -
stateIndex - 1);
state = state.ToUpper();

city = citystatezip.Substring(0, stateIndex);


if (city.Length == 0) {
msg = "\nCannot identify a city." + msgEnd;
throw new FormatException(msg);
}
parseSucceeded = true;
}
catch (FormatException ex)
{
Console.WriteLine(ex.Message);
}
}

private string ReturnCityStateZip()


{
// Make state uppercase.
state = state.ToUpper();
// Put the value of city, state, and zip together in the proper
manner.
string MyCityStateZip = String.Concat(city, ", ", state, " ", zip);

return MyCityStateZip;
}
}

Quando o código anterior é executado, é solicitado que o usuário insira seu nome e
endereço. O aplicativo coloca as informações nas propriedades adequadas e as exibe
novamente ao usuário, criando uma única cadeia de caracteres que exibe as
informações de cidade, estado e CEP.

Confira também
Operações básicas de cadeias de caracteres
Analisar cadeias de caracteres no .NET
Artigo • 10/05/2023

Uma operação de análise converte uma cadeia de caracteres que representa um tipo
base .NET nesse tipo base. Por exemplo, uma operação de análise é usada para
converter uma cadeia de caracteres em um número de ponto flutuante ou em um valor
de data e hora. O método usado com mais frequência para executar uma operação de
análise é o método Parse . Como a análise é a operação inversa da formatação (que
envolve a conversão de um tipo base em sua representação de cadeia de caracteres),
muitas das mesmas regras e convenções se aplicam. Assim como a formatação usa um
objeto que implementa a interface IFormatProvider para fornecer informações de
formatação que diferenciam a cultura, a análise também usa um objeto que implementa
a interface IFormatProvider para determinar como interpretar uma representação de
cadeia de caracteres. Para saber mais, confira Formatar tipos.

Nesta seção
Analisando cadeias de caracteres numéricas
Descreve como converter cadeias de caracteres em tipos numéricos do .NET.

Analisando cadeias de caracteres de data e hora


Descreve como converter cadeias de caracteres em tipos DateTime do .NET.

Analisando outras cadeias de caracteres


Descreve como converter cadeias de caracteres em tipos Char, Boolean e Enum.

Seções relacionadas
Formatar tipos
Descreve conceitos básicos de formatação, como especificadores de formato e
provedores de formato.

Conversão de tipo no .NET


Descreve como converter tipos.
Analisando cadeias de caracteres
numéricas no .NET
Artigo • 10/05/2023

Todos os tipos numéricos têm dois métodos de análise estáticos, Parse e TryParse , que
podem ser usados para converter a representação de cadeia de caracteres de um
número em um tipo numérico. Esses métodos permitem analisar cadeias de caracteres
que foram produzidas usando as cadeias de caracteres de formato documentadas em
Cadeias de caracteres de formato numérico padrão e Cadeias de caracteres de formato
numérico personalizadas. Por padrão, os métodos Parse e TryParse conseguem
converter cadeias de caracteres que contêm dígitos decimais integrais somente em
valores inteiros. Podem converter cadeias de caracteres que contêm dígitos decimais
integrais e dígitos fracionários, separadores de grupo e um separador decimal em
valores de ponto flutuante. O método Parse lança uma exceção se a operação falhar,
enquanto o método TryParse retorna false .

7 Observação

Do .NET 7 em diante, os tipos numéricos no .NET também implementam a interface


System.IParsable<TSelf>, que define os métodos IParsable<TSelf>.Parse e
IParsable<TSelf>.TryParse.

Provedores de análise e formato


Normalmente, as representações de cadeia de caracteres de valores numéricos diferem
por cultura. Elementos de separadores de cadeias de caracteres numéricas, como
símbolos de moeda, separadores de grupo (ou milhares) e separadores decimais, variam
por cultura. Os métodos de análise usam, implícita ou explicitamente, um provedor de
formato que reconhece essas variações específicas da cultura. Se nenhum provedor de
formato for especificado em uma chamada ao método Parse ou TryParse , será usado o
provedor de formato associado à cultura atual (o objeto NumberFormatInfo retornado
pela propriedade NumberFormatInfo.CurrentInfo).

Um provedor de formato é representado por uma implementação IFormatProvider. Essa


interface tem um único membro, o método GetFormat, cujo único parâmetro é um
objeto Type que representa o tipo a ser formatado. Esse método retorna um objeto que
fornece informações de formatação. O .NET dá suporte às duas implementações
IFormatProvider a seguir para analisar cadeias de caracteres numéricas:
Um objeto CultureInfo cujo método CultureInfo.GetFormat retorna um objeto
NumberFormatInfo que fornece informações de formatação específicas da cultura.

Um objeto NumberFormatInfo cujo método NumberFormatInfo.GetFormat retorna


a si mesmo.

O exemplo a seguir tenta converter cada cadeia de caracteres em uma matriz em um


valor Double. Em primeiro lugar, tenta analisar a cadeia de caracteres usando um
provedor de formato que reflete as convenções da cultura do inglês (Estados Unidos).
Se essa operação lançar um FormatException, tentará analisar a cadeia de caracteres
usando um provedor de formato que reflita as convenções da cultura de francês
(França).

C#

using System;
using System.Globalization;

public class Example


{
public static void Main()
{
string[] values = { "1,304.16", "$1,456.78", "1,094", "152",
"123,45 €", "1 304,16", "Ae9f" };
double number;
CultureInfo culture = null;

foreach (string value in values) {


try {
culture = CultureInfo.CreateSpecificCulture("en-US");
number = Double.Parse(value, culture);
Console.WriteLine("{0}: {1} --> {2}", culture.Name, value,
number);
}
catch (FormatException) {
Console.WriteLine("{0}: Unable to parse '{1}'.",
culture.Name, value);
culture = CultureInfo.CreateSpecificCulture("fr-FR");
try {
number = Double.Parse(value, culture);
Console.WriteLine("{0}: {1} --> {2}", culture.Name, value,
number);
}
catch (FormatException) {
Console.WriteLine("{0}: Unable to parse '{1}'.",
culture.Name, value);
}
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// en-US: 1,304.16 --> 1304.16
//
// en-US: Unable to parse '$1,456.78'.
// fr-FR: Unable to parse '$1,456.78'.
//
// en-US: 1,094 --> 1094
//
// en-US: 152 --> 152
//
// en-US: Unable to parse '123,45 €'.
// fr-FR: Unable to parse '123,45 €'.
//
// en-US: Unable to parse '1 304,16'.
// fr-FR: 1 304,16 --> 1304.16
//
// en-US: Unable to parse 'Ae9f'.
// fr-FR: Unable to parse 'Ae9f'.

Análise e valores NumberStyles


Os elementos de estilo (como espaço em branco, separadores de grupo e separador
decimal) que a operação de análise pode manipular são definidos por um valor de
enumeração NumberStyles. Por padrão, as cadeias de caracteres que representam
valores inteiros são analisadas usando o valor NumberStyles.Integer, que permite
apenas dígitos numéricos, espaço em branco à esquerda e à direita e um sinal à
esquerda. Cadeias de caracteres que representam valores de ponto flutuante são
analisadas usando uma combinação dos valores NumberStyles.Float e
NumberStyles.AllowThousands. Esse estilo de composição permite dígitos decimais
junto com o espaço em branco à esquerda e à direita, um sinal à esquerda, um
separador decimal, um separador de grupo e um expoente. Chamando uma sobrecarga
do método Parse ou TryParse que inclui um parâmetro do tipo NumberStyles e
definindo um ou mais sinalizadores NumberStyles, é possível controlar os elementos de
estilo que podem estar presentes na cadeia de caracteres para que a operação de
análise seja bem-sucedida.

Por exemplo, uma cadeia de caracteres que contém um separador de grupo não pode
ser convertida em um valor Int32 usando o método Int32.Parse(String). No entanto, a
conversão será bem-sucedida se você usar o sinalizador NumberStyles.AllowThousands,
como mostra o exemplo a seguir.

C#

using System;
using System.Globalization;
public class Example
{
public static void Main()
{
string value = "1,304";
int number;
IFormatProvider provider = CultureInfo.CreateSpecificCulture("en-US");
if (Int32.TryParse(value, out number))
Console.WriteLine("{0} --> {1}", value, number);
else
Console.WriteLine("Unable to convert '{0}'", value);

if (Int32.TryParse(value, NumberStyles.Integer |
NumberStyles.AllowThousands,
provider, out number))
Console.WriteLine("{0} --> {1}", value, number);
else
Console.WriteLine("Unable to convert '{0}'", value);
}
}
// The example displays the following output:
// Unable to convert '1,304'
// 1,304 --> 1304

2 Aviso

A operação de análise sempre usa as convenções de formatação de uma


determinada cultura. Se você não especificar uma cultura passando um objeto
CultureInfo ou NumberFormatInfo, a cultura associada ao thread atual será usada.

A tabela a seguir lista os membros da enumeração NumberStyles e descreve o efeito


que eles têm sobre a operação de análise.

Valor NumberStyles Efeito sobre a cadeia de caracteres a ser analisada

NumberStyles.None São permitidos apenas dígitos numéricos.

NumberStyles.AllowDecimalPoint O separador decimal e dígitos fracionários são permitidos.


Para valores inteiros, apenas zero é permitido como um
dígito fracionário. Os separadores decimais válidos são
determinados pela propriedade
NumberFormatInfo.NumberDecimalSeparator ou
NumberFormatInfo.CurrencyDecimalSeparator.

NumberStyles.AllowExponent O caractere “e” ou “E” pode ser usado para indicar notação
exponencial. Para obter mais informações, consulte
NumberStyles.
Valor NumberStyles Efeito sobre a cadeia de caracteres a ser analisada

NumberStyles.AllowLeadingWhite É permitido um espaço em branco à esquerda.

NumberStyles.AllowTrailingWhite É permitido um espaço em branco à direita.

NumberStyles.AllowLeadingSign Um sinal positivo ou negativo pode anteceder dígitos


numéricos.

NumberStyles.AllowTrailingSign Um sinal positivo ou negativo pode seguir dígitos


numéricos.

NumberStyles.AllowParentheses Parênteses podem ser usados para indicar valores


negativos.

NumberStyles.AllowThousands É permitido um separador de grupo. O caractere separador


de grupo é determinado pela propriedade
NumberFormatInfo.NumberGroupSeparator ou
NumberFormatInfo.CurrencyGroupSeparator.

NumberStyles.AllowCurrencySymbol É permitido o símbolo de moeda. O símbolo de moeda é


definido pela propriedade
NumberFormatInfo.CurrencySymbol.

NumberStyles.AllowHexSpecifier A cadeia de caracteres a ser analisada é interpretada como


um número hexadecimal. Pode incluir os dígitos decimais
0-9, A-F e a-f. Este sinalizador pode ser usado apenas para
analisar valores inteiros.

Além disso, a enumeração NumberStyles oferece os estilos de composição a seguir, que


incluem vários sinalizadores NumberStyles.

Valor NumberStyles de Inclui membros


composição

NumberStyles.Integer Inclui os estilos NumberStyles.AllowLeadingWhite,


NumberStyles.AllowTrailingWhite e NumberStyles.AllowLeadingSign.
É o estilo padrão usado para analisar valores inteiros.

NumberStyles.Number Inclui os estilos NumberStyles.AllowLeadingWhite,


NumberStyles.AllowTrailingWhite, NumberStyles.AllowLeadingSign,
NumberStyles.AllowTrailingSign, NumberStyles.AllowDecimalPoint e
NumberStyles.AllowThousands.

NumberStyles.Float Inclui os estilos NumberStyles.AllowLeadingWhite,


NumberStyles.AllowTrailingWhite, NumberStyles.AllowLeadingSign,
NumberStyles.AllowDecimalPoint e NumberStyles.AllowExponent.

NumberStyles.Currency Inclui todos os estilos, exceto NumberStyles.AllowExponent e


NumberStyles.AllowHexSpecifier.
Valor NumberStyles de Inclui membros
composição

NumberStyles.Any Inclui todos os estilos, exceto NumberStyles.AllowHexSpecifier.

NumberStyles.HexNumber Inclui os estilos NumberStyles.AllowLeadingWhite,


NumberStyles.AllowTrailingWhite e NumberStyles.AllowHexSpecifier.

Análise e Dígitos Unicode


O padrão Unicode define pontos de código para dígitos em diversos sistemas de escrita.
Por exemplo, os pontos de código de U+0030 a U+0039 representam os dígitos latinos
básicos 0 a 9; os pontos de código de U+09E6 a U+09EF representam os dígitos bengali
de 0 a 9; e os pontos de código de U+FF10 a U+FF19 representam os dígitos de largura
inteira 0 a 9. No entanto, os únicos dígitos numéricos reconhecidos pelos métodos de
análise são os dígitos latinos básicos 0-9 com os pontos de código de U+0030 a
U+0039. Se um método de análise numérica passar uma cadeia de caracteres que
contenha outros dígitos, o método lançará um FormatException.

O exemplo a seguir usa o método Int32.Parse para analisar cadeias de caracteres que
consistem em dígitos em diferentes sistemas de escrita. Como mostra a saída do
exemplo, a tentativa de analisar os dígitos latinos básicos é bem-sucedida, mas a
tentativa de analisar os dígitos de largura inteira, indo-arábicos e bengali.

C#

using System;

public class Example


{
public static void Main()
{
string value;
// Define a string of basic Latin digits 1-5.
value = "\u0031\u0032\u0033\u0034\u0035";
ParseDigits(value);

// Define a string of Fullwidth digits 1-5.


value = "\uFF11\uFF12\uFF13\uFF14\uFF15";
ParseDigits(value);

// Define a string of Arabic-Indic digits 1-5.


value = "\u0661\u0662\u0663\u0664\u0665";
ParseDigits(value);

// Define a string of Bangla digits 1-5.


value = "\u09e7\u09e8\u09e9\u09ea\u09eb";
ParseDigits(value);
}

static void ParseDigits(string value)


{
try {
int number = Int32.Parse(value);
Console.WriteLine("'{0}' --> {1}", value, number);
}
catch (FormatException) {
Console.WriteLine("Unable to parse '{0}'.", value);
}
}
}
// The example displays the following output:
// '12345' --> 12345
// Unable to parse '12345'.
// Unable to parse '١٢٣٤٥'.
// Unable to parse '১২৩৪৫'.

Confira também
NumberStyles
Analisando cadeias de caracteres
Formatar tipos
Analisar cadeias de caracteres de data e
hora no .NET
Artigo • 10/05/2023

A análise de cadeias de caracteres para convertê-las em objetos DateTime exige que


você especifique as informações de como as datas e horas são representadas como
texto. Diferentes culturas usam ordens diferentes para dia, mês e ano. Algumas
representações usam o relógio de 24 horas, outras especificam "AM" e "PM". Alguns
aplicativos precisam somente da data. Outros precisam apenas da hora. Há também
outros que precisam especificar tanto a data quanto a hora. Os métodos que convertem
cadeias de caracteres em objetos DateTime permitem que você forneça informações
detalhadas sobre os formatos esperados e os elementos de uma data e hora que seu
aplicativo precisa. Há três subtarefas para converter corretamente o texto em um
DateTime:

1. Você deve especificar o formato esperado do texto que representa a data e hora.
2. Você pode especificar a cultura para o formato de data/hora.
3. É possível especificar como os componentes ausentes na representação de texto
serão definidos na data e hora.

Os métodos Parse e TryParse convertem muitas representações comuns de data e hora.


Os métodos ParseExact e TryParseExact convertem uma representação em cadeia de
caracteres que cumpra o padrão especificado por uma cadeia de caracteres de formato
de data e hora. Para mais informações, consulte os artigos sobre cadeias de caracteres
de formato de data e hora padrão e cadeias de caracteres de formato de data e hora
personalizadas.

O objeto DateTimeFormatInfo atual fornece mais controle sobre como o texto deve ser
interpretado como data e hora. As propriedades de um DateTimeFormatInfo descrevem
os separadores de data e hora e os nomes de meses, dias e eras, bem como o formato
das designações "AM" e "PM". O CultureInfo retornado por CultureInfo.CurrentCulture
tem uma propriedade CultureInfo.DateTimeFormat que representa a cultura atual. Se
você quer uma cultura específica ou configurações personalizadas, é necessário
especificar o parâmetro IFormatProvider de um método de análise. Para o parâmetro
IFormatProvider, especifique um objeto CultureInfo, o qual representa uma cultura ou
um objeto DateTimeFormatInfo.

O texto que representa uma data ou hora pode ter algumas informações ausentes. Por
exemplo, a maioria das pessoas supõe que a data "12 de março" representa o ano atual.
Da mesma forma, "Março de 2018" representa o mês de março do ano 2018. O texto
que representa a hora geralmente só inclui horas, minutos e uma designação de
AM/PM. Os métodos de análise lidam com essas informações ausentes usando alguns
padrões razoáveis:

Quando apenas a hora estiver presente, a parte de data usará a data atual.
Quando houver apenas uma data, a parte das horas será meia-noite.
Quando o ano não for especificado em uma data, o ano atual será usado.
Quando não é especificado o dia do mês, o primeiro dia do mês será usado.

Se a data estiver presente na cadeia de caracteres, ele deverá incluir o mês e, pelo
menos, o dia ou o ano. Se a hora estiver presente, ela deverá incluir a hora e os minutos
ou o designador AM/PM.

Você pode especificar a constante NoCurrentDateDefault para substituir esses padrões.


Ao usar essa constante, as propriedades ausentes de ano, mês ou dia serão definidas
com o valor 1 . O último exemplo usando Parse demonstra esse comportamento.

Além de um componente de data e hora, a representação de cadeia de caracteres de


data e hora pode incluir um deslocamento que indique a diferença de tempo em
relação ao UTC (Tempo Universal Coordenado). Por exemplo, a cadeia de caracteres
“2/14/2007 5:32:00 -7:00” define um horário sete horas antes do UTC. Se um
deslocamento for omitido da representação de cadeia de caracteres de um horário, a
análise retornará um DateTime do objeto com a propriedade Kind definida como
DateTimeKind.Unspecified. Se for especificada uma diferença, a análise retorna um
objeto DateTime com sua propriedade Kind definida como DateTimeKind.Local. Seu
valor também é ajustado para o fuso horário local do computador. É possível modificar
esse comportamento usando um valor DateTimeStyles com o método de análise.

O provedor de formato também é usado para interpretar uma data numérica ambígua.
Não está claro quais componentes da data representada pela cadeia de caracteres
"02/03/04" são mês, dia e ano. Os componentes são interpretados de acordo com a
ordem dos formatos de data semelhantes no provedor de formato.

Analisar
O exemplo a seguir ilustra o uso do método DateTime.Parse para converter uma string
em um DateTime. Este exemplo usa a cultura associada com o thread atual. Se
CultureInfo associado à cultura atual não puder analisar a cadeia de caracteres de
entrada, será gerado FormatException.

 Dica
Todos os exemplos de C# neste artigo são executados no navegador. Pressione o
botão Executar para ver a saída. Você também pode editá-los para experimentar
como quiser.

7 Observação

Esses exemplos estão disponíveis no repositório de documentos do GitHub para


C# e Visual Basic .

C#

string dateInput = "Jan 1, 2009";


var parsedDate = DateTime.Parse(dateInput);
Console.WriteLine(parsedDate);
// Displays the following output on a system whose culture is en-US:
// 1/1/2009 00:00:00

É possível definir explicitamente a cultura cujas convenções de formatação serão usadas


ao analisar uma cadeia de caracteres. Você especifica um dos objetos
DateTimeFormatInfo padrão retornados pela propriedade CultureInfo.DateTimeFormat.
O exemplo seguir usa um provedor de formato para analisar uma cadeia de caracteres
em alemão em um DateTime. Ele cria um CultureInfo que representa a cultura de-DE .
Esse objeto CultureInfo garante o sucesso da análise dessa cadeia de caracteres
específica. Este processo impede que qualquer configuração esteja no CurrentCulture
do CurrentThread.

C#

var cultureInfo = new CultureInfo("de-DE");


string dateString = "12 Juni 2008";
var dateTime = DateTime.Parse(dateString, cultureInfo);
Console.WriteLine(dateTime);
// The example displays the following output:
// 6/12/2008 00:00:00

No entanto, você pode usar sobrecargas do método Parse para especificar provedores
de formato personalizados. O método Parse não oferece suporte à análise de formatos
não padrão. Para analisar data e hora expressadas em um formato não padrão, use o
método ParseExact.

O exemplo a seguir usa a enumeração DateTimeStylespara especificar que as


informações de data e hora atuais não devem ser adicionadas a DateTime nos campos
não especificados.
C#

var cultureInfo = new CultureInfo("de-DE");


string dateString = "12 Juni 2008";
var dateTime = DateTime.Parse(dateString, cultureInfo,
DateTimeStyles.NoCurrentDateDefault);
Console.WriteLine(dateTime);
// The example displays the following output if the current culture is en-
US:
// 6/12/2008 00:00:00

ParseExact
O método DateTime.ParseExact converte uma cadeia de caracteres em um objeto
DateTime se ele estiver em conformidade com um dos padrões da cadeia de caracteres
especificada. Quando uma cadeia de caracteres que não é de uma das formas
especificadas é aprovada para esse método, é gerado FormatException. Você pode
especificar um dos especificadores de formato de data e hora padrão ou uma
combinação dos especificadores de formato personalizados. Usando os especificadores
de formato personalizados, é possível construir uma cadeia de caracteres de
reconhecimento personalizada. Para uma explicação dos especificadores, consulte os
tópicos cadeias de caracteres de formato de data e hora padrão e cadeias de caracteres
de formato de data e hora personalizadas.

No exemplo a seguir, o método DateTime.ParseExact recebe um objeto de cadeia de


caracteres para analisar, seguido por um especificador de formato, seguido por um
objeto CultureInfo. Esse método ParseExact só consegue analisar cadeias de caracteres
que seguem o padrão de data por extenso na cultura en-US .

C#

var cultureInfo = new CultureInfo("en-US");


string[] dateStrings = { " Friday, April 10, 2009", "Friday, April 10, 2009"
};
foreach (string dateString in dateStrings)
{
try
{
var dateTime = DateTime.ParseExact(dateString, "D", cultureInfo);
Console.WriteLine(dateTime);
}
catch (FormatException)
{
Console.WriteLine("Unable to parse '{0}'", dateString);
}
}
// The example displays the following output:
// Unable to parse ' Friday, April 10, 2009'
// 4/10/2009 00:00:00

Cada sobrecarga dos métodos Parse e ParseExact também tem um parâmetro


IFormatProvider que oferece informações específicas da cultura sobre a formatação da
cadeia de caracteres. Esse objeto IFormatProvider é um objeto CultureInfo que
representa uma cultura padrão ou um objeto DateTimeFormatInfo que retorna pela
propriedade CultureInfo.DateTimeFormat. O ParseExact também usa uma cadeia de
caracteres adicional ou um argumento de matriz de cadeia de caracteres que define um
ou mais formatos de data e hora personalizados.

Confira também
Analisando cadeias de caracteres
Formatando tipos
Conversão de tipo no .NET
Formatos de data e hora padrão
Cadeias de caracteres de formato de data e hora personalizado
Analisando outras cadeias de caracteres
no .NET
Artigo • 10/05/2023

Além das cadeias de caracteres numéricas e DateTime, também é possível analisar


cadeias de caracteres que representam os tipos CharBoolean eEnum em tipos de dados.

Char
O método de análise estática associado ao tipo de dados Char é útil para converter uma
cadeia de caracteres que contém um único caractere em seu valor Unicode. O exemplo
de código a seguir analisa uma cadeia de caracteres em um caractere Unicode.

C#

string MyString1 = "A";


char MyChar = Char.Parse(MyString1);
// MyChar now contains a Unicode "A" character.

Booliano
O tipo de dados Booliano contém um método Parse que pode ser usado para converter
uma cadeia de caracteres que representa um valor Booliano em um tipo Booliano real.
Esse método não diferencia maiúsculas de minúsculas e pode analisar com êxito uma
cadeia de caracteres que contém "True" ou "False". O método Parse associado ao tipo
booliano também pode analisar cadeias de caracteres que estão cercadas por espaços
em branco. Se qualquer outra cadeia de caracteres for passada, um FormatException
será gerado.

O exemplo de código a seguir usa o método Parse para converter uma cadeia de
caracteres em um valor Booliano.

C#

string MyString2 = "True";


bool MyBool = bool.Parse(MyString2);
// MyBool now contains a True Boolean value.

Enumeração
Você pode usar o método estático Parse para inicializar um tipo de enumeração para o
valor de uma cadeia de caracteres. Esse método aceita o tipo de enumeração que você
estiver analisando, a cadeia de caracteres a analisar e um sinalizador Booliano opcional
que indica se a análise diferencia maiúsculas de minúsculas ou não. A cadeia de
caracteres que você está analisando pode conter diversos valores separados por
vírgulas, que podem ser antecedidos ou seguidos por um ou mais espaços vazios
(também chamados de espaços em branco). Quando a cadeia de caracteres contém
diversos valores, o valor do objeto retornado é o valor de todos os valores especificados
combinado com uma operação OR bit a bit.

O exemplo a seguir usa o método Parse para converter uma representação de cadeia de
caracteres em um valor de enumeração. A enumeração DayOfWeek é inicializada para
Quinta-feira de uma cadeia de caracteres.

C#

string MyString3 = "Thursday";


DayOfWeek MyDays = (DayOfWeek)Enum.Parse(typeof(DayOfWeek), MyString3);
Console.WriteLine(MyDays);
// The result is Thursday.

Confira também
Analisando cadeias de caracteres
Formatar tipos
Conversão de tipo no .NET
System.String classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Uma cadeia de caracteres é uma coleção sequencial de caracteres usada para


representar texto. Um String objeto é uma coleção sequencial de objetos que
representam uma cadeia de caracteres, um System.Char objeto corresponde a uma
unidade de System.Char código UTF-16. O valor do String objeto é o conteúdo da
coleção sequencial de System.Char objetos, e esse valor é imutável (ou seja, é somente
leitura). Para obter mais informações sobre a imutabilidade de cadeias de caracteres,
consulte a seção Imutabilidade e a classe StringBuilder. O tamanho máximo de um
String objeto na memória é de 2 GB, ou cerca de 1 bilhão de caracteres.

Para obter mais informações sobre Unicode, UTF-16, unidades de código, pontos de
código e os tipos e Rune eChar, consulte Introdução à codificação de caracteres no
.NET.

Instanciar um objeto String


Você pode instanciar um String objeto das seguintes maneiras:

Atribuindo um literal de cadeia de caracteres a uma String variável. Esse é o


método mais comumente usado para criar uma cadeia de caracteres. O exemplo a
seguir usa atribuição para criar várias cadeias de caracteres. Observe que em C# e
F#, como a barra invertida (\) é um caractere de escape, barras invertidas literais
em uma cadeia de caracteres devem ser escapadas ou a cadeia inteira deve ser @-
quoted.

C#

string string1 = "This is a string created by assignment.";


Console.WriteLine(string1);
string string2a = "The path is C:\\PublicDocuments\\Report1.doc";
Console.WriteLine(string2a);
string string2b = @"The path is C:\PublicDocuments\Report1.doc";
Console.WriteLine(string2b);
// The example displays the following output:
// This is a string created by assignment.
// The path is C:\PublicDocuments\Report1.doc
// The path is C:\PublicDocuments\Report1.doc
Chamando um construtor de String classe. O exemplo a seguir instancia cadeias de
caracteres chamando vários construtores de classe. Observe que alguns dos
construtores incluem ponteiros para matrizes de caracteres ou matrizes de bytes
assinadas como parâmetros. Visual Basic não oferece suporte a chamadas para
esses construtores. Para obter informações detalhadas sobre String construtores,
consulte o resumo do String construtor.

C#

char[] chars = { 'w', 'o', 'r', 'd' };


sbyte[] bytes = { 0x41, 0x42, 0x43, 0x44, 0x45, 0x00 };

// Create a string from a character array.


string string1 = new string(chars);
Console.WriteLine(string1);

// Create a string that consists of a character repeated 20 times.


string string2 = new string('c', 20);
Console.WriteLine(string2);

string stringFromBytes = null;


string stringFromChars = null;
unsafe
{
fixed (sbyte* pbytes = bytes)
{
// Create a string from a pointer to a signed byte array.
stringFromBytes = new string(pbytes);
}
fixed (char* pchars = chars)
{
// Create a string from a pointer to a character array.
stringFromChars = new string(pchars);
}
}
Console.WriteLine(stringFromBytes);
Console.WriteLine(stringFromChars);
// The example displays the following output:
// word
// cccccccccccccccccccc
// ABCDE
// word

Usando o operador de concatenação de cadeia de caracteres (+ em C# e F# e &


ou + no Visual Basic) para criar uma única cadeia de caracteres a partir de
qualquer combinação de instâncias e literais de cadeia de String caracteres. O
exemplo a seguir ilustra o uso do operador de concatenação de cadeia de
caracteres.
C#

string string1 = "Today is " + DateTime.Now.ToString("D") + ".";


Console.WriteLine(string1);

string string2 = "This is one sentence. " + "This is a second. ";


string2 += "This is a third sentence.";
Console.WriteLine(string2);
// The example displays output like the following:
// Today is Tuesday, July 06, 2011.
// This is one sentence. This is a second. This is a third sentence.

Recuperando uma propriedade ou chamando um método que retorna uma cadeia


de caracteres. O exemplo a seguir usa os String métodos da classe para extrair
uma subcadeia de caracteres de uma cadeia de caracteres maior.

C#

string sentence = "This sentence has five words.";


// Extract the second word.
int startPosition = sentence.IndexOf(" ") + 1;
string word2 = sentence.Substring(startPosition,
sentence.IndexOf(" ", startPosition)
- startPosition);
Console.WriteLine("Second word: " + word2);
// The example displays the following output:
// Second word: sentence

Chamando um método de formatação para converter um valor ou objeto em sua


representação de cadeia de caracteres. O exemplo a seguir usa o recurso de
formatação composta para incorporar a representação de cadeia de caracteres de
dois objetos em uma cadeia de caracteres.

C#

DateTime dateAndTime = new DateTime(2011, 7, 6, 7, 32, 0);


double temperature = 68.3;
string result = String.Format("At {0:t} on {0:D}, the temperature was
{1:F1} degrees Fahrenheit.",
dateAndTime, temperature);
Console.WriteLine(result);
// The example displays the following output:
// At 7:32 AM on Wednesday, July 06, 2011, the temperature was
68.3 degrees Fahrenheit.

Objetos Char e caracteres Unicode


Cada caractere em uma cadeia de caracteres é definido por um valor escalar Unicode,
também chamado de ponto de código Unicode ou o valor ordinal (numérico) do
caractere Unicode. Cada ponto de código é codificado usando a codificação UTF-16 e o
valor numérico de cada elemento da codificação é representado por um Char objeto.

7 Observação

Observe que, como uma instância consiste em uma coleção sequencial de unidades
de código UTF-16, é possível criar um String objeto que não seja uma String cadeia
de caracteres Unicode bem formada. Por exemplo, é possível criar uma cadeia de
caracteres que tenha um substituto baixo sem um substituto alto correspondente.
Embora alguns métodos, como os métodos de codificação e decodificação de
objetos no System.Text namespace, possam executar verificações para garantir que
as cadeias de caracteres sejam bem formadas, String os membros da classe não
garantem que uma cadeia de caracteres esteja bem formada.

Um único objeto geralmente representa um único Char ponto de código, ou seja, o


valor numérico do Char é igual ao ponto de código. Por exemplo, o ponto de código
para o caractere "a" é U+0061. No entanto, um ponto de código pode exigir mais de
um elemento codificado (mais de um Char objeto). O padrão Unicode define dois tipos
de caracteres que correspondem a vários Char objetos: grafemas e pontos de código
suplementar Unicode que correspondem a caracteres nos planos suplementares
Unicode.

Um grafema é representado por um caractere base seguido por um ou mais


caracteres combinados. Por exemplo, o caractere ä é representado por um objeto
cujo ponto de código é U+0061 seguido por um CharChar objeto cujo ponto de
código é U+0308. Esse caractere também pode ser definido por um único Char
objeto que tem um ponto de código de U+00E4. Como mostra o exemplo a
seguir, uma comparação sensível à cultura para igualdade indica que essas duas
representações são iguais, embora uma comparação ordinal comum não. No
entanto, se as duas cadeias de caracteres forem normalizadas, uma comparação
ordinal também indicará que elas são iguais. (Para obter mais informações sobre
como normalizar cadeias de caracteres, consulte o Seção de normalização .)

C#

using System;
using System.Globalization;
using System.IO;

public class Example5


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\graphemes.txt");
string grapheme = "\u0061\u0308";
sw.WriteLine(grapheme);

string singleChar = "\u00e4";


sw.WriteLine(singleChar);

sw.WriteLine("{0} = {1} (Culture-sensitive): {2}", grapheme,


singleChar,
String.Equals(grapheme, singleChar,
StringComparison.CurrentCulture));
sw.WriteLine("{0} = {1} (Ordinal): {2}", grapheme, singleChar,
String.Equals(grapheme, singleChar,
StringComparison.Ordinal));
sw.WriteLine("{0} = {1} (Normalized Ordinal): {2}", grapheme,
singleChar,
String.Equals(grapheme.Normalize(),
singleChar.Normalize(),
StringComparison.Ordinal));
sw.Close();
}
}
// The example produces the following output:
// ä
// ä
// ä = ä (Culture-sensitive): True
// ä = ä (Ordinal): False
// ä = ä (Normalized Ordinal): True

Um ponto de código suplementar Unicode (um par substituto) é representado por


um objeto cujo ponto de código é um substituto alto seguido por um objeto cujo
ponto de código é um CharChar substituto baixo. As unidades de código de
substitutos altos variam de U+D800 a U+DBFF. As unidades de código de
substitutos baixos variam de U+DC00 a U+DFFF. Pares substitutos são usados para
representar caracteres nos 16 planos suplementares Unicode. O exemplo a seguir
cria um caractere substituto e o passa para o método para determinar se ele é um
par substituto Char.IsSurrogatePair(Char, Char) .

C#

string surrogate = "\uD800\uDC03";


for (int ctr = 0; ctr < surrogate.Length; ctr++)
Console.Write($"U+{(ushort)surrogate[ctr]:X2} ");

Console.WriteLine();
Console.WriteLine(" Is Surrogate Pair: {0}",
Char.IsSurrogatePair(surrogate[0], surrogate[1]));
// The example displays the following output:
// U+D800 U+DC03
// Is Surrogate Pair: True

O padrão Unicode
Os caracteres em uma cadeia de caracteres são representados por unidades de código
codificadas UTF-16, que correspondem a Char valores.

Cada caractere em uma cadeia de caracteres tem uma categoria de caractere Unicode
associada, que é representada no .NET pela UnicodeCategory enumeração. A categoria
de um caractere ou de um par substituto pode ser determinada chamando o
CharUnicodeInfo.GetUnicodeCategory método.

O .NET mantém sua própria tabela de caracteres e suas categorias correspondentes, o


que garante que uma versão específica de uma implementação do .NET em execução
em diferentes plataformas retorne informações de categoria de caracteres idênticos. Em
todas as versões do .NET e em todas as plataformas do sistema operacional, as
informações de categoria de caracteres são fornecidas pelo Banco de Dados de
Caracteres Unicode.

A tabela a seguir lista as versões do .NET e as versões do Padrão Unicode nas quais suas
categorias de caracteres são baseadas.

ノ Expandir a tabela

Versão do .NET Versão do Padrão Unicode

.NET Framework 1.1 O Padrão Unicode, versão 4.0.0

.NET Framework 2.0 O Padrão Unicode, versão 5.0.0

.NET Framework 3.5 O Padrão Unicode, versão 5.0.0

.NET Framework 4 O Padrão Unicode, versão 5.0.0

.NET Framework 4.5 O Padrão Unicode, versão 6.3.0

.NET Framework 4.5.1 O Padrão Unicode, versão 6.3.0

.NET Framework 4.5.2 O Padrão Unicode, versão 6.3.0

.NET Framework 4.6 O Padrão Unicode, versão 6.3.0

.NET Framework 4.6.1 O Padrão Unicode, versão 6.3.0

.NET Framework 4.6.2 e versões posteriores O Padrão Unicode, versão 8.0.0


Versão do .NET Versão do Padrão Unicode

.NET Core 2.1 O Padrão Unicode, versão 8.0.0

.NET Core 3.1 O padrão Unicode, versão 11.0.0

.NET 5 O padrão Unicode, versão 13.0.0

Além disso, o .NET oferece suporte à comparação e classificação de cadeia de caracteres


com base no padrão Unicode. O .NET Framework 4 e versões anteriores mantêm sua
própria tabela de dados de cadeia de caracteres. Isso também é verdadeiro para versões
do .NET Framework começando com o .NET Framework 4.5 em execução no Windows 7.
A partir do .NET Framework 4.5 em execução no Windows 8 e em versões posteriores
do sistema operacional Windows, o tempo de execução delega operações de
comparação e classificação de cadeia de caracteres ao sistema operacional. No .NET
Core e no .NET 5+, as informações de comparação e classificação de cadeias de
caracteres são fornecidas pelas bibliotecas International Components for Unicode
(exceto em versões do Windows anteriores à Atualização de maio de 2019 do Windows
10). A tabela a seguir lista as versões do .NET e as versões do Unicode Standard nas
quais a comparação e a classificação de caracteres são baseadas.

ノ Expandir a tabela

Versão do .NET Versão do Padrão Unicode

.NET Framework 1.1 O Padrão Unicode, versão 4.0.0

.NET Framework 2.0 O Padrão Unicode, versão 5.0.0

.NET Framework 3.5 O Padrão Unicode, versão 5.0.0

.NET Framework 4 O Padrão Unicode, versão 5.0.0

.NET Framework 4.5 e posterior no Windows 7 O Padrão Unicode, versão 5.0.0

.NET Framework 4.5 e posterior no Windows 8 e O Padrão Unicode, versão 6.3.0


sistemas operacionais Windows posteriores

.NET Core e .NET 5+ Depende da versão do padrão Unicode


compatível com o sistema operacional
subjacente.

Caracteres nulos incorporados


No .NET, um String objeto pode incluir caracteres nulos incorporados, que contam
como parte do comprimento da cadeia de caracteres. No entanto, em algumas
linguagens, como C e C++, um caractere nulo indica o final de uma cadeia de
caracteres; não é considerado uma parte da cadeia de caracteres e não é contado como
parte do comprimento da corda. Isso significa que as seguintes suposições comuns que
programadores ou bibliotecas C e C++ escritas em C ou C++ podem fazer sobre
cadeias de caracteres não são necessariamente válidas quando aplicadas a String
objetos:

O valor retornado pelas strlen funções ou wcslen não é necessariamente igual


String.Lengtha .

A cadeia de caracteres criada pelas strcpy_s funções ou wcscpy_s não é


necessariamente idêntica à cadeia de caracteres criada pelo String.Copy método.

Você deve garantir que o código C e C++ nativo que instancia objetos e o código que é
passado String objetos através da plataforma invocar, não assuma que um caractere
nulo incorporado marca o String final da cadeia de caracteres.

Os caracteres nulos incorporados em uma cadeia de caracteres também são tratados de


forma diferente quando uma cadeia de caracteres é classificada (ou comparada) e
quando uma cadeia de caracteres é pesquisada. Caracteres nulos são ignorados ao
executar comparações sensíveis à cultura entre duas cadeias de caracteres, incluindo
comparações usando a cultura invariante. Eles são considerados apenas para
comparações ordinais ou ordinais que não diferenciam maiúsculas de minúsculas. Por
outro lado, caracteres nulos incorporados são sempre considerados ao pesquisar uma
cadeia de caracteres com métodos como Contains, StartsWithe IndexOf.

Cadeias de caracteres e índices


Um índice é a posição de um objeto (não um caractere Unicode) em um
CharStringarquivo . Um índice é um número não negativo baseado em zero que começa
a partir da primeira posição na cadeia de caracteres, que é a posição zero do índice.
Vários métodos de pesquisa, como IndexOf e , retornam o índice de um caractere
LastIndexOfou subcadeia de caracteres na ocorrência de cadeia de caracteres.

A Chars[] propriedade permite acessar objetos individuais Char por sua posição de
índice na cadeia de caracteres. Como a propriedade é a Chars[] propriedade padrão (no
Visual Basic) ou o indexador (em C# e F#), você pode acessar os objetos individuais Char
em uma cadeia de caracteres usando código como o seguinte. Esse código procura
espaço em branco ou caracteres de pontuação em uma cadeia de caracteres para
determinar quantas palavras a cadeia de caracteres contém.

C#
string s1 = "This string consists of a single short sentence.";
int nWords = 0;

s1 = s1.Trim();
for (int ctr = 0; ctr < s1.Length; ctr++) {
if (Char.IsPunctuation(s1[ctr]) | Char.IsWhiteSpace(s1[ctr]))
nWords++;
}
Console.WriteLine("The sentence\n {0}\nhas {1} words.",
s1, nWords);
// The example displays the following output:
// The sentence
// This string consists of a single short sentence.
// has 8 words.

Como a classe implementa a StringIEnumerable interface, você também pode iterar


pelos Char objetos em uma cadeia de caracteres usando uma foreach construção,
como mostra o exemplo a seguir.

C#

string s1 = "This string consists of a single short sentence.";


int nWords = 0;

s1 = s1.Trim();
foreach (var ch in s1) {
if (Char.IsPunctuation(ch) | Char.IsWhiteSpace(ch))
nWords++;
}
Console.WriteLine("The sentence\n {0}\nhas {1} words.",
s1, nWords);
// The example displays the following output:
// The sentence
// This string consists of a single short sentence.
// has 8 words.

Valores de índice consecutivos podem não corresponder a caracteres Unicode


consecutivos, porque um caractere Unicode pode ser codificado como mais de um Char
objeto. Em particular, uma cadeia de caracteres pode conter unidades de vários
caracteres de texto que são formadas por um caractere base seguido por um ou mais
caracteres combinados ou por pares substitutos. Para trabalhar com caracteres Unicode
em vez de Char objetos, use as System.Globalization.StringInfo classes e ou o
String.EnumerateRunes método e TextElementEnumerator a Rune struct. O exemplo a
seguir ilustra a diferença entre o código que funciona com objetos e o código que
funciona com Char caracteres Unicode. Ele compara o número de caracteres ou
elementos de texto em cada palavra de uma frase. A cadeia de caracteres inclui duas
sequências de um caractere base seguidas por um caractere combinado.
C#

// First sentence of The Mystery of the Yellow Room, by Leroux.


string opening = "Ce n'est pas sans une certaine émotion que "+
"je commence à raconter ici les aventures " +
"extraordinaires de Joseph Rouletabille.";
// Character counters.
int nChars = 0;
// Objects to store word count.
List<int> chars = new List<int>();
List<int> elements = new List<int>();

foreach (var ch in opening) {


// Skip the ' character.
if (ch == '\u0027') continue;

if (Char.IsWhiteSpace(ch) | (Char.IsPunctuation(ch))) {
chars.Add(nChars);
nChars = 0;
}
else {
nChars++;
}
}

System.Globalization.TextElementEnumerator te =
System.Globalization.StringInfo.GetTextElementEnumerator(opening);
while (te.MoveNext()) {
string s = te.GetTextElement();
// Skip the ' character.
if (s == "\u0027") continue;
if ( String.IsNullOrEmpty(s.Trim()) | (s.Length == 1 &&
Char.IsPunctuation(Convert.ToChar(s)))) {
elements.Add(nChars);
nChars = 0;
}
else {
nChars++;
}
}

// Display character counts.


Console.WriteLine("{0,6} {1,20} {2,20}",
"Word #", "Char Objects", "Characters");
for (int ctr = 0; ctr < chars.Count; ctr++)
Console.WriteLine("{0,6} {1,20} {2,20}",
ctr, chars[ctr], elements[ctr]);
// The example displays the following output:
// Word # Char Objects Characters
// 0 2 2
// 1 4 4
// 2 3 3
// 3 4 4
// 4 3 3
// 5 8 8
// 6 8 7
// 7 3 3
// 8 2 2
// 9 8 8
// 10 2 1
// 11 8 8
// 12 3 3
// 13 3 3
// 14 9 9
// 15 15 15
// 16 2 2
// 17 6 6
// 18 12 12

Este exemplo trabalha com elementos de texto usando o


StringInfo.GetTextElementEnumerator método e a TextElementEnumerator classe para
enumerar todos os elementos de texto em uma cadeia de caracteres. Você também
pode recuperar uma matriz que contém o índice inicial de cada elemento de texto
chamando o StringInfo.ParseCombiningCharacters método.

Para obter mais informações sobre como trabalhar com unidades de texto em vez de
valores individuaisChar, consulte Introdução à codificação de caracteres no .NET.

Cadeias de caracteres nulas e cadeias de


caracteres vazias
Uma cadeia de caracteres que foi declarada, mas não recebeu um valor é null . A
tentativa de chamar métodos nessa cadeia de caracteres lança um
NullReferenceExceptionarquivo . Uma cadeia de caracteres nula é diferente de uma
cadeia de caracteres vazia, que é uma cadeia de caracteres cujo valor é "" ou
String.Empty. Em alguns casos, passar uma cadeia de caracteres nula ou uma cadeia de
caracteres vazia como um argumento em uma chamada de método gera uma exceção.
Por exemplo, passar uma cadeia de caracteres nula para o Int32.Parse método lança um
, e passar uma cadeia de caracteres vazia lança um
FormatExceptionArgumentNullExceptionarquivo . Em outros casos, um argumento de
método pode ser uma cadeia de caracteres nula ou uma cadeia de caracteres vazia. Por
exemplo, se você estiver fornecendo uma implementação para uma classe, você deseja
equiparar uma cadeia de caracteres nula e uma IFormattable cadeia de caracteres vazia
com o especificador de formato geral ("G").

A String classe inclui os dois métodos de conveniência a seguir que permitem testar se
uma cadeia de caracteres está null ou vazia:
IsNullOrEmpty, que indica se uma cadeia de caracteres é ou null é igual a
String.Empty. Esse método elimina a necessidade de usar código como o seguinte:

C#

if (str == null || str.Equals(String.Empty))

IsNullOrWhiteSpace, que indica se uma cadeia de caracteres é , é null igual


String.Emptya , ou consiste exclusivamente em caracteres de espaço em branco.
Esse método elimina a necessidade de usar código como o seguinte:

C#

if (str == null || str.Equals(String.Empty) ||


str.Trim().Equals(String.Empty))

O exemplo a seguir usa o IsNullOrEmptyIFormattable.ToString método na


implementação de uma classe personalizada Temperature . O método suporta as
cadeias de caracteres de formato "G", "C", "F" e "K". Se uma cadeia de caracteres de
formato vazia ou uma cadeia de caracteres de formato cujo valor é null passado para o
método, seu valor é alterado para a cadeia de caracteres de formato "G".

C#

public string ToString(string format, IFormatProvider provider)


{
if (String.IsNullOrEmpty(format)) format = "G";
if (provider == null) provider = CultureInfo.CurrentCulture;

switch (format.ToUpperInvariant())
{
// Return degrees in Celsius.
case "G":
case "C":
return temp.ToString("F2", provider) + "°C";
// Return degrees in Fahrenheit.
case "F":
return (temp * 9 / 5 + 32).ToString("F2", provider) + "°F";
// Return degrees in Kelvin.
case "K":
return (temp + 273.15).ToString();
default:
throw new FormatException(
String.Format("The {0} format string is not supported.",
format));
}
}
Imutabilidade e a classe StringBuilder
Um String objeto é chamado de imutável (somente leitura), porque seu valor não pode
ser modificado depois de criado. Os métodos que parecem modificar um objeto
retornam um String novo String objeto que contém a modificação.

Como as cadeias de caracteres são imutáveis, as rotinas de manipulação de cadeias de


caracteres que executam adições ou exclusões repetidas ao que parece ser uma única
cadeia de caracteres podem exigir uma penalidade de desempenho significativa. Por
exemplo, o código a seguir usa um gerador de números aleatórios para criar uma
cadeia de caracteres com 1000 caracteres no intervalo 0x0001 para 0x052F. Embora o
código pareça usar concatenação de cadeia de caracteres para acrescentar um novo
caractere à cadeia de caracteres existente chamada str , ele realmente cria um novo
String objeto para cada operação de concatenação.

C#

using System;
using System.IO;
using System.Text;

public class Example6


{
public static void Main()
{
Random rnd = new Random();

string str = String.Empty;


StreamWriter sw = new StreamWriter(@".\StringFile.txt",
false, Encoding.Unicode);

for (int ctr = 0; ctr <= 1000; ctr++) {


str += (char)rnd.Next(1, 0x0530);
if (str.Length % 60 == 0)
str += Environment.NewLine;
}
sw.Write(str);
sw.Close();
}
}

Você pode usar a StringBuilder classe em vez da String classe para operações que fazem
várias alterações no valor de uma cadeia de caracteres. Ao contrário das instâncias da
classe, os String objetos são mutáveis: quando você concatena, acrescenta ou exclui
subcadeias de caracteres de uma cadeia de caracteres, StringBuilder as operações são
executadas em uma única cadeia de caracteres. Quando terminar de modificar o valor
de um StringBuilder objeto, você poderá chamar seu StringBuilder.ToString método para
convertê-lo em uma cadeia de caracteres. O exemplo a seguir substitui o String usado
no exemplo anterior para concatenar 1000 caracteres aleatórios no intervalo para
0x0001 a 0x052F com um StringBuilder objeto.

C#

using System;
using System.IO;
using System.Text;

public class Example10


{
public static void Main()
{
Random rnd = new Random();
StringBuilder sb = new StringBuilder();
StreamWriter sw = new StreamWriter(@".\StringFile.txt",
false, Encoding.Unicode);

for (int ctr = 0; ctr <= 1000; ctr++) {


sb.Append((char)rnd.Next(1, 0x0530));
if (sb.Length % 60 == 0)
sb.AppendLine();
}
sw.Write(sb.ToString());
sw.Close();
}
}

Ordinal x operações sensíveis à cultura


Os membros da String classe executam operações ordinais ou sensíveis à cultura
(linguísticas) em um String objeto. Uma operação ordinal atua sobre o valor numérico
de cada Char objeto. Uma operação sensível à cultura atua sobre o String valor do
objeto e leva em consideração as regras de invólucro, classificação, formatação e análise
específicas da cultura. As operações sensíveis à cultura são executadas no contexto de
uma cultura explicitamente declarada ou da cultura atual implícita. Os dois tipos de
operações podem produzir resultados muito diferentes quando são executadas na
mesma cadeia de caracteres.

O .NET também oferece suporte a operações de cadeia de caracteres linguísticas sem


diferenciação de cultura usando a cultura invariante (CultureInfo.InvariantCulture), que é
vagamente baseada nas configurações de cultura do idioma inglês independente da
região. Ao contrário de outras System.Globalization.CultureInfo configurações, as
configurações da cultura invariante são garantidas para permanecer consistente em um
único computador, de sistema para sistema e entre versões do .NET. A cultura invariante
pode ser vista como uma espécie de caixa preta que garante a estabilidade das
comparações de cordas e ordenação em todas as culturas.

) Importante

Se seu aplicativo tomar uma decisão de segurança sobre um identificador


simbólico, como um nome de arquivo ou pipe nomeado, ou sobre dados
persistentes, como os dados baseados em texto em um arquivo XML, a operação
deverá usar uma comparação ordinal em vez de uma comparação sensível à
cultura. Isso ocorre porque uma comparação sensível à cultura pode produzir
resultados diferentes dependendo da cultura em vigor, enquanto uma comparação
ordinal depende exclusivamente do valor binário dos caracteres comparados.

) Importante

A maioria dos métodos que executam operações de cadeia de caracteres inclui


uma sobrecarga que tem um parâmetro do tipo StringComparison, que permite
especificar se o método executa uma operação ordinal ou sensível à cultura. Em
geral, você deve chamar essa sobrecarga para deixar clara a intenção da chamada
do método. Para obter práticas recomendadas e orientação para usar operações
ordinais e sensíveis à cultura em cadeias de caracteres, consulte Práticas
recomendadas para usar cadeias de caracteres.

As operações para invólucro, análise e formatação, comparação e classificação e teste


de igualdade podem ser ordinais ou sensíveis à cultura. As seções a seguir discutem
cada categoria de operação.

 Dica

Você deve sempre chamar uma sobrecarga de método que deixe clara a intenção
da chamada de método. Por exemplo, em vez de chamar o método para executar
uma comparação sensível à cultura de duas cadeias de caracteres usando as
convenções da cultura atual, você deve chamar o método com um valor de
StringComparison.CurrentCulture para o comparisonType Compare(String,
String)Compare(String, String, StringComparison) argumento. Para obter mais
informações, consulte Práticas recomendadas para o uso de cadeias de caracteres.

Você pode baixar as tabelas de peso de classificação, um conjunto de arquivos de texto


que contêm informações sobre os pesos de caracteres usados em operações de
classificação e comparação, a partir dos seguintes links:

Windows (.NET Framework e .NET Core): classificando tabelas de peso


Atualização de maio de 2019 do Windows 10 ou posterior (.NET 5+) e Linux e
macOS (.NET Core e .NET 5+): Tabela de elementos de agrupamento Unicode
padrão

Capitalização
As regras de caixa determinam como alterar a capitalização de um caractere Unicode;
por exemplo, de minúsculas para maiúsculas. Muitas vezes, uma operação de invólucro
é executada antes de uma comparação de cadeia de caracteres. Por exemplo, uma
cadeia de caracteres pode ser convertida em maiúsculas para que possa ser comparada
com outra cadeia de caracteres maiúscula. Você pode converter os caracteres em uma
cadeia de caracteres em minúsculas chamando o método or e convertê-los em
maiúsculas chamando o ToLowerToUpper método ouToUpperInvariant.ToLowerInvariant
Além disso, você pode usar o método para converter uma cadeia de caracteres em
maiúsculas e minúsculas TextInfo.ToTitleCase de título.

7 Observação

.NET Core em execução somente nos sistemas Linux e macOS: o comportamento


da ordenação das culturas C e Posix é sempre sensível a maiúsculas e minúsculas
porque essas culturas não usam a ordem da Ordenação Unicode esperada.
Recomendamos usar uma cultura diferente de C ou Posix para executar operações
de classificação que diferenciam culturas e maiúsculas de minúsculas.

As operações de invólucro podem ser baseadas nas regras da cultura atual, de uma
cultura especificada ou da cultura invariante. Como os mapeamentos de caso podem
variar dependendo da cultura usada, o resultado das operações de invólucro pode variar
com base na cultura. As diferenças reais no invólucro são de três tipos:

Diferenças no mapeamento de casos de LETRA MAIÚSCULA LATINA I (U+0049),


LETRA PEQUENA LATINA I (U+0069), LETRA MAIÚSCULA LATINA I COM PONTO
ACIMA (U+0130) e LETRA PEQUENA LATINA SEM PONTO I (U+0131). Nas culturas
tr-TR (turco (Turquia)) e az-Latn-AZ (Azerbaijão, latim), e nas culturas neutras tr, az
e az-Latn, o equivalente minúsculo de LETRA MAIÚSCULA LATINA I é LETRA
PEQUENA LATINA SEM PONTO I, e o equivalente maiúsculo de LETRA PEQUENA
LATINA I é LETRA MAIÚSCULA LATINA I COM PONTO ACIMA. Em todas as outras
culturas, incluindo a cultura invariante, LETRA PEQUENA LATINA I e LETRA
MAIÚSCULA LATINA I são equivalentes minúsculas e maiúsculas.
O exemplo a seguir demonstra como uma comparação de cadeia de caracteres
projetada para impedir o acesso ao sistema de arquivos pode falhar se depender
de uma comparação de caixa sensível à cultura. (As convenções de invólucro da
cultura invariante deveriam ter sido usadas.)

C#

using System;
using System.Globalization;
using System.Threading;

public class Example1


{
const string disallowed = "file";

public static void Main()


{

IsAccessAllowed(@"FILE:\\\c:\users\user001\documents\FinancialInfo.txt"
);
}

private static void IsAccessAllowed(String resource)


{
CultureInfo[] cultures = { CultureInfo.CreateSpecificCulture("en-
US"),
CultureInfo.CreateSpecificCulture("tr-
TR") };
String scheme = null;
int index = resource.IndexOfAny( new Char[] { '\\', '/' } );
if (index > 0)
scheme = resource.Substring(0, index - 1);

// Change the current culture and perform the comparison.


foreach (var culture in cultures) {
Thread.CurrentThread.CurrentCulture = culture;
Console.WriteLine("Culture: {0}",
CultureInfo.CurrentCulture.DisplayName);
Console.WriteLine(resource);
Console.WriteLine("Access allowed: {0}",
! String.Equals(disallowed, scheme,
StringComparison.CurrentCultureIgnoreCase));
Console.WriteLine();
}
}
}
// The example displays the following output:
// Culture: English (United States)
// FILE:\\\c:\users\user001\documents\FinancialInfo.txt
// Access allowed: False
//
// Culture: Turkish (Turkey)
// FILE:\\\c:\users\user001\documents\FinancialInfo.txt
// Access allowed: True

Diferenças nos mapeamentos de casos entre a cultura invariante e todas as outras


culturas. Nesses casos, usar as regras de caixa da cultura invariante para alterar um
caractere para maiúsculas ou minúsculas retorna o mesmo caractere. Para todas as
outras culturas, devolve um caráter diferente. Alguns dos caracteres afetados estão
listados na tabela a seguir.

ノ Expandir a tabela

Character Se alterado Retornos


para

SINAL DE MÍCRON (U+00B5) Maiúsculas LETRA MAIÚSCULA GREGA


MU (U+-39C)

LETRA MAIÚSCULA LATINA I COM PONTO Letras LETRA PEQUENA LATINA I


ACIMA (U+0130) minúsculas (U+0069)

LETRA PEQUENA LATINA SEM PONTO I Maiúsculas LETRA MAIÚSCULA LATINA I


(U+0131) (U+0049)

LETRA PEQUENA LATINA S LONGAS Maiúsculas LETRA S MAIÚSCULA LATINA


(U+017F) (U+0053)

LETRA D MAIÚSCULA LATINA COM LETRA Letras LETRA PEQUENA LATINA DZ


Z PEQUENA COM CARON (U+01C5) minúsculas COM CARON (U+01C6)

COMBINANDO YPOGEGRAMMENI GREGO Maiúsculas LETRA MAIÚSCULA GREGA


(U+0345) IOTA (U+0399)

Diferenças nos mapeamentos de maiúsculas e minúsculas de pares de maiúsculas


e minúsculas no intervalo de caracteres ASCII. Na maioria das culturas, um par de
letras maiúsculas e minúsculas é igual ao par equivalente de duas letras maiúsculas
ou minúsculas. Isso não é verdade para os seguintes pares de duas letras nas
seguintes culturas, porque em cada caso eles são comparados a um dígrafo:
"lJ" e "nJ" na cultura hr-HR (croata (Croácia)).
"cH" nas culturas cs-CZ (República Checa (República Checa)) e sk-SK (Eslovaco
(Eslováquia)).
"aA" na cultura da-DK (dinamarquesa (Dinamarca)).
"cS", "dZ", "dZS", "nY", "sZ", "tY" e "zS" na cultura hu-HU (Hungria).
"cH" e "lL" na cultura es-ES_tradnl (Espanhol (Espanha, Classificação
Tradicional)).
"cH", "gI", "kH", "nG" "nH", "pH", "qU', "tH" e "tR" na cultura vi-VN (vietnamita
(Vietnã)).

No entanto, é incomum encontrar uma situação em que uma comparação sensível


à cultura desses pares crie problemas, porque esses pares são incomuns em
cadeias de caracteres fixas ou identificadores.

O exemplo a seguir ilustra algumas das diferenças nas regras de invólucro entre culturas
ao converter cadeias de caracteres em maiúsculas.

C#

using System;
using System.Globalization;
using System.IO;

public class Example


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\case.txt");
string[] words = { "file", "sıfır", "Dženana" };
CultureInfo[] cultures = { CultureInfo.InvariantCulture,
new CultureInfo("en-US"),
new CultureInfo("tr-TR") };

foreach (var word in words) {


sw.WriteLine("{0}:", word);
foreach (var culture in cultures) {
string name = String.IsNullOrEmpty(culture.Name) ?
"Invariant" : culture.Name;
string upperWord = word.ToUpper(culture);
sw.WriteLine(" {0,10}: {1,7} {2, 38}", name,
upperWord, ShowHexValue(upperWord));
}
sw.WriteLine();
}
sw.Close();
}

private static string ShowHexValue(string s)


{
string retval = null;
foreach (var ch in s) {
byte[] bytes = BitConverter.GetBytes(ch);
retval += String.Format("{0:X2} {1:X2} ", bytes[1], bytes[0]);
}
return retval;
}
}
// The example displays the following output:
// file:
// Invariant: FILE 00 46 00 49 00 4C 00 45
// en-US: FILE 00 46 00 49 00 4C 00 45
// tr-TR: FİLE 00 46 01 30 00 4C 00 45
//
// sıfır:
// Invariant: SıFıR 00 53 01 31 00 46 01 31 00 52
// en-US: SIFIR 00 53 00 49 00 46 00 49 00 52
// tr-TR: SIFIR 00 53 00 49 00 46 00 49 00 52
//
// Dženana:
// Invariant: DžENANA 01 C5 00 45 00 4E 00 41 00 4E 00 41
// en-US: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41
// tr-TR: DŽENANA 01 C4 00 45 00 4E 00 41 00 4E 00 41

Análise e formatação
Formatação e análise são operações inversas. As regras de formatação determinam
como converter um valor, como uma data e hora ou um número, em sua representação
de cadeia de caracteres, enquanto as regras de análise determinam como converter
uma representação de cadeia de caracteres em um valor como uma data e hora. As
regras de formatação e análise dependem de convenções culturais. O exemplo a seguir
ilustra a ambiguidade que pode surgir ao interpretar uma cadeia de caracteres de data
específica da cultura. Sem conhecer as convenções da cultura que foi utilizada para
produzir uma sequência de datas, não é possível saber se 01/03/2011, 01/03/2011 e
01/03/2011 representam 3 de janeiro de 2011 ou 1º de março de 2011.

C#

using System;
using System.Globalization;

public class Example9


{
public static void Main()
{
DateTime date = new DateTime(2011, 3, 1);
CultureInfo[] cultures = { CultureInfo.InvariantCulture,
new CultureInfo("en-US"),
new CultureInfo("fr-FR") };

foreach (var culture in cultures)


Console.WriteLine("{0,-12} {1}", String.IsNullOrEmpty(culture.Name)
?
"Invariant" : culture.Name,
date.ToString("d", culture));
}
}
// The example displays the following output:
// Invariant 03/01/2011
// en-US 3/1/2011
// fr-FR 01/03/2011

Da mesma forma, como mostra o exemplo a seguir, uma única cadeia de caracteres
pode produzir datas diferentes, dependendo da cultura cujas convenções são usadas na
operação de análise.

C#

using System;
using System.Globalization;

public class Example15


{
public static void Main()
{
string dateString = "07/10/2011";
CultureInfo[] cultures = { CultureInfo.InvariantCulture,
CultureInfo.CreateSpecificCulture("en-GB"),
CultureInfo.CreateSpecificCulture("en-US")
};
Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}\n", "Date String",
"Culture",
"Month", "Day");
foreach (var culture in cultures) {
DateTime date = DateTime.Parse(dateString, culture);
Console.WriteLine("{0,-12} {1,10} {2,8} {3,8}", dateString,
String.IsNullOrEmpty(culture.Name) ?
"Invariant" : culture.Name,
date.Month, date.Day);
}
}
}
// The example displays the following output:
// Date String Culture Month Day
//
// 07/10/2011 Invariant 7 10
// 07/10/2011 en-GB 10 7
// 07/10/2011 en-US 7 10

Comparação da cadeia de caracteres e classificação


As convenções para comparar e classificar cadeias de caracteres variam de cultura para
cultura. Por exemplo, a ordem de classificação pode ser baseada na fonética ou na
representação visual de caracteres. Nos idiomas do leste asiático, os caracteres são
ordenados pelo traço e pelo radical dos ideogramas. A classificação também depende
da ordem em que os idiomas e culturas usam para o alfabeto. Por exemplo, a língua
dinamarquesa tem um caractere "Æ" que é ordenado após a letra "Z" no alfabeto. Além
disso, as comparações podem diferenciar maiúsculas de minúsculas ou diferenciar
maiúsculas de minúsculas, e as regras de invólucro podem diferir de acordo com a
cultura. A comparação ordinal, por outro lado, usa os pontos de código Unicode de
caracteres individuais em uma cadeia de caracteres ao comparar e classificar cadeias de
caracteres.

As regras de classificação determinam a ordem alfabética dos caracteres Unicode e


como duas cadeias de caracteres se comparam entre si. Por exemplo, o
String.Compare(String, String, StringComparison) método compara duas cadeias de
caracteres com base no StringComparison parâmetro. Se o valor do parâmetro for , o
método executará uma comparação linguística que usa as convenções da cultura atual,
se o valor do parâmetro for StringComparison.CurrentCultureStringComparison.Ordinal,
o método executará uma comparação ordinal. Consequentemente, como mostra o
exemplo a seguir, se a cultura atual é o inglês dos EUA, a primeira chamada para o
método (usando comparação sensível à cultura) considera "a" menor que "A", mas a
segunda chamada para o String.Compare(String, String, StringComparison) mesmo
método (usando comparação ordinal) considera "a" maior que "A".

C#

using System;
using System.Globalization;
using System.Threading;

public class Example2


{
public static void Main()
{
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("en-US");
Console.WriteLine(String.Compare("A", "a",
StringComparison.CurrentCulture));
Console.WriteLine(String.Compare("A", "a", StringComparison.Ordinal));
}
}
// The example displays the following output:
// 1
// -32

O .NET oferece suporte a regras de classificação de palavras, cadeias de caracteres e


ordinais:

Uma classificação de palavras executa uma comparação cultural de cadeias de


caracteres nas quais determinados caracteres Unicode não alfanuméricos podem
ter pesos especiais atribuídos a eles. Por exemplo, o hífen (-) pode ter um peso
muito pequeno atribuído a ele para que "coop" e "co-op" apareçam um ao lado
do outro em uma lista classificada. Para obter uma lista dos métodos que
comparam duas cadeias de caracteres usando regras de classificação de palavras,
consulte a seção Operações de cadeia de String caracteres por categoria.

Uma classificação de cadeia de caracteres também executa uma comparação


sensível à cultura. É semelhante a uma classificação de palavras, exceto que não há
casos especiais, e todos os símbolos não alfanuméricos vêm antes de todos os
caracteres Unicode alfanuméricos. Duas cadeias de caracteres podem ser
comparadas usando regras de classificação de cadeia de caracteres chamando as
CompareInfo.Compare sobrecargas de método que têm um parâmetro fornecido
com um options valor de CompareOptions.StringSort. Observe que esse é o único
método que o .NET fornece para comparar duas cadeias de caracteres usando
regras de classificação de cadeia de caracteres.

Uma classificação ordinal compara cadeias de caracteres com base no valor


numérico de cada Char objeto na cadeia de caracteres. Uma comparação ordinal
diferencia automaticamente maiúsculas de minúsculas porque as versões
minúsculas e maiúsculas de um caractere têm pontos de código diferentes. No
entanto, se maiúsculas e minúsculas não forem importantes, você poderá
especificar uma comparação ordinal que ignore maiúsculas e minúsculas. Isso
equivale a converter a cadeia de caracteres em maiúsculas usando a cultura
invariante e, em seguida, executando uma comparação ordinal no resultado. Para
obter uma lista dos métodos que comparam duas cadeias de caracteres usando
regras de classificação ordinais, consulte a seção Operações de cadeia de String
caracteres por categoria.

Uma comparação sensível à cultura é qualquer comparação que usa explícita ou


implicitamente um CultureInfo objeto, incluindo a cultura invariante especificada pela
CultureInfo.InvariantCulture propriedade. A cultura implícita é a cultura atual, que é
especificada pelas Thread.CurrentCulture propriedades e CultureInfo.CurrentCulture . Há
uma variação considerável na ordem de classificação dos caracteres alfabéticos (ou seja,
caracteres para os quais a Char.IsLetter propriedade retorna true ) entre culturas. Você
pode especificar uma comparação sensível à cultura que usa as convenções de uma
cultura específica fornecendo um objeto para um CultureInfo método de comparação
de cadeia de caracteres, como Compare(String, String, CultureInfo, CompareOptions).
Você pode especificar uma comparação sensível à cultura que usa as convenções da
cultura atual fornecendo StringComparison.CurrentCulture,
StringComparison.CurrentCultureIgnoreCaseou qualquer membro da CompareOptions
enumeração que não seja CompareOptions.Ordinal ou
CompareOptions.OrdinalIgnoreCase para uma sobrecarga apropriada do Compare
método. Uma comparação sensível à cultura é geralmente apropriada para classificação,
enquanto uma comparação ordinal não é. Uma comparação ordinal é geralmente
apropriada para determinar se duas cadeias de caracteres são iguais (isto é, para
determinar a identidade), enquanto uma comparação sensível à cultura não é.

O exemplo a seguir ilustra a diferença entre comparação cultural e ordinal. O exemplo


avalia três cadeias de caracteres, "Apple", "Æble" e "AEble", usando comparação ordinal
e as convenções das culturas da-DK e en-US (cada uma das quais é a cultura padrão no
momento em que o Compare método é chamado). Como a língua dinamarquesa trata o
caractere "Æ" como uma letra individual e o classifica após "Z" no alfabeto, a cadeia
"Æble" é maior que "Apple". No entanto, "Æble" não é considerado equivalente a
"AEble", então "Æble" também é maior que "AEble". A cultura en-US não inclui a letra
"Æ", mas a trata como equivalente a "AE", o que explica por que "Æble" é menor que
"Apple", mas igual a "AEble". A comparação ordinal, por outro lado, considera "Apple"
menor que "Æble", e "Æble" maior que "AEble".

C#

using System;
using System.Globalization;
using System.Threading;

public class CompareStringSample


{
public static void Main()
{
string str1 = "Apple";
string str2 = "Æble";
string str3 = "AEble";

// Set the current culture to Danish in Denmark.


Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
Console.WriteLine("Current culture: {0}",
CultureInfo.CurrentCulture.Name);
Console.WriteLine("Comparison of {0} with {1}: {2}",
str1, str2, String.Compare(str1, str2));
Console.WriteLine("Comparison of {0} with {1}: {2}\n",
str2, str3, String.Compare(str2, str3));

// Set the current culture to English in the U.S.


Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
Console.WriteLine("Current culture: {0}",
CultureInfo.CurrentCulture.Name);
Console.WriteLine("Comparison of {0} with {1}: {2}",
str1, str2, String.Compare(str1, str2));
Console.WriteLine("Comparison of {0} with {1}: {2}\n",
str2, str3, String.Compare(str2, str3));

// Perform an ordinal comparison.


Console.WriteLine("Ordinal comparison");
Console.WriteLine("Comparison of {0} with {1}: {2}",
str1, str2,
String.Compare(str1, str2,
StringComparison.Ordinal));
Console.WriteLine("Comparison of {0} with {1}: {2}",
str2, str3,
String.Compare(str2, str3,
StringComparison.Ordinal));
}
}
// The example displays the following output:
// Current culture: da-DK
// Comparison of Apple with Æble: -1
// Comparison of Æble with AEble: 1
//
// Current culture: en-US
// Comparison of Apple with Æble: 1
// Comparison of Æble with AEble: 0
//
// Ordinal comparison
// Comparison of Apple with Æble: -133
// Comparison of Æble with AEble: 133

Use as seguintes diretrizes gerais para escolher um método apropriado de classificação


ou comparação de cadeia de caracteres:

Se você quiser que as cadeias de caracteres sejam ordenadas com base na cultura
do usuário, você deve ordená-las com base nas convenções da cultura atual. Se a
cultura do usuário for alterada, a ordem das cadeias de caracteres classificadas
também será alterada de acordo. Por exemplo, um aplicativo de dicionário de
sinônimos deve sempre classificar palavras com base na cultura do usuário.

Se desejar que as cadeias de caracteres sejam ordenadas com base nas


convenções de uma cultura específica, você deve ordená-las fornecendo um
objeto que represente essa cultura para um CultureInfo método de comparação.
Por exemplo, em um aplicativo projetado para ensinar aos alunos um idioma
específico, você deseja que as cadeias de caracteres sejam ordenadas com base
nas convenções de uma das culturas que fala esse idioma.

Se quiser que a ordem das cadeias de caracteres permaneça inalterada entre


culturas, você deve ordená-las com base nas convenções da cultura invariante ou
usar uma comparação ordinal. Por exemplo, você usaria uma classificação ordinal
para organizar os nomes de arquivos, processos, mutexes ou pipes nomeados.

Para uma comparação que envolve uma decisão de segurança (como se um nome
de usuário é válido), você deve sempre executar um teste ordinal para igualdade
chamando uma sobrecarga do Equals método.
7 Observação

As regras de classificação e invólucro sensíveis à cultura usadas na comparação de


cadeias de caracteres dependem da versão do .NET. No .NET Core, a comparação
de cadeia de caracteres depende da versão do padrão Unicode suportada pelo
sistema operacional subjacente. No .NET Framework 4.5 e versões posteriores em
execução no Windows 8 ou posterior, as informações de classificação, invólucro,
normalização e caracteres Unicode estão em conformidade com o padrão Unicode
6.0. Em outros sistemas operacionais Windows, eles estão em conformidade com o
padrão Unicode 5.0.

Para obter mais informações sobre regras de classificação de palavra, cadeia de


caracteres e ordinal, consulte o System.Globalization.CompareOptions tópico . Para
obter recomendações adicionais sobre quando usar cada regra, consulte Práticas
recomendadas para usar cadeias de caracteres.

Normalmente, você não chama métodos de comparação de cadeias de caracteres,


como Compare diretamente, para determinar a ordem de classificação das cadeias de
caracteres. Em vez disso, os métodos de comparação são chamados por métodos de
classificação como Array.Sort ou List<T>.Sort. O exemplo a seguir executa quatro
operações de classificação diferentes (classificação de palavras usando a cultura atual,
classificação de palavras usando a cultura invariante, classificação ordinal e classificação
de cadeia de caracteres usando a cultura invariante) sem chamar explicitamente um
método de comparação de cadeia de caracteres, embora eles especifiquem o tipo de
comparação a ser usado. Observe que cada tipo de classificação produz uma ordem
exclusiva de cadeias de caracteres em sua matriz.

C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;

public class Example3


{
public static void Main()
{
string[] strings = { "coop", "co-op", "cooperative",
"co\u00ADoperative", "cœur", "coeur" };

// Perform a word sort using the current (en-US) culture.


string[] current = new string[strings.Length];
strings.CopyTo(current, 0);
Array.Sort(current, StringComparer.CurrentCulture);
// Perform a word sort using the invariant culture.
string[] invariant = new string[strings.Length];
strings.CopyTo(invariant, 0);
Array.Sort(invariant, StringComparer.InvariantCulture);

// Perform an ordinal sort.


string[] ordinal = new string[strings.Length];
strings.CopyTo(ordinal, 0);
Array.Sort(ordinal, StringComparer.Ordinal);

// Perform a string sort using the current culture.


string[] stringSort = new string[strings.Length];
strings.CopyTo(stringSort, 0);
Array.Sort(stringSort, new SCompare());

// Display array values


Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}\n",
"Original", "Word Sort", "Invariant Word",
"Ordinal Sort", "String Sort");
for (int ctr = 0; ctr < strings.Length; ctr++)
Console.WriteLine("{0,13} {1,13} {2,15} {3,13} {4,13}",
strings[ctr], current[ctr], invariant[ctr],
ordinal[ctr], stringSort[ctr] );
}
}

// IComparer<String> implementation to perform string sort.


internal class SCompare : IComparer<String>
{
public int Compare(string x, string y)
{
return CultureInfo.CurrentCulture.CompareInfo.Compare(x, y,
CompareOptions.StringSort);
}
}
// The example displays the following output:
// Original Word Sort Invariant Word Ordinal Sort String
Sort
//
// coop cœur cœur co-op co-
op
// co-op coeur coeur coeur
cœur
// cooperative coop coop coop
coeur
// co­
operative co-op co-op cooperative coop
// cœur cooperative cooperative co­operative cooperative
// coeur co­operative co­
operative cœur co­operative

 Dica
Internamente, o .NET usa chaves de classificação para oferecer suporte à
comparação de cadeias de caracteres culturalmente sensíveis. Cada caractere em
uma cadeia de caracteres recebe várias categorias de pesos de classificação,
incluindo alfabética, maiúsculas e minúsculas e diacríticas. Uma chave de
classificação, representada pela SortKey classe, fornece um repositório desses
pesos para uma cadeia de caracteres específica. Se seu aplicativo executar um
grande número de operações de pesquisa ou classificação no mesmo conjunto de
cadeias de caracteres, você poderá melhorar seu desempenho gerando e
armazenando chaves de classificação para todas as cadeias de caracteres que ele
usa. Quando uma operação de classificação ou comparação é necessária, use as
chaves de classificação em vez das cadeias de caracteres. Para obter mais
informações, consulte a classe SortKey.

Se você não especificar uma convenção de comparação de cadeia de caracteres,


métodos de classificação, como Array.Sort(Array) executar uma classificação sensível a
maiúsculas e minúsculas em cadeias de caracteres. O exemplo a seguir ilustra como a
alteração da cultura atual afeta a ordem das cadeias de caracteres classificadas em uma
matriz. Ele cria uma matriz de três cadeias de caracteres. Primeiro, ele define a
System.Threading.Thread.CurrentThread.CurrentCulture propriedade como en-US e

chama o Array.Sort(Array) método. A ordem de classificação resultante é baseada em


convenções de classificação para a cultura inglesa (Estados Unidos). Em seguida, o
exemplo define a System.Threading.Thread.CurrentThread.CurrentCulture propriedade
como da-DK e chama o Array.Sort método novamente. Observe como a ordem de
classificação resultante difere dos resultados en-US porque usa as convenções de
classificação para dinamarquês (Dinamarca).

C#

using System;
using System.Globalization;
using System.Threading;

public class ArraySort


{
public static void Main(String[] args)
{
// Create and initialize a new array to store the strings.
string[] stringArray = { "Apple", "Æble", "Zebra"};

// Display the values of the array.


Console.WriteLine( "The original string array:");
PrintIndexAndValues(stringArray);

// Set the CurrentCulture to "en-US".


Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");
// Sort the values of the array.
Array.Sort(stringArray);

// Display the values of the array.


Console.WriteLine("After sorting for the culture \"en-US\":");
PrintIndexAndValues(stringArray);

// Set the CurrentCulture to "da-DK".


Thread.CurrentThread.CurrentCulture = new CultureInfo("da-DK");
// Sort the values of the Array.
Array.Sort(stringArray);

// Display the values of the array.


Console.WriteLine("After sorting for the culture \"da-DK\":");
PrintIndexAndValues(stringArray);
}
public static void PrintIndexAndValues(string[] myArray)
{
for (int i = myArray.GetLowerBound(0); i <=
myArray.GetUpperBound(0); i++ )
Console.WriteLine("[{0}]: {1}", i, myArray[i]);
Console.WriteLine();
}
}
// The example displays the following output:
// The original string array:
// [0]: Apple
// [1]: Æble
// [2]: Zebra
//
// After sorting for the "en-US" culture:
// [0]: Æble
// [1]: Apple
// [2]: Zebra
//
// After sorting for the culture "da-DK":
// [0]: Apple
// [1]: Zebra
// [2]: Æble

2 Aviso

Se o seu objetivo principal ao comparar cadeias de caracteres é determinar se elas


são iguais, você deve chamar o String.Equals método. Normalmente, você deve
usar Equals para executar uma comparação ordinal. O String.Compare método
destina-se principalmente a classificar cadeias de caracteres.

Os métodos de pesquisa de cadeia de caracteres, como String.StartsWith e


String.IndexOf, também podem executar comparações de cadeia de caracteres ordinais
ou sensíveis à cultura. O exemplo a seguir ilustra as diferenças entre comparações
ordinais e sensíveis à cultura usando o IndexOf método. Uma pesquisa sensível à cultura
em que a cultura atual é o inglês (Estados Unidos) considera a substring "oe" para
corresponder à ligadura "œ". Como um hífen suave (U+00AD) é um caractere de largura
zero, a pesquisa trata o hífen suave como equivalente e String.Empty localiza uma
correspondência no início da cadeia de caracteres. Uma busca ordinal, por outro lado,
não encontra correspondência em nenhum dos casos.

C#

using System;

public class Example8


{
public static void Main()
{
// Search for "oe" and "œu" in "œufs" and "oeufs".
string s1 = "œufs";
string s2 = "oeufs";
FindInString(s1, "oe", StringComparison.CurrentCulture);
FindInString(s1, "oe", StringComparison.Ordinal);
FindInString(s2, "œu", StringComparison.CurrentCulture);
FindInString(s2, "œu", StringComparison.Ordinal);
Console.WriteLine();

string s3 = "co\u00ADoperative";
FindInString(s3, "\u00AD", StringComparison.CurrentCulture);
FindInString(s3, "\u00AD", StringComparison.Ordinal);
}

private static void FindInString(string s, string substring,


StringComparison options)
{
int result = s.IndexOf(substring, options);
if (result != -1)
Console.WriteLine("'{0}' found in {1} at position {2}",
substring, s, result);
else
Console.WriteLine("'{0}' not found in {1}",
substring, s);
}
}
// The example displays the following output:
// 'oe' found in œufs at position 0
// 'oe' not found in œufs
// 'œu' found in oeufs at position 0
// 'œu' not found in oeufs
//
// '­
' found in co­
operative at position 0
// '­
' found in co­
operative at position 2
Pesquisar em cadeias de caracteres
Os métodos de pesquisa de cadeia de caracteres, como String.StartsWith e , também
podem executar comparações de cadeia de caracteres ordinais ou sensíveis à cultura
para determinar se um caractere String.IndexOfou subcadeia de caracteres é encontrado
em uma cadeia de caracteres especificada.

Os métodos de pesquisa na String classe que procuram um caractere individual, como o


método, ou um de um conjunto de caracteres, como o IndexOfIndexOfAny método,
todos executam uma pesquisa ordinal. Para executar uma pesquisa sensível à cultura
para um caractere, você deve chamar um CompareInfo método como
CompareInfo.IndexOf(String, Char) ou CompareInfo.LastIndexOf(String, Char). Observe
que os resultados da busca por um caractere usando comparação ordinal e sensível à
cultura podem ser muito diferentes. Por exemplo, uma busca por um caractere Unicode
pré-composto, como a ligadura "Æ" (U+00C6), pode corresponder a qualquer
ocorrência de seus componentes na sequência correta, como "AE" (U+041U+0045),
dependendo da cultura. O exemplo a seguir ilustra a diferença entre os
String.IndexOf(Char) métodos e ao procurar um caractere CompareInfo.IndexOf(String,
Char) individual. A ligadura "æ" (U+00E6) é encontrada na cadeia "aérea" quando se usa
as convenções da cultura en-US, mas não quando se usa as convenções da cultura da-
DK ou quando se realiza uma comparação ordinal.

C#

using System;
using System.Globalization;

public class Example17


{
public static void Main()
{
String[] cultureNames = { "da-DK", "en-US" };
CompareInfo ci;
String str = "aerial";
Char ch = 'æ'; // U+00E6

Console.Write("Ordinal comparison -- ");


Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
str.IndexOf(ch));

foreach (var cultureName in cultureNames) {


ci = CultureInfo.CreateSpecificCulture(cultureName).CompareInfo;
Console.Write("{0} cultural comparison -- ", cultureName);
Console.WriteLine("Position of '{0}' in {1}: {2}", ch, str,
ci.IndexOf(str, ch));
}
}
}
// The example displays the following output:
// Ordinal comparison -- Position of 'æ' in aerial: -1
// da-DK cultural comparison -- Position of 'æ' in aerial: -1
// en-US cultural comparison -- Position of 'æ' in aerial: 0

Por outro lado, String os métodos de classe que procuram uma cadeia de caracteres em
vez de um caractere executam uma pesquisa sensível à cultura se as opções de pesquisa
não forem explicitamente especificadas por um parâmetro do tipo StringComparison. A
única exceção é Contains, que realiza uma pesquisa ordinal.

Teste de igualdade
Use o String.Compare método para determinar a relação de duas cadeias de caracteres
na ordem de classificação. Normalmente, essa é uma operação sensível à cultura. Em
contrapartida, chame o método para testar a String.Equals igualdade. Como o teste de
igualdade geralmente compara a entrada do usuário com alguma cadeia de caracteres
conhecida, como um nome de usuário válido, uma senha ou um caminho do sistema de
arquivos, normalmente é uma operação ordinal.

2 Aviso

É possível testar a igualdade chamando o método e determinando se o


String.Compare valor de retorno é zero. No entanto, essa prática não é
recomendada. Para determinar se duas cadeias de caracteres são iguais, você deve
chamar uma das sobrecargas do String.Equals método. A sobrecarga preferencial a
ser chamada é o método de instância Equals(String, StringComparison) ou o
método estático Equals(String, String, StringComparison) , porque ambos os
métodos incluem um System.StringComparison parâmetro que especifica
explicitamente o tipo de comparação.

O exemplo a seguir ilustra o perigo de realizar uma comparação sensível à cultura para
igualdade quando uma ordinal deve ser usada em vez disso. Nesse caso, a intenção do
código é proibir o acesso ao sistema de arquivos de URLs que começam com "FILE://"
ou "file://", realizando uma comparação sem diferenciação de maiúsculas e minúsculas
do início de uma URL com a cadeia de caracteres "FILE://". No entanto, se uma
comparação sensível à cultura for realizada usando a cultura turca (Turquia) em uma
URL que começa com "file://", a comparação para igualdade falhará, porque o
equivalente turco maiúsculo do "i" minúsculo é "İ" em vez de "I". Como resultado, o
acesso ao sistema de arquivos é permitido inadvertidamente. Por outro lado, se uma
comparação ordinal for realizada, a comparação para igualdade será bem-sucedida e o
acesso ao sistema de arquivos será negado.
C#

using System;
using System.Globalization;
using System.Threading;

public class Example4


{
public static void Main()
{
Thread.CurrentThread.CurrentCulture =
CultureInfo.CreateSpecificCulture("tr-TR");

string filePath = "file://c:/notes.txt";

Console.WriteLine("Culture-sensitive test for equality:");


if (! TestForEquality(filePath,
StringComparison.CurrentCultureIgnoreCase))
Console.WriteLine("Access to {0} is allowed.", filePath);
else
Console.WriteLine("Access to {0} is not allowed.", filePath);

Console.WriteLine("\nOrdinal test for equality:");


if (! TestForEquality(filePath, StringComparison.OrdinalIgnoreCase))
Console.WriteLine("Access to {0} is allowed.", filePath);
else
Console.WriteLine("Access to {0} is not allowed.", filePath);
}

private static bool TestForEquality(string str, StringComparison cmp)


{
int position = str.IndexOf("://");
if (position < 0) return false;

string substring = str.Substring(0, position);


return substring.Equals("FILE", cmp);
}
}
// The example displays the following output:
// Culture-sensitive test for equality:
// Access to file://c:/notes.txt is allowed.
//
// Ordinal test for equality:
// Access to file://c:/notes.txt is not allowed.

Normalização
Alguns caracteres Unicode têm várias representações. Por exemplo, qualquer um dos
seguintes pontos de código pode representar a letra "ắ":

U+1EAF
U+0103 U+0301
U+0061 U+0306 U+0301

Várias representações para um único caractere complicam a pesquisa, a classificação, a


correspondência e outras operações de cadeia de caracteres.

O padrão Unicode define um processo chamado normalização que retorna uma


representação binária de um caractere Unicode para qualquer uma de suas
representações binárias equivalentes. A normalização pode usar vários algoritmos,
chamados de formulários de normalização, que seguem regras diferentes. O .NET
oferece suporte aos formulários de normalização Unicode C, D, KC e KD. Quando as
cadeias de caracteres foram normalizadas para a mesma forma de normalização, elas
podem ser comparadas usando a comparação ordinal.

Uma comparação ordinal é uma comparação binária do valor escalar Unicode dos
objetos correspondentes Char em cada cadeia de caracteres. A String classe inclui vários
métodos que podem executar uma comparação ordinal, incluindo o seguinte:

Qualquer sobrecarga dos Comparemétodos , , , EndsWithIndexOf,


EqualsStartsWithe LastIndexOf que inclui um StringComparison parâmetro. O
método executa uma comparação ordinal se você fornecer um valor de
StringComparison.Ordinal ou OrdinalIgnoreCase para esse parâmetro.

As sobrecargas do CompareOrdinal método.

Métodos que usam comparação ordinal por padrão, como Contains, Replacee
Split.

Métodos que procuram um Char valor ou os elementos em uma matriz em uma


Char instância de cadeia de caracteres. Tais métodos incluem IndexOf(Char) e
Split(Char[]).

Você pode determinar se uma cadeia de caracteres é normalizada para o formulário de


normalização C chamando o método ou você pode chamar o
String.IsNormalized()String.IsNormalized(NormalizationForm) método para determinar
se uma cadeia de caracteres é normalizada para um formulário de normalização
especificado. Você também pode chamar o método para converter uma cadeia de
caracteres em forma de normalização C ou pode chamar o
String.Normalize()String.Normalize(NormalizationForm) método para converter uma
cadeia de caracteres em um formulário de normalização especificado. Para obter
informações passo a passo sobre como normalizar e comparar cadeias de caracteres,
consulte os Normalize() métodos e Normalize(NormalizationForm) .
O exemplo simples a seguir ilustra a normalização de cadeia de caracteres. Ele define a
letra "ố" de três maneiras diferentes em três cadeias de caracteres diferentes, e usa uma
comparação ordinal para igualdade para determinar que cada cadeia de caracteres
difere das outras duas cadeias de caracteres. Em seguida, ele converte cada cadeia de
caracteres para os formulários de normalização com suporte e novamente executa uma
comparação ordinal de cada cadeia de caracteres em um formulário de normalização
especificado. Em cada caso, o segundo teste de igualdade mostra que as cadeias de
caracteres são iguais.

C#

using System;
using System.Globalization;
using System.IO;
using System.Text;

public class Example13


{
private static StreamWriter sw;

public static void Main()


{
sw = new StreamWriter(@".\TestNorm1.txt");

// Define three versions of the same word.


string s1 = "sống"; // create word with U+1ED1
string s2 = "s\u00F4\u0301ng";
string s3 = "so\u0302\u0301ng";

TestForEquality(s1, s2, s3);


sw.WriteLine();

// Normalize and compare strings using each normalization form.


foreach (string formName in Enum.GetNames(typeof(NormalizationForm)))
{
sw.WriteLine("Normalization {0}:\n", formName);
NormalizationForm nf = (NormalizationForm)
Enum.Parse(typeof(NormalizationForm), formName);
string[] sn = NormalizeStrings(nf, s1, s2, s3);
TestForEquality(sn);
sw.WriteLine("\n");
}

sw.Close();
}

private static void TestForEquality(params string[] words)


{
for (int ctr = 0; ctr <= words.Length - 2; ctr++)
for (int ctr2 = ctr + 1; ctr2 <= words.Length - 1; ctr2++)
sw.WriteLine("{0} ({1}) = {2} ({3}): {4}",
words[ctr], ShowBytes(words[ctr]),
words[ctr2], ShowBytes(words[ctr2]),
words[ctr].Equals(words[ctr2],
StringComparison.Ordinal));
}

private static string ShowBytes(string str)


{
string result = null;
foreach (var ch in str)
result += $"{(ushort)ch:X4} ";
return result.Trim();
}

private static string[] NormalizeStrings(NormalizationForm nf, params


string[] words)
{
for (int ctr = 0; ctr < words.Length; ctr++)
if (! words[ctr].IsNormalized(nf))
words[ctr] = words[ctr].Normalize(nf);
return words;
}
}
// The example displays the following output:
// sống (0073 1ED1 006E 0067) = sống (0073 00F4 0301 006E 0067): False
// sống (0073 1ED1 006E 0067) = sống (0073 006F 0302 0301 006E 0067):
False
// sống (0073 00F4 0301 006E 0067) = sống (0073 006F 0302 0301 006E
0067): False
//
// Normalization FormC:
//
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
// Normalization FormD:
//
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301
006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301
006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301
006E 0067): True
//
//
// Normalization FormKC:
//
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
// sống (0073 1ED1 006E 0067) = sống (0073 1ED1 006E 0067): True
//
//
// Normalization FormKD:
//
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301
006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301
006E 0067): True
// sống (0073 006F 0302 0301 006E 0067) = sống (0073 006F 0302 0301
006E 0067): True

Para obter mais informações sobre formulários de normalização e normalização,


consulte System.Text.NormalizationForm, bem como Unicode Standard Annex #15:
Unicode Normalization Forms e as Perguntas frequentes sobre normalização no site
da unicode.org.

Operações da cadeia de caracteres por


categoria
A String classe fornece membros para comparar cadeias de caracteres, testar cadeias de
caracteres para igualdade, localizar caracteres ou subcadeias de caracteres em uma
cadeia de caracteres, modificar uma cadeia de caracteres, extrair subcadeias de
caracteres de uma cadeia de caracteres, combinar cadeias de caracteres, formatar
valores, copiar uma cadeia de caracteres e normalizar uma cadeia de caracteres.

Comparar cadeias de caracteres


Você pode comparar cadeias de caracteres para determinar sua posição relativa na
ordem de classificação usando os seguintes String métodos:

Compare Retorna um inteiro que indica a relação de uma cadeia de caracteres


com uma segunda cadeia de caracteres na ordem de classificação.

CompareOrdinal Retorna um inteiro que indica a relação de uma cadeia de


caracteres para uma segunda cadeia de caracteres com base em uma comparação
de seus pontos de código.

CompareTo Retorna um inteiro que indica a relação da instância de cadeia de


caracteres atual com uma segunda cadeia de caracteres na ordem de classificação.
O CompareTo(String) método fornece o IComparable e IComparable<T>
implementações para a String classe.

Testar a igualdade das cadeias de caracteres


Você chama o Equals método para determinar se duas cadeias de caracteres são iguais.
A instância Equals(String, String, StringComparison) e as sobrecargas estáticas
Equals(String, StringComparison) permitem especificar se a comparação é sensível à
cultura ou ordinal e se as maiúsculas e minúsculas são consideradas ou ignoradas. A
maioria dos testes de igualdade é ordinal, e as comparações para igualdade que
determinam o acesso a um recurso do sistema (como um objeto do sistema de
arquivos) devem ser sempre ordinais.

Localizar caracteres em uma cadeia de caracteres


A String classe inclui dois tipos de métodos de pesquisa:

Métodos que retornam um Boolean valor para indicar se uma subcadeia de


caracteres específica está presente em uma instância de cadeia de caracteres. Estes
incluem o Contains, EndsWithe StartsWith métodos.

Métodos que indicam a posição inicial de uma substring em uma ocorrência de


string. Estes incluem o IndexOf, , , IndexOfAnyLastIndexOfe LastIndexOfAny
métodos.

2 Aviso

Se você quiser pesquisar uma cadeia de caracteres para um padrão específico em


vez de uma subcadeia de caracteres específica, você deve usar expressões
regulares. Para obter mais informações, consulte Expressões regulares do .NET.

Modificar uma cadeia de caracteres


A String classe inclui os seguintes métodos que parecem modificar o valor de uma
cadeia de caracteres:

Insert Insere uma cadeia de caracteres na instância atual String .

PadLeft Insere uma ou mais ocorrências de um caractere especificado no início de


uma cadeia de caracteres.

PadRight Insere uma ou mais ocorrências de um caractere especificado no final de


uma cadeia de caracteres.

Remove exclui uma substring da instância atual String .

Replace Substitui uma substring por outra substring na instância atual String .

ToLower e converta ToLowerInvariant todos os caracteres em uma cadeia de


caracteres em minúsculas.
ToUpper e converta ToUpperInvariant todos os caracteres em uma cadeia de
caracteres em maiúsculas.

Trim Remove todas as ocorrências de um caractere do início e do fim de uma


cadeia de caracteres.

TrimEnd Remove todas as ocorrências de um caractere do final de uma cadeia de


caracteres.

TrimStart Remove todas as ocorrências de um caractere do início de uma cadeia de


caracteres.

) Importante

Todos os métodos de modificação de cadeia de caracteres retornam um novo


String objeto. Eles não modificam o valor da instância atual.

Extrair substrings de uma cadeia de caracteres


O String.Split método separa uma única cadeia de caracteres em várias cadeias de
caracteres. As sobrecargas do método permitem especificar vários delimitadores, limitar
o número de subcadeias de caracteres que o método extrai, cortar espaço em branco de
subcadeias de caracteres e especificar se cadeias de caracteres vazias (que ocorrem
quando os delimitadores são adjacentes) são incluídas entre as cadeias de caracteres
retornadas.

Combinar cadeias de caracteres


Os seguintes String métodos podem ser usados para concatenação de cadeia de
caracteres:

Concat Combina uma ou mais subcadeias de caracteres em uma única cadeia de


caracteres.
Join Concatena uma ou mais subcadeias de caracteres em um único elemento e
adiciona um separador entre cada subcadeia de caracteres.

Formatar valores
O String.Format método usa o recurso de formatação composta para substituir um ou
mais espaços reservados em uma cadeia de caracteres com a representação de cadeia
de caracteres de algum objeto ou valor. O Format método é frequentemente usado
para fazer o seguinte:

Para incorporar a representação de cadeia de caracteres de um valor numérico em


uma cadeia de caracteres.
Para incorporar a representação de cadeia de caracteres de um valor de data e
hora em uma cadeia de caracteres.
Para incorporar a representação de cadeia de caracteres de um valor de
enumeração em uma cadeia de caracteres.
Para incorporar a representação de cadeia de caracteres de algum objeto que
ofereça suporte à IFormattable interface em uma cadeia de caracteres.
Para justificar à direita ou à esquerda uma subcadeia de caracteres em um campo
dentro de uma cadeia de caracteres maior.

Para obter informações detalhadas sobre operações de formatação e exemplos,


consulte o resumo de Format sobrecarga.

Copiar uma cadeia de caracteres


Você pode chamar os seguintes String métodos para fazer uma cópia de uma cadeia de
caracteres:

Clone Retorna uma referência a um objeto existente String .


Copy Cria uma cópia de uma cadeia de caracteres existente.
CopyTo Copia uma parte de uma cadeia de caracteres para uma matriz de
caracteres.

Normalizar uma cadeia de caracteres


Em Unicode, um único caractere pode ter vários pontos de código. A normalização
converte esses caracteres equivalentes na mesma representação binária. O
String.Normalize método executa a normalização e o String.IsNormalized método
determina se uma cadeia de caracteres é normalizada.

Para obter mais informações e um exemplo, consulte a seção Normalização


anteriormente neste artigo.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
Selecione um link para fornecer
A fonte deste conteúdo pode comentários:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores.
produto
System.String construtor
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Sintaxe de construtor sobrecarregada


Os construtores de cadeia de caracteres se enquadram em duas categorias: aqueles sem
parâmetros de ponteiro e aqueles com parâmetros de ponteiro. Os construtores que
usam ponteiros não são compatíveis com CLS. Além disso, Visual Basic não oferece
suporte ao uso de ponteiros e C# requer código que usa ponteiros para executar em
um contexto não seguro. Para obter mais informações, consulte unsafe.

Para obter orientação adicional sobre como escolher uma sobrecarga, consulte Qual
método eu chamo?.

String(Char[] value)

Inicializa a nova instância para o valor indicado por uma matriz de caracteres Unicode.
Este construtor copia caracteres Unicode(Exemplo 2: Usar uma matriz de caracteres).

String(Char[] value, Int32 startIndex, Int32 length)

Inicializa a nova instância para o valor indicado por uma matriz de caracteres Unicode,
uma posição de caractere inicial dentro dessa matriz e um comprimento (Exemplo 3:
Usar uma parte de uma matriz de caracteres e repetir um único caractere).

String(Char c, Int32 count)

Inicializa a nova instância para o valor indicado por um caractere Unicode especificado
repetido um número especificado de vezes (Exemplo 3: Usar uma parte de uma matriz
de caracteres e repetir um único caractere).

String(char* value)

(Não compatível com CLS) Inicializa a nova instância para o valor indicado por um
ponteiro para uma matriz de caracteres Unicode que é terminada por um caractere nulo
(U+0000 ou '\0'). (Exemplo 4: Use um ponteiro para uma matriz de caracteres).

Permissão: SecurityCriticalAttribute, requer confiança total para o chamador imediato.


Este membro não pode ser usado pelo código transparente ou parcialmente confiável.

String(char* value, Int32 startIndex, Int32 length)

(Não compatível com CLS) Inicializa a nova instância para o valor indicado por um
ponteiro para uma matriz de caracteres Unicode, uma posição de caractere inicial
dentro dessa matriz e um comprimento. O construtor copia os caracteres Unicode de
value começar no índice e terminar no índice startIndex startIndex length + - 1

(Exemplo 5: Instanciar uma cadeia de caracteres de um ponteiro e um intervalo de uma


matriz).

Permissão: SecurityCriticalAttribute, requer confiança total para o chamador imediato.


Este membro não pode ser usado pelo código transparente ou parcialmente confiável.

String(SByte* value)

(Não compatível com CLS) Inicializa a nova instância para o valor indicado por um
ponteiro para uma matriz de inteiros assinados de 8 bits. Presume-se que a matriz
represente uma cadeia de caracteres codificada usando a página de código do sistema
atual (ou seja, a codificação especificada por Encoding.Default). O construtor processa
caracteres a partir do value local especificado pelo ponteiro até que um caractere nulo
(0x00) seja atingido (Exemplo 6: Instanciar uma cadeia de caracteres de um ponteiro
para uma matriz de bytes assinada).

Permissão: SecurityCriticalAttribute, requer confiança total para o chamador imediato.


Este membro não pode ser usado pelo código transparente ou parcialmente confiável.

String(SByte* value, Int32 startIndex, Int32 length)

(Não compatível com CLS) Inicializa a nova instância para o valor indicado por um
ponteiro para uma matriz de inteiros assinados de 8 bits, uma posição inicial dentro
dessa matriz e um comprimento. Presume-se que a matriz represente uma cadeia de
caracteres codificada usando a página de código do sistema atual (ou seja, a codificação
especificada por Encoding.Default). O construtor processa caracteres do valor
começando em e terminando em startIndex startIndex length + - 1 (Exemplo 6:
Instanciar uma cadeia de caracteres de um ponteiro para uma matriz de bytes assinada).

Permissão: SecurityCriticalAttribute, requer confiança total para o chamador imediato.


Este membro não pode ser usado pelo código transparente ou parcialmente confiável.

String(SByte* value, Int32 startIndex, Int32 length, Encoding enc)

(Não compatível com CLS) Inicializa a nova instância para o valor indicado por um
ponteiro para uma matriz de inteiros assinados de 8 bits, uma posição inicial dentro
dessa matriz, um comprimento e um Encoding objeto.

Permissão: SecurityCriticalAttribute, requer confiança total para o chamador imediato.


Este membro não pode ser usado pelo código transparente ou parcialmente confiável.

Parâmetros
Aqui está uma lista completa de parâmetros usados por String construtores que não
incluem um parâmetro de ponteiro. Para os parâmetros usados por cada sobrecarga,
consulte a sintaxe de sobrecarga acima.

ノ Expandir a tabela

Parâmetro Tipo Descrição

value Char[] Uma matriz de caracteres Unicode.

c Char Um caractere Unicode.

startIndex Int32 A posição inicial no value primeiro caractere na nova cadeia de caracteres.

Valor padrão: 0

length Int32 O número de caracteres a serem incluídos na nova cadeia de caracteres


value .

Valor padrão: Array.Length

count Int32 O número de vezes que o caractere c é repetido na nova cadeia de


caracteres. Se count for zero, o valor do novo objeto será String.Empty.

Aqui está uma lista completa de parâmetros usados por String construtores que incluem
um parâmetro de ponteiro. Para os parâmetros usados por cada sobrecarga, consulte a
sintaxe de sobrecarga acima.

ノ Expandir a tabela

Parâmetro Tipo Descrição

value Char* Um ponteiro para uma matriz terminada em nulo de caracteres Unicode
ou uma matriz de inteiros assinados de 8 bits. Se value for null ou
-ou- uma matriz vazia, o valor da nova cadeia de caracteres será
String.Empty.
SByte*

startIndex Int32 O índice do elemento de matriz que define o primeiro caractere na nova
cadeia de caracteres.

Valor padrão: 0

length Int32 O número de elementos de matriz a serem usados para criar a nova
cadeia de caracteres. Se length for zero, o construtor criará uma cadeia
de caracteres cujo valor será String.Empty.

Valor padrão: Array.Length


Parâmetro Tipo Descrição

enc Encoding Um objeto que especifica como a value matriz é codificada.

Valor padrão: Encoding.Default, ou a página de código ANSI atual do


sistema

Exceções
Aqui está uma lista de exceções lançadas por construtores que não incluem parâmetros
de ponteiro.

ノ Expandir a tabela

Exceção Condição Lançado por

ArgumentNullException value é null . String(Char[],


Int32, Int32)

ArgumentOutOfRangeException startIndex , length , ou count é menor que String(Char,


zero. Int32)

-ou- String(Char[],
Int32, Int32)
A soma de startIndex e length é maior
que o número de elementos em value .

-ou-

count é menor que zero.

Aqui está uma lista de exceções lançadas por construtores que incluem parâmetros de
ponteiro.

ノ Expandir a tabela

Exceção Condição Lançado por

ArgumentException value especifica uma matriz que contém Todos os


um caractere Unicode inválido. construtores com
ponteiros.
-ou-

value ou value + startIndex especifica


um endereço inferior a 64K.
Exceção Condição Lançado por

-ou-

Uma nova String instância não pôde ser


inicializada a partir da matriz de bytes
porque value não usa a value codificação
de página de código padrão.

ArgumentNullException value é nulo. String(SByte*)

String(SByte*,
Int32, Int32)

String(SByte*,
Int32, Int32,
Encoding)

ArgumentOutOfRangeException O processo atual não tem acesso de leitura Todos os


a todos os caracteres endereçados. construtores com
ponteiros.
-ou-

startIndex ou length é menor que zero,


value + startIndex causa um estouro do
ponteiro ou o processo atual não tem
acesso de leitura a todos os caracteres
endereçados.

-ou-

O comprimento da nova cadeia de


caracteres é muito grande para alocar.

AccessViolationException value , ou value + + startIndex length - String(SByte*)


1, especifica um endereço inválido.
String(SByte*,
Int32, Int32)

String(SByte*,
Int32, Int32,
Encoding)

Qual método devo chamar?


ノ Expandir a tabela
Para Ligar ou usar

Crie uma cadeia de caracteres. Atribuição de um literal de cadeia de caracteres ou de


uma cadeia de caracteres existente (Exemplo 1: Usar
atribuição de cadeia de caracteres)

Crie uma cadeia de caracteres a partir String(Char[]) (Exemplo 2: Usar uma matriz de
de uma matriz de caracteres inteira. caracteres)

Crie uma cadeia de caracteres a partir String(Char[], Int32, Int32) (Exemplo 3: Usar uma parte
de uma parte de uma matriz de de uma matriz de caracteres e repetir um único
caracteres. caractere)

Crie uma cadeia de caracteres que String(Char, Int32) (Exemplo 3: Usar uma parte de uma
repita o mesmo caractere várias vezes. matriz de caracteres e repetir um único caractere)

Crie uma cadeia de caracteres de um String(Char*)


ponteiro para um Unicode ou matriz de
caracteres largos.

Crie uma cadeia de caracteres a partir String(Char*, Int32, Int32)


de uma parte de uma matriz de
caracteres Unicode ou ampla usando
seu ponteiro.

Crie uma cadeia de caracteres a partir String(SByte*), String(SByte*, Int32, Int32)


de uma matriz C++ char .
-ou-

String(SByte*, Int32, Int32, Encoding)

Crie uma cadeia de caracteres a partir ASCIIEncoding.GetString


de caracteres ASCII.

Criar cadeias de caracteres


A técnica mais comumente usada para criar cadeias de caracteres programaticamente é
a atribuição simples, conforme ilustrado no Exemplo 1. A String classe também inclui
quatro tipos de sobrecargas de construtor que permitem criar cadeias de caracteres a
partir dos seguintes valores:

A partir de uma matriz de caracteres (uma matriz de caracteres codificados em


UTF-16). Você pode criar um novo String objeto a partir dos caracteres em toda a
matriz ou em uma parte dela. O String(Char[]) construtor copia todos os caracteres
na matriz para a nova cadeia de caracteres. O String(Char[], Int32, Int32) construtor
copia os caracteres de índice para índice startIndex startIndex + length - 1 para
a nova cadeia de caracteres. Se length for zero, o valor da nova cadeia de
caracteres será String.Empty.

Se o código instanciar repetidamente cadeias de caracteres que têm o mesmo


valor, você poderá melhorar o desempenho do aplicativo usando um meio
alternativo de criar cadeias de caracteres. Para obter mais informações, consulte
Manipular cadeias de caracteres repetitivas.

De um único caractere que é duplicado zero, uma ou mais vezes, usando o


String(Char, Int32) construtor. Se count for zero, o valor da nova cadeia de
caracteres será String.Empty.

De um ponteiro para uma matriz de caracteres terminada em nulo, usando o


String(Char*) construtor ou String(Char*, Int32, Int32) . A matriz inteira ou um
intervalo especificado pode ser usado para inicializar a cadeia de caracteres. O
construtor copia uma sequência de caracteres Unicode começando do ponteiro
especificado ou do ponteiro especificado mais startIndex e continuando até o
final da matriz ou para length caracteres. Se value for um ponteiro nulo ou
length for zero, o construtor criará uma cadeia de caracteres cujo valor é

String.Empty. Se a operação de cópia prosseguir até o final da matriz e a matriz


não for terminada em nulo, o comportamento do construtor será dependente do
sistema. Tal condição pode causar uma violação de acesso.

Se a matriz contiver caracteres nulos incorporados (U+0000 ou '\0') e a sobrecarga


for chamada, a String(Char*, Int32, Int32) ocorrência de cadeia de caracteres
conterá length caracteres, incluindo quaisquer nulos incorporados. O exemplo a
seguir mostra o que acontece quando um ponteiro para uma matriz de 10
elementos que inclui dois caracteres nulos é passado para o String(Char*, Int32,
Int32) método. Como o endereço é o início da matriz e todos os elementos na
matriz devem ser adicionados à cadeia de caracteres, o construtor instancia uma
cadeia de caracteres com dez caracteres, incluindo dois nulos incorporados. Por
outro lado, se a mesma matriz for passada para o construtor, o resultado será uma
cadeia de caracteres de quatro caracteres que não inclui o String(Char*) primeiro
caractere nulo.

C#

using System;

public class Example2


{
public unsafe static void Main()
{
char[] chars = { 'a', 'b', 'c', 'd', '\0', 'A', 'B', 'C', 'D',
'\0' };
string s = null;

fixed(char* chPtr = chars) {


s = new string(chPtr, 0, chars.Length);
}

foreach (var ch in s)
Console.Write($"{(ushort)ch:X4} ");
Console.WriteLine();

fixed(char* chPtr = chars) {


s = new string(chPtr);
}

foreach (var ch in s)
Console.Write($"{(ushort)ch:X4} ");
Console.WriteLine();
}
}
// The example displays the following output:
// 0061 0062 0063 0064 0000 0041 0042 0043 0044 0000
// 0061 0062 0063 0064

A matriz deve conter caracteres Unicode. Em C++, isso significa que a matriz de
caracteres deve ser definida como o tipo gerenciado Char[] ou o tipo não
gerenciado wchar_t [].

Se a sobrecarga for chamada e a matriz não for terminada em nulo, ou se a


sobrecarga for chamada e -1 incluir um intervalo que está fora da memória
alocada para a String(Char*)String(Char*, Int32, Int32) sequência de caracteres, o
comportamento do construtor é dependente do sistema e startIndex +
length uma violação de acesso pode ocorrer.

De um ponteiro para uma matriz de bytes assinada. A matriz inteira ou um


intervalo especificado pode ser usado para inicializar a cadeia de caracteres. A
sequência de bytes pode ser interpretada usando a codificação de página de
código padrão ou uma codificação pode ser especificada na chamada do
construtor. Se o construtor tentar instanciar uma cadeia de caracteres de uma
matriz inteira que não é terminada em nulo, ou se o intervalo da matriz de -
value length + + startIndex 1 estiver fora da memória alocada para a matriz, o

comportamento desse construtor é dependente do sistema e uma violação de


value + startIndex acesso pode ocorrer.

Os três construtores que incluem uma matriz de bytes assinada como um


parâmetro são projetados principalmente para converter uma matriz C++ char em
uma cadeia de caracteres, conforme mostrado neste exemplo:
C++

using namespace System;

void main()
{
char chars[] = { 'a', 'b', 'c', 'd', '\x00' };

char* charPtr = chars;


String^ value = gcnew String(charPtr);

Console::WriteLine(value);
}
// The example displays the following output:
// abcd

Se a matriz contiver caracteres nulos ('\0') ou bytes cujo valor for 0 e a sobrecarga
for chamada, a String(SByte*, Int32, Int32) ocorrência de cadeia de caracteres
conterá length caracteres, incluindo quaisquer nulos incorporados. O exemplo a
seguir mostra o que acontece quando um ponteiro para uma matriz de 10
elementos que inclui dois caracteres nulos é passado para o String(SByte*, Int32,
Int32) método. Como o endereço é o início da matriz e todos os elementos na
matriz devem ser adicionados à cadeia de caracteres, o construtor instancia uma
cadeia de caracteres com dez caracteres, incluindo dois nulos incorporados. Por
outro lado, se a mesma matriz for passada para o construtor, o resultado será uma
cadeia de caracteres de quatro caracteres que não inclui o String(SByte*) primeiro
caractere nulo.

C#

using System;

public class Example5


{
public unsafe static void Main()
{
sbyte[] bytes = { 0x61, 0x62, 0x063, 0x064, 0x00, 0x41, 0x42,
0x43, 0x44, 0x00 };

string s = null;
fixed (sbyte* bytePtr = bytes) {
s = new string(bytePtr, 0, bytes.Length);
}

foreach (var ch in s)
Console.Write($"{(ushort)ch:X4} ");

Console.WriteLine();
fixed(sbyte* bytePtr = bytes) {
s = new string(bytePtr);
}

foreach (var ch in s)
Console.Write($"{(ushort)ch:X4} ");
Console.WriteLine();
}
}
// The example displays the following output:
// 0061 0062 0063 0064 0000 0041 0042 0043 0044 0000
// 0061 0062 0063 0064

Como os String(SByte*) construtores e String(SByte*, Int32, Int32) interpretam


value usando a página de código ANSI padrão, chamar esses construtores com

matrizes de bytes idênticas pode criar cadeias de caracteres que têm valores
diferentes em sistemas diferentes.

Manipular cadeias de caracteres repetitivas


Os aplicativos que analisam ou decodificam fluxos de texto geralmente usam o
construtor ou o String(Char[], Int32, Int32)StringBuilder.Append(Char[], Int32, Int32)
método para converter sequências de caracteres em uma cadeia de caracteres. Criar
repetidamente novas cadeias de caracteres com o mesmo valor em vez de criar e
reutilizar uma cadeia de caracteres desperdiça memória. Se for provável que você crie o
mesmo valor de cadeia de caracteres repetidamente chamando o String(Char[], Int32,
Int32) construtor, mesmo que não saiba com antecedência quais podem ser esses
valores de cadeia de caracteres idênticos, você pode usar uma tabela de pesquisa.

Por exemplo, suponha que você leia e analise um fluxo de caracteres de um arquivo que
contém marcas e atributos XML. Quando você analisa o fluxo, você encontra
repetidamente certos tokens (ou seja, sequências de caracteres que têm um significado
simbólico). Tokens equivalentes às cadeias de caracteres "0", "1", "true" e "false"
provavelmente ocorrerão com frequência em um fluxo XML.

Em vez de converter cada token em uma nova cadeia de caracteres, você pode criar um
System.Xml.NameTable objeto para manter cadeias de caracteres que ocorrem com
frequência. O NameTable objeto melhora o desempenho, porque recupera cadeias de
caracteres armazenadas sem alocar memória temporária. Quando você encontrar um
token, use o NameTable.Get(Char[], Int32, Int32) método para recuperar o token da
tabela. Se o token existir, o método retornará a cadeia de caracteres correspondente. Se
o token não existir, use o NameTable.Add(Char[], Int32, Int32) método para inserir o
token na tabela e obter a cadeia de caracteres correspondente.
Exemplo 1: Usar atribuição de cadeia de
caracteres
O exemplo a seguir cria uma nova cadeia de caracteres atribuindo-lhe um literal de
cadeia de caracteres. Ele cria uma segunda cadeia de caracteres atribuindo o valor da
primeira cadeia de caracteres a ela. Essas são as duas maneiras mais comuns de
instanciar um novo String objeto.

C#

using System;

public class Example3


{
public static void Main()
{
String value1 = "This is a string.";
String value2 = value1;
Console.WriteLine(value1);
Console.WriteLine(value2);
}
}
// The example displays the following output:
// This is a string.
// This is a string.

VB

Module Example
Public Sub Main()
Dim value1 As String = "This is a string."
Dim value2 As String = value1
Console.WriteLine(value1)
Console.WriteLine(value2)
End Sub
End Module
' The example displays the following output:
' This is a string.
' This is a string.

Exemplo 2: Usar uma matriz de caracteres


O exemplo a seguir demonstra como criar um novo String objeto a partir de uma matriz
de caracteres.

C#
// Unicode Mathematical operators
char [] charArr1 = {'\u2200','\u2202','\u200F','\u2205'};
String szMathSymbols = new String(charArr1);

// Unicode Letterlike Symbols


char [] charArr2 = {'\u2111','\u2118','\u2122','\u2126'};
String szLetterLike = new String (charArr2);

// Compare Strings - the result is false


Console.WriteLine("The Strings are equal? " +
(String.Compare(szMathSymbols, szLetterLike)==0?"true":"false") );

VB

' Unicode Mathematical operators


Dim charArr1() As Char = {ChrW(&H2200), ChrW(&H2202), _
ChrW(&H200F), ChrW(&H2205)}
Dim szMathSymbols As New String(charArr1)

' Unicode Letterlike Symbols


Dim charArr2() As Char = {ChrW(&H2111), ChrW(&H2118), _
ChrW(&H2122), ChrW(&H2126)}
Dim szLetterLike As New String(charArr2)

' Compare Strings - the result is false


Console.WriteLine("The strings are equal? " & _
CStr(szMathSymbols.Equals(szLetterLike)))

Exemplo 3: Usar uma parte de uma matriz de


caracteres e repetir um único caractere
O exemplo a seguir demonstra como criar um novo objeto a partir de uma parte de
uma matriz de caracteres e como criar um novo StringString objeto que contém várias
ocorrências de um único caractere.

C#

// Create a Unicode String with 5 Greek Alpha characters


String szGreekAlpha = new String('\u0391',5);
// Create a Unicode String with a Greek Omega character
String szGreekOmega = new String(new char []
{'\u03A9','\u03A9','\u03A9'},2,1);

String szGreekLetters = String.Concat(szGreekOmega, szGreekAlpha,


szGreekOmega.Clone());

// Examine the result


Console.WriteLine(szGreekLetters);
// The first index of Alpha
int ialpha = szGreekLetters.IndexOf('\u0391');
// The last index of Omega
int iomega = szGreekLetters.LastIndexOf('\u03A9');

Console.WriteLine("The Greek letter Alpha first appears at index " + ialpha


+
" and Omega last appears at index " + iomega + " in this String.");

VB

' Create a Unicode String with 5 Greek Alpha characters


Dim szGreekAlpha As New String(ChrW(&H0391), 5)
' Create a Unicode String with a Greek Omega character
Dim szGreekOmega As New String(New Char() {ChrW(&H03A9), ChrW(&H03A9), _
ChrW(&H03A9)}, 2, 1)

Dim szGreekLetters As String = String.Concat(szGreekOmega, szGreekAlpha, _


szGreekOmega.Clone())

' Examine the result


Console.WriteLine(szGreekLetters)

' The first index of Alpha


Dim iAlpha As Integer = szGreekLetters.IndexOf(ChrW(&H0391))
' The last index of Omega
Dim iomega As Integer = szGreekLetters.LastIndexOf(ChrW(&H03A9))

Console.WriteLine("The Greek letter Alpha first appears at index {0}.", _


ialpha)
Console.WriteLIne("The Greek letter Omega last appears at index {0}.", _
iomega)

Exemplo 4: Usar um ponteiro para uma matriz


de caracteres
O exemplo a seguir demonstra como criar um novo String objeto de um ponteiro para
uma matriz de caracteres. O exemplo C# deve ser compilado usando a opção do
/unsafe compilador.

C#

using System;

public class Example4


{
public static unsafe void Main()
{
char[] characters = { 'H', 'e', 'l', 'l', 'o', ' ',
'w', 'o', 'r', 'l', 'd', '!', '\u0000' };
string value;

fixed (char* charPtr = characters) {


value = new String(charPtr);
}
Console.WriteLine(value);
}
}
// The example displays the following output:
// Hello world!

Exemplo 5: Instanciar uma cadeia de caracteres


de um ponteiro e um intervalo de uma matriz
O exemplo a seguir examina os elementos de uma matriz de caracteres para um ponto
ou um ponto de exclamação. Se um for encontrado, ele instancia uma cadeia de
caracteres dos caracteres na matriz que precedem o símbolo de pontuação. Caso
contrário, ele instancia uma cadeia de caracteres com todo o conteúdo da matriz. O
exemplo C# deve ser compilado usando a opção do /unsafe compilador.

C#

using System;

public class Example1


{
public static unsafe void Main()
{
char[] characters = { 'H', 'e', 'l', 'l', 'o', ' ',
'w', 'o', 'r', 'l', 'd', '!', '\u0000' };
String value;

fixed (char* charPtr = characters) {


int length = 0;
Char* iterator = charPtr;

while (*iterator != '\x0000')


{
if (*iterator == '!' || *iterator == '.')
break;
iterator++;
length++;
}
value = new String(charPtr, 0, length);
}
Console.WriteLine(value);
}
}
// The example displays the following output:
// Hello World

Exemplo 6: Instanciar uma cadeia de caracteres


de um ponteiro para uma matriz de bytes
assinada
O exemplo a seguir demonstra como você pode criar uma instância da String classe
com o String(SByte*) construtor.

C#

unsafe
{
// Null terminated ASCII characters in an sbyte array
String szAsciiUpper = null;
sbyte[] sbArr1 = new sbyte[] { 0x41, 0x42, 0x43, 0x00 };
// Instruct the Garbage Collector not to move the memory
fixed(sbyte* pAsciiUpper = sbArr1)
{
szAsciiUpper = new String(pAsciiUpper);
}
String szAsciiLower = null;
sbyte[] sbArr2 = { 0x61, 0x62, 0x63, 0x00 };
// Instruct the Garbage Collector not to move the memory
fixed(sbyte* pAsciiLower = sbArr2)
{
szAsciiLower = new String(pAsciiLower, 0, sbArr2.Length);
}
// Prints "ABC abc"
Console.WriteLine(szAsciiUpper + " " + szAsciiLower);

// Compare Strings - the result is true


Console.WriteLine("The Strings are equal when capitalized ? " +
(String.Compare(szAsciiUpper.ToUpper(),
szAsciiLower.ToUpper())==0?"true":"false") );

// This is the effective equivalent of another Compare method, which


ignores case
Console.WriteLine("The Strings are equal when capitalized ? " +
(String.Compare(szAsciiUpper, szAsciiLower, true)==0?"true":"false")
);
}
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.String.Format
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

) Importante

Em vez de chamar o método String.Format ou usar cadeias de caracteres de


formato de composição, é possível usar cadeias de caracteres interpoladas quando
a linguagem é compatível com eles. Uma cadeia de caracteres interpolada é uma
cadeia de caracteres que contém expressões interpoladas. Cada expressão
interpolada é resolvida com o valor da expressão e incluída na cadeia de caracteres
resultante quando a cadeia de caracteres é atribuída. Para saber mais, consulte o
tópico Interpolação de cadeia de caracteres (Referência do C#) ou Cadeias de
caracteres interpoladas (Referência do Visual Basic).

Exemplos
Vários exemplos que chamam o Format método são intercalados ao longo deste artigo.
Você também pode baixar um conjunto completo de String.Format exemplos, que
estão incluídos em um projeto .NET Core para C#.

A seguir estão alguns dos exemplos incluídos no artigo:

Criar uma cadeia de caracteres de formato


Inserir uma cadeia de caracteres
O item de formato
Formatar itens que tenham o mesmo índice

Controle de saída formatada


Formatação de controle
Espaçamento de controle
Alinhamento de controle
Controlar o número de dígitos integrais
Controlar o número de dígitos após o separador decimal
Incluir chaves literais na cadeia de caracteres de resultados
Tornar as cadeias de caracteres de formato sensíveis à
cultura
Tornar as cadeias de caracteres de formato sensíveis à cultura

Personalizar a operação de formatação


Uma operação de formatação personalizada
Um provedor de interceptação e formatador de algarismos romanos

Introdução ao método String.Format


Use String.Format se você precisar inserir o valor de um objeto, variável ou expressão em
outra cadeia de caracteres. Por exemplo, você pode inserir o valor de um Decimal valor
em uma cadeia de caracteres para exibi-lo ao usuário como uma única cadeia de
caracteres:

C#

Decimal pricePerOunce = 17.36m;


String s = String.Format("The current price is {0} per ounce.",
pricePerOunce);
Console.WriteLine(s);
// Result: The current price is 17.36 per ounce.

E você pode controlar a formatação desse valor:

C#

Decimal pricePerOunce = 17.36m;


String s = String.Format("The current price is {0:C2} per ounce.",
pricePerOunce);
Console.WriteLine(s);
// Result if current culture is en-US:
// The current price is $17.36 per ounce.

Além da formatação, você também pode controlar o alinhamento e o espaçamento.

Inserir uma cadeia de caracteres


String.Format Começa com uma cadeia de caracteres de formato, seguida por um ou
mais objetos ou expressões que serão convertidos em cadeias de caracteres e inseridos
em um local especificado na cadeia de caracteres de formato. Por exemplo:
C#

decimal temp = 20.4m;


string s = String.Format("The temperature is {0}°C.", temp);
Console.WriteLine(s);
// Displays 'The temperature is 20.4°C.'

O {0} na cadeia de caracteres de formato é um item de formato. 0 é o índice do objeto


cujo valor de cadeia de caracteres será inserido nessa posição. (Os índices começam em
0.) Se o objeto a ser inserido não for uma cadeia de caracteres, seu ToString método
será chamado para convertê-lo em um antes de inseri-lo na cadeia de caracteres de
resultado.

Aqui está outro exemplo que usa dois itens de formato e dois objetos na lista de
objetos:

C#

string s = String.Format("At {0}, the temperature is {1}°C.",


DateTime.Now, 20.4);
Console.WriteLine(s);
// Output similar to: 'At 4/10/2015 9:29:41 AM, the temperature is 20.4°C.'

Você pode ter quantos itens de formato e quantos objetos desejar na lista de objetos,
desde que o índice de cada item de formato tenha um objeto correspondente na lista de
objetos. Você também não precisa se preocupar com qual sobrecarga você chama; O
compilador selecionará o apropriado para você.

Formatação de controle
Você pode seguir o índice em um item de formato com uma cadeia de caracteres de
formato para controlar como um objeto é formatado. Por exemplo, {0:d} aplica a
cadeia de caracteres de formato "d" ao primeiro objeto na lista de objetos. Aqui está um
exemplo com um único objeto e dois itens de formato:

C#

string s = String.Format("It is now {0:d} at {0:t}", DateTime.Now);


Console.WriteLine(s);
// Output similar to: 'It is now 4/10/2015 at 10:04 AM'

Vários tipos oferecem suporte a cadeias de caracteres de formato, incluindo todos os


tipos numéricos (cadeias de caracteres de formato padrão e personalizado), todas as
datas e horas (cadeias de caracteres de formato padrão e personalizado) e intervalos de
tempo (cadeias de caracteres de formato padrão e personalizado), todos os tipos de
enumeração tipos de enumeração e GUIDs. Você também pode adicionar suporte para
cadeias de caracteres de formato aos seus próprios tipos.

Espaçamento de controle
Você pode definir a largura da cadeia de caracteres inserida na cadeia de caracteres de
resultado usando uma sintaxe como {0,12} , que insere uma cadeia de caracteres de 12
caracteres. Nesse caso, a representação de cadeia de caracteres do primeiro objeto é
alinhada à direita no campo de 12 caracteres. (No entanto, se a representação da cadeia
de caracteres do primeiro objeto tiver mais de 12 caracteres, a largura do campo
preferencial será ignorada e a cadeia de caracteres inteira será inserida na cadeia de
caracteres de resultado.)

O exemplo a seguir define um campo de 6 caracteres para conter a cadeia de caracteres


"Ano" e algumas cadeias de caracteres de ano, bem como um campo de 15 caracteres
para conter a cadeia de caracteres "População" e alguns dados de população. Observe
que os caracteres estão alinhados à direita no campo.

C#

int[] years = { 2013, 2014, 2015 };


int[] population = { 1025632, 1105967, 1148203 };
var sb = new System.Text.StringBuilder();
sb.Append(String.Format("{0,6} {1,15}\n\n", "Year", "Population"));
for (int index = 0; index < years.Length; index++)
sb.Append(String.Format("{0,6} {1,15:N0}\n", years[index],
population[index]));

Console.WriteLine(sb);

// Result:
// Year Population
//
// 2013 1,025,632
// 2014 1,105,967
// 2015 1,148,203

Alinhamento de controle
Por padrão, as cadeias de caracteres são alinhadas à direita dentro de seu campo se
você especificar uma largura de campo. Para alinhar cadeias de caracteres à esquerda
em um campo, você prefacia a largura do campo com um sinal negativo, como {0,-12}
para definir um campo alinhado à esquerda de 12 caracteres.
O exemplo a seguir é semelhante ao anterior, exceto que ele alinha à esquerda rótulos e
dados.

C#

int[] years = { 2013, 2014, 2015 };


int[] population = { 1025632, 1105967, 1148203 };
String s = String.Format("{0,-10} {1,-10}\n\n", "Year", "Population");
for (int index = 0; index < years.Length; index++)
s += String.Format("{0,-10} {1,-10:N0}\n",
years[index], population[index]);
Console.WriteLine($"\n{s}");
// Result:
// Year Population
//
// 2013 1,025,632
// 2014 1,105,967
// 2015 1,148,203

String.Format faz uso do recurso de formatação composta. Para obter mais informações,
veja Formatação de composição.

Qual método devo chamar?


ノ Expandir a tabela

Para Chamar

Formate um ou mais objetos usando Exceto para as sobrecargas que incluem um parâmetro, as
as convenções da cultura atual. sobrecargas restantes Format incluem um parâmetro
seguido por um provider String ou mais parâmetros de
objeto. Por isso, você não precisa determinar qual Format
sobrecarga pretende chamar. Seu compilador de
linguagem seleciona a sobrecarga apropriada entre as
sobrecargas que não têm um provider parâmetro, com
base em sua lista de argumentos. Por exemplo, se sua lista
de argumentos tiver cinco argumentos, o compilador
chamará o Format(String, Object[]) método.

Formate um ou mais objetos usando Cada Format sobrecarga que começa com um parâmetro
as convenções de uma cultura é seguida por um parâmetro e um provider String ou
específica. mais parâmetros de objeto. Por isso, você não precisa
determinar qual sobrecarga específica Format pretende
chamar. O compilador de linguagem seleciona a
sobrecarga apropriada entre as sobrecargas que têm um
provider parâmetro, com base na sua lista de
argumentos. Por exemplo, se sua lista de argumentos tiver
Para Chamar

cinco argumentos, o compilador chamará o


Format(IFormatProvider, String, Object[]) método.

Execute uma operação de Qualquer uma das quatro sobrecargas com um provider
formatação personalizada com uma parâmetro. O compilador seleciona a sobrecarga
implementação ou uma apropriada entre as sobrecargas que têm um provider
ICustomFormatterIFormattable parâmetro, com base em sua lista de argumentos.
implementação.

O método Format resumido


Cada sobrecarga do Format método usa o recurso de formatação composta para incluir
espaços reservados indexados baseados em zero, chamados itens de formato, em uma
cadeia de caracteres de formato composto. Em tempo de execução, cada item de
formato é substituído pela representação de cadeia de caracteres do argumento
correspondente em uma lista de parâmetros. Se o valor do argumento for null , o item
de formato será substituído por String.Empty. Por exemplo, a chamada a seguir para o
Format(String, Object, Object, Object) método inclui uma cadeia de caracteres de
formato com três itens de formato, , {0}{1}e , e {2}uma lista de argumentos com três
itens.

C#

DateTime dat = new DateTime(2012, 1, 17, 9, 30, 0);


string city = "Chicago";
int temp = -16;
string output = String.Format("At {0} in {1}, the temperature was {2}
degrees.",
dat, city, temp);
Console.WriteLine(output);
// The example displays output like the following:
// At 1/17/2012 9:30:00 AM in Chicago, the temperature was -16 degrees.

O item de formato
Um item de formato tem esta sintaxe:

txt

{index[,alignment][:formatString]}
Os colchetes indicam elementos opcionais. As chaves de abertura e fechamento são
obrigatórias. (Para incluir uma chave de abertura ou fechamento literal na cadeia de
caracteres de formato, consulte o Seção Escapando chaves no artigo Formatação
composta.)

Por exemplo, um item de formato para formatar um valor de moeda pode aparecer
assim:

C#

var value = String.Format("{0,-10:C}", 126347.89m);


Console.WriteLine(value);

Um item de formato tem os seguintes elementos:

index
O índice baseado em zero do argumento cuja representação de cadeia de caracteres
deve ser incluída nessa posição na cadeia de caracteres. Se esse argumento for null ,
uma cadeia de caracteres vazia será incluída nessa posição na cadeia de caracteres.

alinhamento
Opcional. Um inteiro assinado que indica o comprimento total do campo no qual o
argumento está inserido e se ele está alinhado à direita (um inteiro positivo) ou alinhado
à esquerda (um inteiro negativo). Se você omitir o alinhamento, a representação de
cadeia de caracteres do argumento correspondente será inserida em um campo sem
espaços à esquerda ou à direita.

Se o valor do alinhamento for menor que o comprimento do argumento a ser inserido, o


alinhamento será ignorado e o comprimento da representação da cadeia de caracteres do
argumento será usado como a largura do campo.

formatString
Opcional. Uma cadeia de caracteres que especifica o formato da cadeia de caracteres de
resultado do argumento correspondente. Se você omitir formatString, o método sem
ToString parâmetros do argumento correspondente será chamado para produzir sua

representação de cadeia de caracteres. Se você especificar formatString, o argumento


referenciado pelo item de formato deverá implementar a IFormattable interface. Os tipos
que oferecem suporte a cadeias de caracteres de formato incluem:

Todos os tipos de integral e ponto flutuante. (Veja Cadeias de caracteres de


formato numérico padrão e cadeias de caracteres de formato numérico
personalizado.)
DateTime e DateTimeOffset. (Veja Cadeias de caracteres de formato de data e hora
padrão e cadeias de caracteres de formato de data e hora personalizadas.)

Todos os tipos de enumeração. (Veja Sequências de Formato de Enumeração.)

valores TimeSpan. (Veja Cadeias de caracteres de formato TimeSpan padrão e


cadeias de caracteres de formato TimeSpan personalizadas.)

GUIDs. (Veja o Guid.ToString(String) método.)

No entanto, observe que qualquer tipo personalizado pode implementar IFormattable


ou estender a implementação de IFormattable um tipo existente.

O exemplo a seguir usa os alignment argumentos e formatString para produzir saída


formatada.

C#

// Create array of 5-tuples with population data for three U.S. cities,
1940-1950.
Tuple<string, DateTime, int, DateTime, int>[] cities =
{ Tuple.Create("Los Angeles", new DateTime(1940, 1, 1), 1504277,
new DateTime(1950, 1, 1), 1970358),
Tuple.Create("New York", new DateTime(1940, 1, 1), 7454995,
new DateTime(1950, 1, 1), 7891957),
Tuple.Create("Chicago", new DateTime(1940, 1, 1), 3396808,
new DateTime(1950, 1, 1), 3620962),
Tuple.Create("Detroit", new DateTime(1940, 1, 1), 1623452,
new DateTime(1950, 1, 1), 1849568) };

// Display header
var header = String.Format("{0,-12}{1,8}{2,12}{1,8}{2,12}{3,14}\n",
"City", "Year", "Population", "Change (%)");
Console.WriteLine(header);
foreach (var city in cities) {
var output = String.Format("{0,-12}{1,8:yyyy}{2,12:N0}{3,8:yyyy}{4,12:N0}
{5,14:P1}",
city.Item1, city.Item2, city.Item3, city.Item4,
city.Item5,
(city.Item5 - city.Item3)/ (double)city.Item3);
Console.WriteLine(output);
}
// The example displays the following output:
// City Year Population Year Population Change (%)
//
// Los Angeles 1940 1,504,277 1950 1,970,358 31.0 %
// New York 1940 7,454,995 1950 7,891,957 5.9 %
// Chicago 1940 3,396,808 1950 3,620,962 6.6 %
// Detroit 1940 1,623,452 1950 1,849,568 13.9 %
Como os argumentos são formatados
Os itens de formato são processados sequencialmente a partir do início da cadeia de
caracteres. Cada item de formato tem um índice que corresponde a um objeto na lista
de argumentos do método. O Format método recupera o argumento e deriva sua
representação de cadeia de caracteres da seguinte maneira:

Se o argumento for null , o método será insere na cadeia de caracteres de


String.Empty resultado. Você não precisa se preocupar em lidar com argumentos
nulos NullReferenceException .

Se você chamar a sobrecarga e a Format(IFormatProvider, String, Object[]) provider


implementação do IFormatProvider.GetFormat objeto retornar uma implementação
não nula ICustomFormatter , o argumento será passado para seu
ICustomFormatter.Format(String, Object, IFormatProvider) método. Se o item de
formato incluir um argumento formatString , ele será passado como o primeiro
argumento para o método. Se a implementação estiver disponível e produzir uma
cadeia de caracteres não nula, essa cadeia de caracteres será retornada como a
representação de cadeia de caracteres do argumento, caso contrário, a
ICustomFormatter próxima etapa será executada.

Se o argumento implementa a IFormattable interface, sua IFormattable.ToString


implementação é chamada.

O método sem ToString parâmetros do argumento, que substitui ou herda de


uma implementação de classe base, é chamado.

Para obter um exemplo que intercepta chamadas para o método e permite que você
veja quais informações o ICustomFormatter.FormatFormat método passa para um
método de formatação para cada item de formato em uma cadeia de caracteres de
formato composto, consulte Exemplo: um provedor de interceptação e formatador de
numeração romana.

Para obter mais informações, consulte Processando pedido.

Itens de formato que têm o mesmo índice


O Format método lança uma FormatException exceção se o índice de um item de índice
for maior ou igual ao número de argumentos na lista de argumentos. No entanto, pode
incluir mais itens de formato do que argumentos, format desde que vários itens de
formato tenham o mesmo índice. Na chamada para o método no exemplo a seguir, a
lista de argumentos tem um único argumento, mas a cadeia de caracteres de formato
inclui dois itens de formato: um exibe o valor decimal de um número e o Format(String,
Object) outro exibe seu valor hexadecimal.

C#

short[] values= { Int16.MinValue, -27, 0, 1042, Int16.MaxValue };


Console.WriteLine("{0,10} {1,10}\n", "Decimal", "Hex");
foreach (short value in values)
{
string formatString = String.Format("{0,10:G}: {0,10:X}", value);
Console.WriteLine(formatString);
}
// The example displays the following output:
// Decimal Hex
//
// -32768: 8000
// -27: FFE5
// 0: 0
// 1042: 412
// 32767: 7FFF

Formato e cultura
Geralmente, os objetos na lista de argumentos são convertidos em suas representações
de cadeia de caracteres usando as convenções da cultura atual, que é retornada pela
CultureInfo.CurrentCulture propriedade. Você pode controlar esse comportamento
chamando uma das sobrecargas de Format que inclui um provider parâmetro. O
provider parâmetro é uma IFormatProvider implementação que fornece informações de

formatação personalizadas e específicas da cultura que são usadas para moderar o


processo de formatação.

A IFormatProvider interface tem um único membro, , GetFormatque é responsável por


retornar o objeto que fornece informações de formatação. O .NET tem três
IFormatProvider implementações que fornecem formatação específica da cultura:

CultureInfo. Seu GetFormat método retorna um objeto específico de cultura para


formatar valores numéricos e um objeto específico
DateTimeFormatInfoNumberFormatInfo de cultura para formatar valores de data e
hora.
DateTimeFormatInfo, que é usado para formatação específica da cultura de valores
de data e hora. Seu GetFormat método retorna a si mesmo.
NumberFormatInfo, que é usado para formatação específica da cultura de valores
numéricos. Seu GetFormat(Type) método retorna a si mesmo.
Operações de formatação personalizadas
Você também pode chamar qualquer uma das sobrecargas do Format método que têm
um provider parâmetro de tipo IFormatProvider para executar operações de formatação
personalizadas. Por exemplo, você pode formatar um inteiro como um número de
identificação ou como um número de telefone. Para executar a formatação
personalizada, seu provider argumento deve implementar as IFormatProvider interfaces
e ICustomFormatter . Quando o método é passado uma ICustomFormatter
implementação como o argumento, o FormatFormat provider método chama sua
IFormatProvider.GetFormat implementação e solicita um objeto do tipo
ICustomFormatter. Em seguida, ele chama o método do objeto retornado
ICustomFormatter para formatar cada item de Format formato na cadeia de caracteres
composta passada para ele.

Para obter mais informações sobre como fornecer soluções de formatação


personalizadas, consulte Como: Definir e usar provedores de formato numérico
personalizado e ICustomFormatter. Para obter um exemplo que converte inteiros em
números personalizados formatados, consulte Exemplo: uma operação de formatação
personalizada. Para obter um exemplo que converte bytes não assinados em algarismos
romanos, consulte Exemplo: um provedor de interceptação e formatador de algarismos
romanos.

Exemplo: uma operação de formatação personalizada


Este exemplo define um provedor de formato que formata um valor inteiro como um
número de conta de cliente no formato x-xxxxx-xx.

C#

using System;

public class TestFormatter


{
public static void Main()
{
int acctNumber = 79203159;
Console.WriteLine(String.Format(new CustomerFormatter(), "{0}",
acctNumber));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:G}",
acctNumber));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:S}",
acctNumber));
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:P}",
acctNumber));
try {
Console.WriteLine(String.Format(new CustomerFormatter(), "{0:X}",
acctNumber));
}
catch (FormatException e) {
Console.WriteLine(e.Message);
}
}
}

public class CustomerFormatter : IFormatProvider, ICustomFormatter


{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(string format,


object arg,
IFormatProvider formatProvider)
{
if (! this.Equals(formatProvider))
{
return null;
}
else
{
if (String.IsNullOrEmpty(format))
format = "G";

string customerString = arg.ToString();


if (customerString.Length < 8)
customerString = customerString.PadLeft(8, '0');

format = format.ToUpper();
switch (format)
{
case "G":
return customerString.Substring(0, 1) + "-" +
customerString.Substring(1, 5) + "-" +
customerString.Substring(6);
case "S":
return customerString.Substring(0, 1) + "/" +
customerString.Substring(1, 5) + "/" +
customerString.Substring(6);
case "P":
return customerString.Substring(0, 1) + "." +
customerString.Substring(1, 5) + "." +
customerString.Substring(6);
default:
throw new FormatException(
String.Format("The '{0}' format specifier is not
supported.", format));
}
}
}
}
// The example displays the following output:
// 7-92031-59
// 7-92031-59
// 7/92031/59
// 7.92031.59
// The 'X' format specifier is not supported.

Exemplo: um provedor de interceptação e um formatador


de numeração romana
Este exemplo define um provedor de formato personalizado que implementa as
ICustomFormatter interfaces e IFormatProvider para fazer duas coisas:

Ele exibe os parâmetros passados para sua ICustomFormatter.Format


implementação. Isso nos permite ver quais parâmetros o Format(IFormatProvider,
String, Object[]) método está passando para a implementação de formatação
personalizada para cada objeto que ele tenta formatar. Isso pode ser útil quando
você estiver depurando seu aplicativo.

Se o objeto a ser formatado for um valor de byte não assinado que deve ser
formatado usando a cadeia de caracteres de formato padrão "R", o formatador
personalizado formatará o valor numérico como um algarismo romano.

C#

using System;
using System.Globalization;

public class InterceptProvider : IFormatProvider, ICustomFormatter


{
public object GetFormat(Type formatType)
{
if (formatType == typeof(ICustomFormatter))
return this;
else
return null;
}

public string Format(String format, Object obj, IFormatProvider provider)


{
// Display information about method call.
string formatString = format ?? "<null>";
Console.WriteLine("Provider: {0}, Object: {1}, Format String: {2}",
provider.GetType().Name, obj ?? "<null>",
formatString);
if (obj == null) return String.Empty;

// If this is a byte and the "R" format string, format it with Roman
numerals.
if (obj is Byte && formatString.ToUpper().Equals("R")) {
Byte value = (Byte) obj;
int remainder;
int result;
String returnString = String.Empty;

// Get the hundreds digit(s)


result = Math.DivRem(value, 100, out remainder);
if (result > 0)
returnString = new String('C', result);
value = (Byte) remainder;
// Get the 50s digit
result = Math.DivRem(value, 50, out remainder);
if (result == 1)
returnString += "L";
value = (Byte) remainder;
// Get the tens digit.
result = Math.DivRem(value, 10, out remainder);
if (result > 0)
returnString += new String('X', result);
value = (Byte) remainder;
// Get the fives digit.
result = Math.DivRem(value, 5, out remainder);
if (result > 0)
returnString += "V";
value = (Byte) remainder;
// Add the ones digit.
if (remainder > 0)
returnString += new String('I', remainder);

// Check whether we have too many X characters.


int pos = returnString.IndexOf("XXXX");
if (pos >= 0) {
int xPos = returnString.IndexOf("L");
if (xPos >= 0 & xPos == pos - 1)
returnString = returnString.Replace("LXXXX", "XC");
else
returnString = returnString.Replace("XXXX", "XL");
}
// Check whether we have too many I characters
pos = returnString.IndexOf("IIII");
if (pos >= 0)
if (returnString.IndexOf("V") >= 0)
returnString = returnString.Replace("VIIII", "IX");
else
returnString = returnString.Replace("IIII", "IV");

return returnString;
}
// Use default for all other formatting.
if (obj is IFormattable)
return ((IFormattable) obj).ToString(format,
CultureInfo.CurrentCulture);
else
return obj.ToString();
}
}

public class Example


{
public static void Main()
{
int n = 10;
double value = 16.935;
DateTime day = DateTime.Now;
InterceptProvider provider = new InterceptProvider();
Console.WriteLine(String.Format(provider, "{0:N0}: {1:C2} on {2:d}\n",
n, value, day));
Console.WriteLine(String.Format(provider, "{0}: {1:F}\n", "Today: ",
(DayOfWeek) DateTime.Now.DayOfWeek));
Console.WriteLine(String.Format(provider, "{0:X}, {1}, {2}\n",
(Byte) 2, (Byte) 12, (Byte) 199));
Console.WriteLine(String.Format(provider, "{0:R}, {1:R}, {2:R}\n",
(Byte) 2, (Byte) 12, (Byte) 199));
}
}
// The example displays the following output:
// Provider: InterceptProvider, Object: 10, Format String: N0
// Provider: InterceptProvider, Object: 16.935, Format String: C2
// Provider: InterceptProvider, Object: 1/31/2013 6:10:28 PM, Format
String: d
// 10: $16.94 on 1/31/2013
//
// Provider: InterceptProvider, Object: Today: , Format String: <null>
// Provider: InterceptProvider, Object: Thursday, Format String: F
// Today: : Thursday
//
// Provider: InterceptProvider, Object: 2, Format String: X
// Provider: InterceptProvider, Object: 12, Format String: <null>
// Provider: InterceptProvider, Object: 199, Format String: <null>
// 2, 12, 199
//
// Provider: InterceptProvider, Object: 2, Format String: R
// Provider: InterceptProvider, Object: 12, Format String: R
// Provider: InterceptProvider, Object: 199, Format String: R
// II, XII, CXCIX

Perguntas frequentes
Por que você recomenda a interpolação de cadeia de
caracteres sobre chamadas para o String.Format método?
A interpolação de cadeia de caracteres é:

Mais flexível. Ele pode ser usado em qualquer cadeia de caracteres sem exigir uma
chamada para um método que ofereça suporte à formatação composta. Caso
contrário, você precisará chamar o Format método ou outro método que ofereça
suporte à formatação composta, como Console.WriteLine ou
StringBuilder.AppendFormat.

Mais legível. Como a expressão a ser inserida em uma cadeia de caracteres aparece
na expressão interpolada em vez de em uma lista de argumentos, as cadeias de
caracteres interpoladas são muito mais fáceis de codificar e ler. Devido à sua maior
legibilidade, as cadeias de caracteres interpoladas podem substituir não apenas
chamadas para métodos de formato composto, mas também podem ser usadas
em operações de concatenação de cadeia de caracteres para produzir código mais
conciso e claro.

Uma comparação dos dois exemplos de código a seguir ilustra a superioridade de


cadeias de caracteres interpoladas sobre concatenação de cadeia de caracteres e
chamadas para métodos de formatação composta. O uso de várias operações de
concatenação de cadeia de caracteres no exemplo a seguir produz código detalhado e
difícil de ler.

C#

string[] names = { "Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska",


"Yuma" };
string output = names[0] + ", " + names[1] + ", " + names[2] + ", " +
names[3] + ", " + names[4] + ", " + names[5] + ", " +
names[6];

output += "\n";
var date = DateTime.Now;
output += String.Format("It is {0:t} on {0:d}. The day of the week is {1}.",
date, date.DayOfWeek);
Console.WriteLine(output);
// The example displays the following output:
// Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
// It is 10:29 AM on 1/8/2018. The day of the week is Monday.

Por outro lado, o uso de cadeias de caracteres interpoladas no exemplo a seguir produz
um código muito mais claro e conciso do que a instrução de concatenação de cadeia de
caracteres e a chamada para o Format método no exemplo anterior.
C#

string[] names = { "Balto", "Vanya", "Dakota", "Samuel", "Koani", "Yiska",


"Yuma" };
string output = $"{names[0]}, {names[1]}, {names[2]}, {names[3]},
{names[4]}, " +
$"{names[5]}, {names[6]}";

var date = DateTime.Now;


output += $"\nIt is {date:t} on {date:d}. The day of the week is
{date.DayOfWeek}.";
Console.WriteLine(output);
// The example displays the following output:
// Balto, Vanya, Dakota, Samuel, Koani, Yiska, Yuma
// It is 10:29 AM on 1/8/2018. The day of the week is Monday.

Onde posso encontrar as cadeias de caracteres de


formato predefinidas?
Para todos os tipos integrais e de ponto flutuante, consulte Cadeias de caracteres
de formato numérico padrão e Cadeias de caracteres de formato numérico
personalizado.

Para obter valores de data e hora, consulte Cadeias de caracteres de formato de


data e hora padrão e Cadeias de caracteres de formato de data e hora
personalizadas.

Para valores de enumeração, consulte Cadeias de caracteres de formato de


enumeração.

Para TimeSpan obter valores, consulte Cadeias de caracteres de formato TimeSpan


padrão e Cadeias de caracteres de formato TimeSpan personalizadas.

Para Guid obter valores, consulte a seção Comentários da página de


Guid.ToString(String) referência.

Como posso controlar o alinhamento das cadeias de


caracteres de resultado que substituem os itens de
formato?
A sintaxe geral de um item de formato é:

txt
{index[,alignment][: formatString]}

onde alinhamento é um inteiro assinado que define a largura do campo. Se esse valor
for negativo, o texto no campo será alinhado à esquerda. Se for positivo, o texto é
alinhado à direita.

Como posso controlar o número de dígitos após o


separador decimal?
Todas as cadeias de caracteres de formato numérico padrão, exceto "D" (que é usado
apenas com inteiros), "G", "R" e "X", permitem um especificador de precisão que define o
número de dígitos decimais na cadeia de caracteres de resultado. O exemplo a seguir
usa cadeias de caracteres de formato numérico padrão para controlar o número de
dígitos decimais na cadeia de caracteres de resultado.

C#

object[] values = { 1603, 1794.68235, 15436.14 };


string result;
foreach (var value in values)
{
result = String.Format("{0,12:C2} {0,12:E3} {0,12:F4} {0,12:N3}
{1,12:P2}\n",
Convert.ToDouble(value), Convert.ToDouble(value)
/ 10000);
Console.WriteLine(result);
}
// The example displays output like the following:
// $1,603.00 1.603E+003 1603.0000 1,603.000 16.03
%
//
// $1,794.68 1.795E+003 1794.6824 1,794.682 17.95
%
//
// $15,436.14 1.544E+004 15436.1400 15,436.140 154.36
%

Se você estiver usando uma cadeia de caracteres de formato numérico personalizada,


use o especificador de formato "0" para controlar o número de dígitos decimais na
cadeia de caracteres de resultado, como mostra o exemplo a seguir.

C#

decimal value = 16309.5436m;


string result = String.Format("{0,12:#.00000} {0,12:0,000.00}
{0,12:000.00#}",
value);
Console.WriteLine(result);
// The example displays the following output:
// 16309.54360 16,309.54 16309.544

Como posso controlar o número de dígitos integrais?


Por padrão, as operações de formatação exibem apenas dígitos integrais diferentes de
zero. Se você estiver formatando inteiros, poderá usar um especificador de precisão com
as cadeias de caracteres de formato padrão "D" e "X" para controlar o número de
dígitos.

C#

int value = 1326;


string result = String.Format("{0,10:D6} {0,10:X8}", value);
Console.WriteLine(result);
// The example displays the following output:
// 001326 0000052E

Você pode preencher um número inteiro ou de ponto flutuante com zeros à esquerda
para produzir uma cadeia de caracteres de resultado com um número especificado de
dígitos integrais usando o especificador de formato numérico personalizado "0", como
mostra o exemplo a seguir.

C#

int value = 16342;


string result = String.Format("{0,18:00000000} {0,18:00000000.000}
{0,18:000,0000,000.0}",
value);
Console.WriteLine(result);
// The example displays the following output:
// 00016342 00016342.000 0,000,016,342.0

Quantos itens posso incluir na lista de formatos?


Não há limite prático. O segundo parâmetro do método é marcado com o
ParamArrayAttribute atributo , que permite incluir uma lista delimitada ou uma matriz de
objetos como sua lista de Format(IFormatProvider, String, Object[]) formatos.

Como incluir chaves literais ("{" e "}") na cadeia de


caracteres de resultados?
Por exemplo, como você impede que a chamada de método a seguir lance uma
FormatException exceção?

C#

result = String.Format("The text has {0} '{' characters and {1} '}'
characters.",
nOpen, nClose);

Uma única chave de abertura ou fechamento é sempre interpretada como o início ou o


fim de um item de formato. Para ser interpretada literalmente, é preciso fugir. Você
escapa de uma chave adicionando outra chave ("{{" e "}}" em vez de "{" e "}"), como na
seguinte chamada de método:

C#

string result;
int nOpen = 1;
int nClose = 2;
result = String.Format("The text has {0} '{{' characters and {1} '}}'
characters.",
nOpen, nClose);
Console.WriteLine(result);

No entanto, mesmo aparelhos escapados são facilmente mal interpretados.


Recomendamos que você inclua chaves na lista de formatos e use itens de formato para
inseri-los na cadeia de caracteres de resultados, como mostra o exemplo a seguir.

C#

string result;
int nOpen = 1;
int nClose = 2;
result = String.Format("The text has {0} '{1}' characters and {2} '{3}'
characters.",
nOpen, "{", nClose, "}");
Console.WriteLine(result);

Por que minha chamada para o método String.Format


lança um FormatException?
A causa mais comum da exceção é que o índice de um item de formato não
corresponde a um objeto na lista de formatos. Normalmente, isso indica que você
numerou incorretamente os índices de itens de formato ou esqueceu de incluir um
objeto na lista de formatos. A tentativa de incluir um caractere de chave esquerda ou
direita não escapado também lança um FormatExceptionarquivo . Ocasionalmente, a
exceção é o resultado de um erro de digitação; Por exemplo, um erro típico é digitar
incorretamente "[" (o colchete esquerdo) em vez de "{" (o colchete esquerdo).

Se o método
Format(System.IFormatProvider,System.String,System.Object[])
oferece suporte a matrizes de parâmetros, por que meu
código lança uma exceção quando uso uma matriz?
Por exemplo, o código a seguir lança uma FormatException exceção:

C#

Random rnd = new Random();


int[] numbers = new int[4];
int total = 0;
for (int ctr = 0; ctr <= 2; ctr++)
{
int number = rnd.Next(1001);
numbers[ctr] = number;
total += number;
}
numbers[3] = total;
Console.WriteLine("{0} + {1} + {2} = {3}", numbers);

Este é um problema de resolução de sobrecarga do compilador. Como o compilador


não pode converter uma matriz de inteiros em uma matriz de objeto, ele trata a matriz
inteira como um único argumento, por isso chama o Format(String, Object) método. A
exceção é lançada porque há quatro itens de formato, mas apenas um único item na
lista de formatos.

Como nem Visual Basic nem C# pode converter uma matriz inteira em uma matriz de
objeto, você precisa executar a conversão antes de chamar o Format(String, Object[])
método. O exemplo a seguir fornece uma implementação.

C#

Random rnd = new Random();


int[] numbers = new int[4];
int total = 0;
for (int ctr = 0; ctr <= 2; ctr++)
{
int number = rnd.Next(1001);
numbers[ctr] = number;
total += number;
}
numbers[3] = total;
object[] values = new object[numbers.Length];
numbers.CopyTo(values, 0);
Console.WriteLine("{0} + {1} + {2} = {3}", values);

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.String.Intern
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Common Language Runtime conserva o armazenamento de cadeias de caracteres


mantendo uma tabela, chamada pool interno, que contém uma única referência a cada
cadeia de caracteres literal exclusiva declarada ou criada programaticamente em seu
programa. Consequentemente, uma instância de uma cadeia de caracteres literal com
um valor específico só existe uma vez no sistema.

Por exemplo, se você atribuir a mesma cadeia de caracteres literal a várias variáveis, o
tempo de execução recuperará a mesma referência à cadeia de caracteres literal do pool
interno e a atribuirá a cada variável.

O Intern método usa o pool interno para procurar uma cadeia de caracteres igual ao
valor de str . Se essa cadeia de caracteres existir, sua referência no pool interno será
retornada. Se a cadeia de caracteres não existir, uma referência a str for adicionada ao
pool interno, essa referência será retornada.

No exemplo a seguir, a cadeia de caracteres s1, que tem um valor de "MyTest", já está
internada porque é um literal no programa. A System.Text.StringBuilder classe gera um
novo objeto de cadeia de caracteres que tem o mesmo valor que s1. Uma referência a
essa cadeia de caracteres é atribuída a s2. O Intern método procura uma cadeia de
caracteres que tenha o mesmo valor que s2. Como essa cadeia de caracteres existe, o
método retorna a mesma referência atribuída a s1. Essa referência é então atribuída a
s3. As referências s1 e s2 comparam-se desiguais por se referirem a objetos diferentes;
As referências S1 e S3 comparam-se iguais porque se referem à mesma cadeia de
caracteres.

C#

string s1 = "MyTest";
string s2 = new StringBuilder().Append("My").Append("Test").ToString();
string s3 = String.Intern(s2);
Console.WriteLine((Object)s2==(Object)s1); // Different references.
Console.WriteLine((Object)s3==(Object)s1); // The same reference.

Compare esse método com o IsInterned método.

No exemplo a seguir, a variável recebe uma referência a , e a variável str1 str2 recebe a
referência a String.EmptyString.Empty que é retornada chamando o Intern método
depois de converter um StringBuilder objeto cujo valor é String.Empty em uma cadeia
de caracteres. Em seguida, as referências contidas e str1 str2 são comparadas para a
igualdade. str1 e str2 são iguais.

C#

string str1 = String.Empty;


string str2 = String.Empty;

StringBuilder sb = new StringBuilder().Append(String.Empty);


str2 = String.Intern(sb.ToString());

if ((object)str1 == (object)str2)
Console.WriteLine("The strings are equal.");
else
Console.WriteLine("The strings are not equal.");

Considerações sobre o desempenho


Se você estiver tentando reduzir a quantidade total de memória que seu aplicativo
aloca, tenha em mente que internar uma cadeia de caracteres tem dois efeitos colaterais
indesejados. Primeiro, a memória alocada para objetos internos String provavelmente
não será liberada até que o CLR (Common Language Runtime) termine. O motivo é que
a referência do CLR ao objeto internado String pode persistir depois que seu aplicativo,
ou até mesmo seu domínio de aplicativo, termina. Em segundo lugar, para estagiar uma
cadeia de caracteres, você deve primeiro criar a cadeia de caracteres. A memória usada
pelo String objeto ainda deve ser alocada, mesmo que a memória acabe sendo coletada
como lixo.

O CompilationRelaxations.NoStringInterning membro de enumeração marca um


assembly como não exigindo estágio literal de cadeia de caracteres. Você pode aplicar
NoStringInterning a um assembly usando o CompilationRelaxationsAttribute atributo .
Além disso, quando você usa Ngen.exe (Native Image Generator) para compilar um
assembly antes do tempo de execução, as cadeias de caracteres não são internadas
entre módulos.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método System.String.IsNullOrEmpty
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

IsNullOrEmpty é um método conveniente que permite testar simultaneamente se um


String é ou seu valor é null String.Empty. É equivalente ao seguinte código:

C#

bool TestForNullOrEmpty(string s)
{
bool result;
result = s == null || s == string.Empty;
return result;
}

string s1 = null;
string s2 = "";
Console.WriteLine(TestForNullOrEmpty(s1));
Console.WriteLine(TestForNullOrEmpty(s2));

// The example displays the following output:


// True
// True

Você pode usar o IsNullOrWhiteSpace método para testar se uma cadeia de caracteres é
, seu valor é null String.Empty, ou consiste apenas em caracteres de espaço em branco.

O que é uma cadeia de caracteres nula?


Uma cadeia de caracteres é null se não lhe tiver sido atribuído um valor (em C++ e
Visual Basic) ou se tiver sido explicitamente atribuído um valor de null . Embora o
recurso de formatação composta possa manipular normalmente uma cadeia de
caracteres nula, como mostra o exemplo a seguir, tentar chamar um se seus membros
lançarem um NullReferenceExceptionarquivo .

C#

String s = null;

Console.WriteLine("The value of the string is '{0}'", s);

try
{
Console.WriteLine("String length is {0}", s.Length);
}
catch (NullReferenceException e)
{
Console.WriteLine(e.Message);
}

// The example displays the following output:


// The value of the string is ''
// Object reference not set to an instance of an object.

O que é uma cadeia de caracteres vazia?


Uma cadeia de caracteres estará vazia se for explicitamente atribuída uma cadeia de
caracteres vazia ("") ou String.Empty. Uma cadeia de caracteres vazia tem a Length de 0.
O exemplo a seguir cria uma cadeia de caracteres vazia e exibe seu valor e seu
comprimento.

C#

String s = "";
Console.WriteLine("The length of '{0}' is {1}.", s, s.Length);

// The example displays the following output:


// The length of '' is 0.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Char struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para essa API.

A Char estrutura representa pontos de código Unicode usando codificação UTF-16. O valor de um Char objeto é seu valor numérico
(ordinal) de 16 bits.

Se você não estiver familiarizado com Unicode, valores escalares, pontos de código, pares substitutos, UTF-16 e o Rune tipo, consulte
Introdução à codificação de caracteres no .NET.

Este artigo examina a relação entre um objeto e um Char caractere e discute algumas tarefas comuns executadas com Char
instâncias. Recomendamos que você considere o Rune tipo, introduzido no .NET Core 3.0, como uma alternativa para Char executar
algumas dessas tarefas.

Objetos Char, caracteres Unicode e cadeias de caracteres


Um String objeto é uma coleção sequencial de estruturas que representa uma cadeia de caracteres de Char texto. A maioria dos
caracteres Unicode pode ser representada por um único Char objeto, mas um caractere codificado como um caractere base, par
substituto e/ou sequência de caracteres combinando é representado por vários Char objetos. Por esse motivo, uma Char estrutura
em um objeto não é necessariamente equivalente a um String único caractere Unicode.

Várias unidades de código de 16 bits são usadas para representar caracteres Unicode únicos nos seguintes casos:

Glifos, que podem consistir em um único caractere ou em um caractere base seguido por um ou mais caracteres combinados.
Por exemplo, o caractere ä é representado por um objeto cuja unidade de código é U+0061 seguido por um CharChar objeto
cuja unidade de código é U+0308. (O caractere ä também pode ser definido por um único Char objeto que tem uma unidade
de código de U+00E4.) O exemplo a seguir ilustra que o caractere ä consiste em dois Char objetos.

C#

using System;
using System.IO;

public class Example1


{
public static void Main()
{
StreamWriter sw = new StreamWriter("chars1.txt");
char[] chars = { '\u0061', '\u0308' };
string strng = new String(chars);
sw.WriteLine(strng);
sw.Close();
}
}
// The example produces the following output:
// ä

Caracteres fora do Plano Multilíngue Básico (BMP) Unicode. O Unicode suporta dezesseis planos, além do BMP, que representa
o plano 0. Um ponto de código Unicode é representado em UTF-32 por um valor de 21 bits que inclui o plano. Por exemplo,
U+1D160 representa o caractere SÍMBOLO MUSICAL OITAVA NOTA. Como a codificação UTF-16 tem apenas 16 bits, os
caracteres fora do BMP são representados por pares substitutos em UTF-16. O exemplo a seguir ilustra que o equivalente UTF-
32 de U+1D160, o caractere MUSICAL SYMBOL EIGHTH NOTE, é U+D834 U+DD60. U+D834 é o substituto alto; os substitutos
elevados variam de U+D800 a U+DBFF. U+DD60 é o substituto baixo; os substitutos baixos variam de U+DC00 a U+DFFF.

C#

using System;
using System.IO;

public class Example3


{
public static void Main()
{
StreamWriter sw = new StreamWriter(@".\chars2.txt");
int utf32 = 0x1D160;
string surrogate = Char.ConvertFromUtf32(utf32);
sw.WriteLine("U+{0:X6} UTF-32 = {1} ({2}) UTF-16",
utf32, surrogate, ShowCodePoints(surrogate));
sw.Close();
}

private static string ShowCodePoints(string value)


{
string retval = null;
foreach (var ch in value)
retval += String.Format("U+{0:X4} ", Convert.ToUInt16(ch));

return retval.Trim();
}
}
// The example produces the following output:
// U+01D160 UTF-32 = ð (U+D834 U+DD60) UTF-16

Personagens e categorias de personagens


Cada caractere Unicode ou par substituto válido pertence a uma categoria Unicode. No .NET, as categorias Unicode são
representadas por membros da UnicodeCategory enumeração e incluem valores como UnicodeCategory.CurrencySymbol, e
UnicodeCategory.SpaceSeparator, UnicodeCategory.LowercaseLetterpor exemplo.

Para determinar a categoria Unicode de um caractere, chame o GetUnicodeCategory método. Por exemplo, o exemplo a seguir
chama o GetUnicodeCategory para exibir a categoria Unicode de cada caractere em uma cadeia de caracteres. O exemplo funciona
corretamente somente se não houver pares substitutos na String instância.

C#

using System;
using System.Globalization;

class Example
{
public static void Main()
{
// Define a string with a variety of character categories.
String s = "The red car drove down the long, narrow, secluded road.";
// Determine the category of each character.
foreach (var ch in s)
Console.WriteLine("'{0}': {1}", ch, Char.GetUnicodeCategory(ch));
}
}
// The example displays the following output:
// 'T': UppercaseLetter
// 'h': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'r': LowercaseLetter
// 'e': LowercaseLetter
// 'd': LowercaseLetter
// ' ': SpaceSeparator
// 'c': LowercaseLetter
// 'a': LowercaseLetter
// 'r': LowercaseLetter
// ' ': SpaceSeparator
// 'd': LowercaseLetter
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'v': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'd': LowercaseLetter
// 'o': LowercaseLetter
// 'w': LowercaseLetter
// 'n': LowercaseLetter
// ' ': SpaceSeparator
// 't': LowercaseLetter
// 'h': LowercaseLetter
// 'e': LowercaseLetter
// ' ': SpaceSeparator
// 'l': LowercaseLetter
// 'o': LowercaseLetter
// 'n': LowercaseLetter
// 'g': LowercaseLetter
// ',': OtherPunctuation
// ' ': SpaceSeparator
// 'n': LowercaseLetter
// 'a': LowercaseLetter
// 'r': LowercaseLetter
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'w': LowercaseLetter
// ',': OtherPunctuation
// ' ': SpaceSeparator
// 's': LowercaseLetter
// 'e': LowercaseLetter
// 'c': LowercaseLetter
// 'l': LowercaseLetter
// 'u': LowercaseLetter
// 'd': LowercaseLetter
// 'e': LowercaseLetter
// 'd': LowercaseLetter
// ' ': SpaceSeparator
// 'r': LowercaseLetter
// 'o': LowercaseLetter
// 'a': LowercaseLetter
// 'd': LowercaseLetter
// '.': OtherPunctuation

Internamente, para caracteres fora do intervalo ASCII (U+0000 a U+00FF), o GetUnicodeCategory método depende das categorias
Unicode relatadas CharUnicodeInfo pela classe. A partir do .NET Framework 4.6.2, os caracteres Unicode são classificados com base
no padrão Unicode, versão 8.0.0 . Em versões do .NET Framework do .NET Framework 4 para o .NET Framework 4.6.1, eles são
classificados com base no padrão Unicode, versão 6.3.0 .

Caracteres e elementos de texto


Como um único caractere pode ser representado por vários Char objetos, nem sempre é significativo trabalhar com objetos
individuais Char . Por exemplo, o exemplo a seguir converte os pontos de código Unicode que representam os números do Egeu de
zero a 9 em unidades de código codificadas UTF-16. Como ele equipara Char erroneamente objetos a caracteres, ele relata
incorretamente que a cadeia de caracteres resultante tem 20 caracteres.

C#

using System;

public class Example5


{
public static void Main()
{
string result = String.Empty;
for (int ctr = 0x10107; ctr <= 0x10110; ctr++) // Range of Aegean numbers.
result += Char.ConvertFromUtf32(ctr);

Console.WriteLine("The string contains {0} characters.", result.Length);


}
}
// The example displays the following output:
// The string contains 20 characters.

Você pode fazer o seguinte para evitar a suposição de que um objeto representa um Char único caractere:

Você pode trabalhar com um String objeto em sua totalidade em vez de trabalhar com seus caracteres individuais para
representar e analisar o conteúdo linguístico.

Você pode usar String.EnumerateRunes como mostrado no exemplo a seguir:

C#

int CountLetters(string s)
{
int letterCount = 0;

foreach (Rune rune in s.EnumerateRunes())


{
if (Rune.IsLetter(rune))
{ letterCount++; }
}

return letterCount;
}

Você pode usar a StringInfo classe para trabalhar com elementos de texto em vez de objetos individuais Char . O exemplo a
seguir usa o objeto para contar o StringInfo número de elementos de texto em uma cadeia de caracteres que consiste nos
números do mar Egeu de zero a nove. Como ele considera um par substituto um único caractere, ele relata corretamente que a
cadeia de caracteres contém dez caracteres.

C#

using System;
using System.Globalization;

public class Example4


{
public static void Main()
{
string result = String.Empty;
for (int ctr = 0x10107; ctr <= 0x10110; ctr++) // Range of Aegean numbers.
result += Char.ConvertFromUtf32(ctr);

StringInfo si = new StringInfo(result);


Console.WriteLine("The string contains {0} characters.",
si.LengthInTextElements);
}
}
// The example displays the following output:
// The string contains 10 characters.

Se uma cadeia de caracteres contiver um caractere base que tenha um ou mais caracteres combinados, você poderá chamar o
String.Normalize método para converter a subcadeia de caracteres em uma única unidade de código codificada em UTF-16. O
exemplo a seguir chama o método para converter o caractere base U+0061 (LATIN SMALL LETTER A) e combinar o
String.Normalize caractere U+0308 (COMBINANDO DIAERESIS) em U+00E4 (LATIN SMALL LETTER A COM DIAERESIS).

C#

using System;

public class Example2


{
public static void Main()
{
string combining = "\u0061\u0308";
ShowString(combining);

string normalized = combining.Normalize();


ShowString(normalized);
}

private static void ShowString(string s)


{
Console.Write("Length of string: {0} (", s.Length);
for (int ctr = 0; ctr < s.Length; ctr++)
{
Console.Write("U+{0:X4}", Convert.ToUInt16(s[ctr]));
if (ctr != s.Length - 1) Console.Write(" ");
}
Console.WriteLine(")\n");
}
}
// The example displays the following output:
// Length of string: 2 (U+0061 U+0308)
//
// Length of string: 1 (U+00E4)

Operações comuns
A Char estrutura fornece métodos para comparar Char objetos, converter o valor do objeto atual Char em um objeto de outro tipo e
determinar a categoria Unicode de um Char objeto:

ノ Expandir a tabela

Para fazer Use estes System.Char métodos


isso

Comparar CompareTo e Equals


Char
objetos

Converter ConvertFromUtf32
um ponto
de código Veja também o Rune tipo.
em uma
cadeia de
caracteres

Converter Para um único caractere: Convert.ToInt32(Char)


um objeto
ou um par Para um par substituto ou um caractere em uma cadeia de caracteres: Char.ConvertToUtf32
substituto
de objetos Veja também o Rune tipo.
em um
Char
ponto de
Char
código

Obter a GetUnicodeCategory
categoria
Unicode Consulte também Rune.GetUnicodeCategory.
de um
caractere

Determine IsControl, , IsHighSurrogate, , , , ,


se um IsNumberIsLowerIsLowSurrogateIsPunctuationIsSeparatorIsDigitIsSurrogatePairIsLetterIsUpperIsLetterOrDigitIsSurrogateIsSymboleIsWhiteSpace
caractere
está em Consulte também os Rune métodos correspondentes no tipo.
uma
categoria
Unicode
específica,
como
dígito,
letra,
pontuação,
caractere
de
controle e
assim por
diante

Converter GetNumericValue
um objeto
que Consulte também Rune.GetNumericValue.
representa
um
número
em um
Char tipo
de valor
numérico

Converter Parse e TryParse


um
caractere
em uma
cadeia de
caracteres
em um
Para fazer Use estes System.Char métodos
isso

Char
objeto

Converter ToString
um objeto
em um
CharString
objeto

Alterar ToLower, ToLowerInvariant, ToUpper e ToUpperInvariant


maiúsculas
e Consulte também os Rune métodos correspondentes no tipo.
minúsculas
de um
Char
objeto

Valores de Char e interoperabilidade


Quando um tipo gerenciado, que é representado como uma unidade de código codificada Unicode UTF-16, é passado para código
não gerenciado Char , o marshaller de interoperabilidade converte o conjunto de caracteres em ANSI por padrão. Você pode aplicar o
atributo a declarações de invocação de plataforma e o DllImportAttribute atributo a uma declaração de interoperabilidade COM para
controlar qual conjunto de StructLayoutAttribute caracteres um tipo empacotado Char usa.

6 Colaborar conosco no GitHub Comentários do .NET


A fonte deste conteúdo pode ser O .NET é um projeto código aberto. Selecione um link para fornecer
encontrada no GitHub, onde você comentários:
também pode criar e revisar
problemas e solicitações de pull. Para  Abrir um problema de documentação
obter mais informações, confira o
nosso guia para colaboradores.  Fornecer comentários sobre o produto
System.StringComparer classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Um objeto derivado da classe incorpora StringComparer operações de comparação,


igualdade e código hash baseadas em cadeia de caracteres que levam em conta as
regras de comparação específicas de maiúsculas e minúsculas e culturais. Você pode
usar a StringComparer classe para criar uma comparação específica de tipo para
classificar os elementos em uma coleção genérica. Classes como Hashtable, ,
Dictionary<TKey,TValue>SortedListe SortedList<TKey,TValue> usam a StringComparer
classe para fins de classificação.

Uma operação de comparação representada pela classe é definida como diferenciando


maiúsculas de minúsculas ou sem diferenciação de maiúsculas e minúsculas
StringComparer e usa regras de comparação de palavras (sensíveis à cultura) ou ordinais
(sem distinção de cultura). Para obter mais informações sobre regras de comparação de
palavras e ordinais, consulte System.Globalization.CompareOptions.

7 Observação

Você pode baixar a Tabela de Elementos de Agrupamento Unicode Padrão, a


versão mais recente da tabela de peso de classificação. A versão específica da
tabela de peso de classificação depende da versão das bibliotecas International
Components for Unicode instaladas no sistema. Para obter informações sobre
versões de ICU e as versões Unicode que elas implementam, veja Baixar ICU .

Para o .NET Framework no Windows, você pode baixar as Tabelas de Peso de


Classificação , um conjunto de arquivos de texto que contêm informações sobre
os pesos de caracteres usados em operações de classificação e comparação.

Propriedades implementadas
Você pode estar confuso sobre como usar as StringComparer propriedades da classe
por causa de uma aparente contradição. A StringComparer classe é declarada
( MustInherit no Visual Basic), o que significa que seus membros podem ser invocados
somente em um objeto de uma classe derivada abstract da StringComparer classe. A
contradição é que cada propriedade da classe é declarada static ( Shared no Visual
Basic), o StringComparer que significa que a propriedade pode ser invocada sem
primeiro criar uma classe derivada.

Você pode chamar uma propriedade diretamente porque cada propriedade realmente
retorna uma instância de uma StringComparer classe anônima que é derivada da
StringComparer classe. Consequentemente, o tipo de cada valor de propriedade é , que
é StringComparera classe base da classe anônima, não o tipo da classe anônima em si.
Cada StringComparer propriedade de classe retorna um StringComparer objeto que
oferece suporte a maiúsculas e minúsculas predefinidas e regras de comparação.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Text.Encoding classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A classe Encoding representa uma codificação de caracteres.

A codificação é o processo de transformar um conjunto de caracteres Unicode em uma


sequência de bytes. Por outro lado, a decodificação é o processo de transformar uma
sequência de bytes codificados em um conjunto de caracteres Unicode. Para obter
informações sobre os formatos de transformação Unicode (UTFs) e outras codificações
suportadas pelo Encoding, consulte Codificação de caracteres no .NET.

Encoding destina-se a operar em caracteres Unicode em vez de dados binários


arbitrários, como matrizes de bytes. Se você deve codificar dados binários arbitrários em
texto, você deve usar um protocolo como uuencode, que é implementado por métodos
como Convert.ToBase64CharArray.

O .NET fornece as seguintes implementações da Encoding classe para oferecer suporte a


codificações Unicode atuais e outras codificações:

ASCIIEncoding codifica caracteres Unicode como caracteres ASCII únicos de 7 bits.


Essa codificação só oferece suporte a valores de caracteres entre U+0000 e
U+007F. Página de código 20127. Também disponível através da ASCII
propriedade.

UTF7Encoding codifica caracteres Unicode usando a codificação UTF-7. Essa


codificação oferece suporte a todos os valores de caracteres Unicode. Página de
código 65000. Também disponível através da UTF7 propriedade.

UTF8Encoding codifica caracteres Unicode usando a codificação UTF-8. Essa


codificação oferece suporte a todos os valores de caracteres Unicode. Página de
código 65001. Também disponível através da UTF8 propriedade.

UnicodeEncoding codifica caracteres Unicode usando a codificação UTF-16. As


ordens de bytes little endian e big endian são suportadas. Também disponível
através da propriedade e da UnicodeBigEndianUnicode propriedade.

UTF32Encoding codifica caracteres Unicode usando a codificação UTF-32. As


ordens de bytes little endian (página de código 12000) e big endian (página de
código 12001) são suportadas. Também disponível através da UTF32 propriedade.
A Encoding classe destina-se principalmente a converter entre codificações diferentes e
Unicode. Muitas vezes, uma das classes Unicode derivadas é a escolha correta para seu
aplicativo.

Use o método para obter outras codificações e chame o GetEncodingGetEncodings


método para obter uma lista de todas as codificações.

Lista de codificações
A tabela a seguir lista as codificações suportadas pelo .NET. Ele lista o número da página
de código de cada codificação e os valores da codificação EncodingInfo.Name e
EncodingInfo.DisplayName propriedades. Uma marca de seleção na coluna de suporte
do .NET Framework, suporte do .NET Core ou suporte do .NET 5 e posterior indica que
a página de código é suportada nativamente por essa implementação do .NET,
independentemente da plataforma subjacente. Para o .NET Framework, a
disponibilidade de outras codificações listadas na tabela depende do sistema
operacional. Para .NET Core e .NET 5 e versões posteriores, outras codificações estão
disponíveis usando a System.Text.CodePagesEncodingProvider classe ou derivando da
System.Text.EncodingProvider classe.

7 Observação

As páginas de código cuja EncodingInfo.Name propriedade corresponde a uma


norma internacional não estão necessariamente em conformidade total com essa
norma.

ノ Expandir a tabela

Página Nome Nome de Suporte ao Suporte Suporte ao


de exibição .NET do .NET .NET 5 e
código Framework Core posterior

37 IBM037 IBM EBCDIC (EUA-


Canadá)

437 IBM437 OEM Estados


Unidos

500 IBM500 IBM EBCDIC


(Internacional)

708 ASMO-708 Árabe (ASMO 708)

720 DOS-720 Árabe (DOS)


Página Nome Nome de Suporte ao Suporte Suporte ao
737 IBM737 Grego (DOS)
de exibição .NET do .NET .NET 5 e
código
775 IBM775 Báltico (DOS) Framework Core posterior

850 IBM 850 Europa Ocidental


(DOS)

852 IBM852 Europa Central


(DOS)

855 IBM855 OEM cirílico

857 IBM857 Turco (DOS)

858 IBM00858 OEM Latim


Multilingue I

860 IBM860 Português (DOS)

861 IBM861 Islandês (DOS)

862 DOS-862 Hebraico (DOS)

863 IBM863 Francês


Canadense (DOS)

864 IBM864 Árabe (864)

865 IBM865 Nórdico (DOS)

866 cp866 Cirílico (DOS)

869 IBM869 Grego, Moderno


(DOS)

870 IBM870 IBM EBCDIC


(Latim
Multilingue-2)

874 janelas-874 Tailandês


(Windows)

875 CP875 IBM EBCDIC


(Grego Moderno)

932 shift_jis Japonês (Shift-JIS)

936 gb2312 Chinês ✓


simplificado
(GB2312)

949 ks_c_5601-1987 Coreano


Página Nome Nome de Suporte ao Suporte Suporte ao
de exibição .NET do .NET .NET 5 e
código Framework Core posterior

950 big5 Chinês Tradicional


(Big5)

1026 IBM1026 IBM EBCDIC


(Latim turco-5)

1047 IBM01047 IBM Latin-1

1140 IBM01140 IBM EBCDIC (EUA-


Canadá-Euro)

1141 IBM01141 IBM EBCDIC


(Alemanha-Euro)

1142 IBM01142 IBM EBCDIC


(Dinamarca-
Noruega-Euro)

1143 IBM01143 IBM EBCDIC


(Finlândia-Suécia-
Euro)

1144 IBM01144 IBM EBCDIC


(Itália-Euro)

1145 IBM01145 IBM EBCDIC


(Espanha-Euro)

1146 IBM01146 IBM EBCDIC (UK-


Euro)

1147 IBM01147 IBM EBCDIC


(França-Euro)

1148 IBM01148 IBM EBCDIC


(Internacional-
Euro)

1149 IBM01149 IBM EBCDIC


(islandês-euro)

1200 UTF-16 Unicode ✓ ✓ ✓

1201 unicodeFFFE Unicode (Big ✓ ✓ ✓


endian)

1250 janelas-1250 Europa Central


(Windows)
Página Nome Nome de Suporte ao Suporte Suporte ao
de exibição .NET do .NET .NET 5 e
código Framework Core posterior

1251 janelas-1251 Cirílico (Windows)

1252 Windows-1252 Oeste da Europa ✓


(Windows)

1253 janelas-1253 Grego (Windows)

1254 janelas-1254 Turco (Windows)

1255 janelas-1255 Hebraico


(Windows)

1256 janelas-1256 Árabe (Windows)

1257 janelas-1257 Báltico (Windows)

1258 janelas-1258 Vietnamita


(Windows)

1361 Joabe Coreano (Johab)

10000 Macintosh Europa Ocidental


(Mac)

10001 x-mac-japonês Japonês (Mac)

10002 X-Mac- Chinês Tradicional


Chinesetrad (Mac)

10003 x-mac-coreano Coreano (Mac) ✓

10004 x-mac-árabe Árabe (Mac)

10005 x-mac-hebraico Hebraico (Mac)

10006 x-mac-grego Grego (Mac)

10007 x-mac-cirílico Cirílico (Mac)

10008 x-mac- Chinês ✓


chinesesimp simplificado (Mac)

10010 x-mac-romeno Romeno (Mac)

10017 x-mac-ucraniano Ucraniano (Mac)

10021 X-Mac-Tailandês Tailandês (Mac)


Página Nome Nome de Suporte ao Suporte Suporte ao
de exibição .NET do .NET .NET 5 e
código Framework Core posterior

10029 X-Mac-CE Europa Central


(Mac)

10079 X-Mac-Islandês Islandês (Mac)

10081 x-mac-turco Turco (Mac)

10082 x-mac-croata Croata (Mac)

12000 UTF-32 Unicode (UTF-32) ✓ ✓ ✓

12001 utf-32BE Unicode (UTF-32 ✓ ✓ ✓


Big endian)

20000 x-Chinês-CNS Chinês Tradicional


(CNS)

20001 x-cp20001 TCA Taiwan

20002 x-Chinês-Eten Chinês Tradicional


(Eten)

20003 x-cp20003 IBM5550 Taiwan

20004 x-cp20004 TeleText Taiwan

20005 x-cp20005 Wang Taiwan

20105 x-IA5 Europa Ocidental


(IA5)

20106 x-IA5-Alemão Alemão (IA5)

20107 x-IA5-Sueco Sueco (IA5)

20108 x-IA5-Norueguês Norueguês (IA5)

20127 EUA-ASCII EUA-ASCII ✓ ✓ ✓

20261 x-cp20261 T.61

20269 x-cp20269 ISO-6937

20273 IBM273 IBM EBCDIC


(Alemanha)

20277 IBM277 IBM EBCDIC


(Dinamarca-
Noruega)
Página Nome Nome de Suporte ao Suporte Suporte ao
de exibição .NET do .NET .NET 5 e
código Framework Core posterior

20278 IBM278 IBM EBCDIC


(Finlândia-Suécia)

20280 IBM280 IBM EBCDIC (Itália)

20284 IBM284 IBM EBCDIC


(Espanha)

20285 IBM285 IBM EBCDIC


(REINO UNIDO)

20290 IBM290 IBM EBCDIC


(katakana japonês)

20297 IBM297 IBM EBCDIC


(França)

20420 IBM420 IBM EBCDIC


(árabe)

20423 IBM423 IBM EBCDIC


(grego)

20424 IBM424 IBM EBCDIC


(hebraico)

20833 x-EBCDIC- IBM EBCDIC


KoreanExtended (Coreano
Estendido)

20838 IBM-Tailandês IBM EBCDIC


(Tailandês)

20866 Koi8-R Cirílico (KOI8-R)

20871 IBM871 IBM EBCDIC


(islandês)

20880 IBM880 IBM EBCDIC (russo


cirílico)

20905 IBM905 IBM EBCDIC


(turco)

20924 IBM00924 IBM Latin-1

20932 EUC-JP Japonês (JIS 0208-


1990 e 0212-1990)
Página Nome Nome de Suporte ao Suporte Suporte ao
de exibição .NET do .NET .NET 5 e
código Framework Core posterior

20936 x-cp20936 Chinês ✓


simplificado
(GB2312-80)

20949 x-cp20949 Wansung coreano ✓

21025 CP1025 IBM EBCDIC


(cirílico sérvio-
búlgaro)

21866 koi8-u Cirílico (KOI8-U)

28591 ISO-8859-1 Europa Ocidental ✓ ✓ ✓


(ISO)

28592 ISO-8859-2 Europa Central


(ISO)

28593 ISO-8859-3 Latim 3 (ISO)

28594 ISO-8859-4 Báltico (ISO)

28595 ISO-8859-5 Cirílico (ISO)

28596 ISO-8859-6 Árabe (ISO)

28597 ISO-8859-7 Grego (ISO)

28598 ISO-8859-8 Hebraico (ISO- ✓


Visual)

28599 ISO-8859-9 Turco (ISO)

28603 ISO-8859-13 Estónio (ISO)

28605 ISO-8859-15 Latim 9 (ISO)

29001 x-Europa Europa

38598 ISO-8859-8-I Hebraico (ISO- ✓


Lógico)

50220 ISO-2022-JP Japonês (JIS) ✓

50221 csISO2022JP Japonês (JIS-Allow ✓


1 byte Kana)

50222 ISO-2022-JP Japonês (JIS-Allow ✓


1 byte Kana -
Página Nome Nome de Suporte ao Suporte Suporte ao
de exibição .NET do .NET .NET 5 e
código Framework Core posterior

SO/SI)

50225 ISO-2022-KR Coreano (ISO) ✓

50227 x-cp50227 Chinês ✓


simplificado (ISO-
2022)

51932 EUC-JP Japonês (EUC) ✓

51936 EUC-CN Chinês ✓


simplificado (EUC)

51949 EUC-KR Coreano (EUC) ✓

52936 HZ-GB-2312 Chinês ✓


simplificado (HZ)

54936 GB18030 Chinês ✓


simplificado
(GB18030)

57002 x-iscii-de ISCII Devanagari ✓

57003 x-iscii-be ISCII Bengali ✓

57004 x-iscii-ta ISCII Tamil ✓

57005 x-iscii-te ISCII Telugu ✓

57006 x-iscii-as ISCII Assamese ✓

57007 x-iscii-ou ISCII Oriya ✓

57008 x-iscii-ka ISCII Kannada ✓

57009 x-iscii-ma ISCII Malaiala ✓

57010 x-iscii-gu ISCII Gujarati ✓

57011 x-iscii-pa ISCII Punjabi ✓

65000 UTF-7 Unicode (UTF-7) ✓ ✓

65001 utf-8 Unicode (UTF-8) ✓ ✓ ✓

O exemplo a seguir chama os métodos e GetEncoding(String) para obter a


GetEncoding(Int32) codificação de página de código grego (Windows). Ele compara os
Encoding objetos retornados pelas chamadas de método para mostrar que eles são
iguais e, em seguida, mapeia exibe o ponto de código Unicode e o valor de página de
código correspondente para cada caractere no alfabeto grego.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
Encoding enc = Encoding.GetEncoding(1253);
Encoding altEnc = Encoding.GetEncoding("windows-1253");
Console.WriteLine("{0} = Code Page {1}: {2}", enc.EncodingName,
altEnc.CodePage, enc.Equals(altEnc));
string greekAlphabet = "Α α Β β Γ γ Δ δ Ε ε Ζ ζ Η η " +
"Θ θ Ι ι Κ κ Λ λ Μ μ Ν ν Ξ ξ " +
"Ο ο Π π Ρ ρ Σ σ ς Τ τ Υ υ " +
"Φ φ Χ χ Ψ ψ Ω ω";
Console.OutputEncoding = Encoding.UTF8;
byte[] bytes = enc.GetBytes(greekAlphabet);
Console.WriteLine("{0,-12} {1,20} {2,20:X2}", "Character",
"Unicode Code Point", "Code Page 1253");
for (int ctr = 0; ctr < bytes.Length; ctr++) {
if (greekAlphabet[ctr].Equals(' '))
continue;

Console.WriteLine("{0,-12} {1,20} {2,20:X2}", greekAlphabet[ctr],


GetCodePoint(greekAlphabet[ctr]), bytes[ctr]);
}
}

private static string GetCodePoint(char ch)


{
string retVal = "u+";
byte[] bytes = Encoding.Unicode.GetBytes(ch.ToString());
for (int ctr = bytes.Length - 1; ctr >= 0; ctr--)
retVal += bytes[ctr].ToString("X2");

return retVal;
}
}
// The example displays the following output:
// Character Unicode Code Point Code Page 1253
// Α u+0391 C1
// α u+03B1 E1
// Β u+0392 C2
// β u+03B2 E2
// Γ u+0393 C3
// γ u+03B3 E3
// Δ u+0394 C4
// δ u+03B4 E4
// Ε u+0395 C5
// ε u+03B5 E5
// Ζ u+0396 C6
// ζ u+03B6 E6
// Η u+0397 C7
// η u+03B7 E7
// Θ u+0398 C8
// θ u+03B8 E8
// Ι u+0399 C9
// ι u+03B9 E9
// Κ u+039A CA
// κ u+03BA EA
// Λ u+039B CB
// λ u+03BB EB
// Μ u+039C CC
// μ u+03BC EC
// Ν u+039D CD
// ν u+03BD ED
// Ξ u+039E CE
// ξ u+03BE EE
// Ο u+039F CF
// ο u+03BF EF
// Π u+03A0 D0
// π u+03C0 F0
// Ρ u+03A1 D1
// ρ u+03C1 F1
// Σ u+03A3 D3
// σ u+03C3 F3
// ς u+03C2 F2
// Τ u+03A4 D4
// τ u+03C4 F4
// Υ u+03A5 D5
// υ u+03C5 F5
// Φ u+03A6 D6
// φ u+03C6 F6
// Χ u+03A7 D7
// χ u+03C7 F7
// Ψ u+03A8 D8
// ψ u+03C8 F8
// Ω u+03A9 D9
// ω u+03C9 F9

Se os dados a serem convertidos estiverem disponíveis apenas em blocos sequenciais


(como dados lidos de um fluxo) ou se a quantidade de dados for tão grande que precise
ser dividida em blocos menores, você deverá usar o ou o fornecido pelo GetDecoder
método ou o GetEncoderDecoderEncoder método, respectivamente, de uma classe
derivada.

Os codificadores UTF-16 e UTF-32 podem usar a ordem de bytes big endian (byte mais
significativo primeiro) ou a ordem de bytes little endian (byte menos significativo
primeiro). Por exemplo, a letra maiúscula latina A (U+0041) é serializada da seguinte
forma (em hexadecimal):

UTF-16 big endian byte ordem: 00 41


UTF-16 pequena ordem de bytes endian: 41 00
UTF-32 big endian byte ordem: 00 00 00 41
UTF-32 pequena ordem de bytes endian: 41 00 00 00

Geralmente, é mais eficiente armazenar caracteres Unicode usando a ordem de bytes


nativa. Por exemplo, é melhor usar a pequena ordem de bytes endian em plataformas
little endian, como computadores Intel.

O GetPreamble método recupera uma matriz de bytes que inclui a marca de ordem de
bytes (BOM). Se essa matriz de bytes for prefixada a um fluxo codificado, ela ajudará o
decodificador a identificar o formato de codificação usado.

Para obter mais informações sobre a ordem de bytes e a marca de ordem de bytes,
consulte O padrão Unicode na home page do Unicode.

Observe que as classes de codificação permitem erros:

Mude silenciosamente para um personagem "?"


Use um caractere "mais adequado".
Mude para um comportamento específico do aplicativo por meio do uso das
EncoderFallback classes e com o caractere DecoderFallback de substituição
Unicode U+FFFD.

Você deve lançar uma exceção em qualquer erro de fluxo de dados. Um aplicativo usa
um sinalizador "throwonerror" quando aplicável ou usa as EncoderExceptionFallback
classes e DecoderExceptionFallback . O fallback de melhor ajuste geralmente não é
recomendado porque pode causar perda de dados ou confusão e é mais lento do que
simples substituições de caracteres. Para codificações ANSI, o melhor comportamento
de ajuste é o padrão.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Propriedade
System.Text.Encoding.Default
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

2 Aviso

Computadores diferentes podem usar codificações diferentes como padrão, e a


codificação padrão pode ser alterada em um único computador. Se você usar a
Encoding.Default codificação para codificar e decodificar dados transmitidos entre
computadores ou recuperados em momentos diferentes no mesmo computador,
ele poderá converter esses dados incorretamente. Além disso, a codificação
retornada pela propriedade usa o Default fallback de melhor ajuste para mapear
caracteres sem suporte para caracteres suportados pela página de código. Por
esses motivos, o uso da codificação padrão não é recomendado. Para garantir que
os bytes codificados sejam decodificados corretamente, você deve usar uma
codificação Unicode, como UTF8Encoding ou UnicodeEncoding. Você também
pode usar um protocolo de nível superior para garantir que o mesmo formato seja
usado para codificação e decodificação.

.NET Framework
No .NET Framework, a propriedade sempre obtém a página de código ativa do sistema
e cria um Encoding objeto que corresponde a Default ela. A página de código ativa
pode ser uma página de código ANSI, que inclui o conjunto de caracteres ASCII junto
com caracteres adicionais que variam de acordo com a página de código. Como todas
as Default codificações baseadas em páginas de código ANSI perdem dados, considere
usar a Encoding.UTF8 codificação. UTF-8 é muitas vezes idêntico no intervalo U+00 a
U+7F, mas pode codificar caracteres fora do intervalo ASCII sem perda.

.NET Core
No .NET Core, a Default propriedade sempre retorna o UTF8Encodingarquivo . O UTF-8
é suportado em todos os sistemas operacionais (Windows, Linux e macOS) nos quais os
aplicativos .NET Core são executados.
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Text.RegularExpressions.Regex
classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A Regex classe representa . Mecanismo de expressão regular da NET. Ele pode ser usado
para analisar rapidamente grandes quantidades de texto para encontrar padrões de
caracteres específicos; para extrair, editar, substituir ou excluir substrings de texto; e
para adicionar as cadeias de caracteres extraídas a uma coleção para gerar um relatório.

7 Observação

Se você quiser validar uma cadeia de caracteres determinando se ela está em


conformidade com um padrão específico, você pode usar a
System.Configuration.RegexStringValidator classe.

Para usar expressões regulares, defina o padrão que deseja identificar em um fluxo de
texto usando a sintaxe documentada em Linguagem de Expressão Regular - Referência
Rápida. Em seguida, você pode instanciar opcionalmente um Regex objeto. Finalmente,
você chama um método que executa alguma operação, como substituir texto que
corresponde ao padrão de expressão regular ou identificar uma correspondência de
padrão.

7 Observação

Para obter alguns padrões de expressão regular comuns, consulte Exemplos de


expressão regular. Há também uma série de bibliotecas on-line de padrões de
expressão regular, como a de Regular-Expressions.info .

Para saber mais sobre a linguagem de expressão regular, confira Linguagem de


expressão regular - referência rápida ou faça download e imprima um dos seguintes
folhetos:

Referência rápida no formato Word (.docx) Referência rápida em formato PDF (.pdf)

Métodos Regex vs. String


A System.String classe inclui vários métodos de pesquisa e comparação que você pode
usar para executar a correspondência de padrões com texto. Por exemplo, os métodos ,
e e determinam se uma ocorrência de cadeia de caracteres contém uma subcadeia de
caracteres especificada, e os String.ContainsString.IndexOfmétodos ,
String.LastIndexOfString.IndexOfAnyString.EndsWith, e
String.StartsWithString.LastIndexOfAny retornam a posição inicial de uma subcadeia de
caracteres especificada em uma cadeia de caracteres. Use os System.String métodos da
classe quando você estiver procurando por uma cadeia de caracteres específica. Use a
Regex classe quando estiver procurando um padrão específico em uma cadeia de
caracteres. Para obter mais informações e exemplos, consulte Expressões regulares do
.NET.

Métodos estáticos versus métodos de instância


Depois de definir um padrão de expressão regular, você pode fornecê-lo ao mecanismo
de expressão regular de duas maneiras:

Instanciando um Regex objeto que representa a expressão regular. Para fazer isso,
você passa o padrão de expressão regular para um Regex construtor. Um Regex
objeto é imutável: quando você instancia um Regex objeto com uma expressão
regular, a expressão regular desse objeto não pode ser alterada.

Fornecendo a expressão regular e o texto para pesquisar em um static ( Shared


no Visual Basic) Regex método. Isso permite que você use uma expressão regular
sem criar explicitamente um Regex objeto.

Todos os Regex métodos de identificação de padrões incluem sobrecargas estáticas e


de instância.

O mecanismo de expressão regular deve compilar um padrão específico antes que o


padrão possa ser usado. Como Regex os objetos são imutáveis, esse é um procedimento
único que ocorre quando um construtor de classe ou um Regex método estático é
chamado. Para eliminar a necessidade de compilar repetidamente uma única expressão
regular, o mecanismo de expressão regular armazena em cache as expressões regulares
compiladas usadas em chamadas de método estático. Como resultado, os métodos de
correspondência de padrões de expressão regular oferecem desempenho comparável
para métodos estáticos e de instância. No entanto, o cache pode afetar negativamente
o desempenho nos dois casos a seguir:

Quando você usa chamadas de método estático com um grande número de


expressões regulares. Por padrão, o mecanismo de expressão regular armazena em
cache as 15 expressões regulares estáticas usadas mais recentemente. Se seu
aplicativo usa mais de 15 expressões regulares estáticas, algumas expressões
regulares devem ser recompiladas. Para evitar essa recompilação, você pode
aumentar a Regex.CacheSize propriedade.

Quando você instancia novos Regex objetos com expressões regulares que foram
compiladas anteriormente. Por exemplo, o código a seguir define uma expressão
regular para localizar palavras duplicadas em um fluxo de texto. Embora o exemplo
use uma única expressão regular, ele instancia um novo Regex objeto para
processar cada linha de texto. Isso resulta na recompilação da expressão regular
com cada iteração do loop.

C#

StreamReader sr = new StreamReader(filename);


string input;
string pattern = @"\b(\w+)\s\1\b";
while (sr.Peek() >= 0)
{
input = sr.ReadLine();
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);
MatchCollection matches = rgx.Matches(input);
if (matches.Count > 0)
{
Console.WriteLine("{0} ({1} matches):", input, matches.Count);
foreach (Match match in matches)
Console.WriteLine(" " + match.Value);
}
}
sr.Close();

Para impedir a recompilação, você deve instanciar um único Regex objeto que seja
acessível a todo o código que o exija, conforme mostrado no exemplo reescrito a
seguir.

C#

StreamReader sr = new StreamReader(filename);


string input;
string pattern = @"\b(\w+)\s\1\b";
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase);

while (sr.Peek() >= 0)


{
input = sr.ReadLine();
MatchCollection matches = rgx.Matches(input);
if (matches.Count > 0)
{
Console.WriteLine("{0} ({1} matches):", input, matches.Count);
foreach (Match match in matches)
Console.WriteLine(" " + match.Value);
}
}
sr.Close();

Executar operações de expressão regular


Se você decidir instanciar um Regex objeto e chamar seus métodos ou chamar métodos
estáticos, a classe oferece a Regex seguinte funcionalidade de correspondência de
padrão:

Validação de uma correspondência. Você chama o IsMatch método para


determinar se uma correspondência está presente.

Recuperação de uma única partida. Você chama o Match método para recuperar
um Match objeto que representa a primeira correspondência em uma cadeia de
caracteres ou em parte de uma cadeia de caracteres. As correspondências
subsequentes podem ser recuperadas chamando o Match.NextMatch método.

Recuperação de todas as partidas. Você chama o Matches método para recuperar


um System.Text.RegularExpressions.MatchCollection objeto que representa todas
as correspondências encontradas em uma cadeia de caracteres ou em parte de
uma cadeia de caracteres.

Substituição de texto correspondente. Você chama o método para substituir o


Replace texto correspondente. O texto de substituição também pode ser definido
por uma expressão regular. Além disso, alguns dos Replace métodos incluem um
MatchEvaluator parâmetro que permite definir programaticamente o texto de
substituição.

Criação de uma matriz de cadeia de caracteres que é formada a partir de partes de


uma cadeia de caracteres de entrada. Você chama o Split método para dividir uma
cadeia de caracteres de entrada em posições definidas pela expressão regular.

Além de seus métodos de correspondência de padrões, a Regex classe inclui vários


métodos de propósito especial:

O Escape método escapa de quaisquer caracteres que podem ser interpretados


como operadores de expressão regular em uma expressão regular ou cadeia de
caracteres de entrada.

O Unescape método remove esses caracteres de escape.


O CompileToAssembly método cria um assembly que contém expressões regulares
predefinidas. O .NET contém exemplos desses assemblies de finalidade especial no
System.Web.RegularExpressions namespace.

Definir um valor de tempo limite


O .NET oferece suporte a uma linguagem de expressão regular completa que fornece
poder e flexibilidade substanciais na correspondência de padrões. No entanto, o poder
e a flexibilidade têm um custo: o risco de mau desempenho. Expressões regulares com
desempenho ruim são surpreendentemente fáceis de criar. Em alguns casos, as
operações de expressão regular que dependem de retrocesso excessivo podem parecer
parar de responder quando processam texto que quase corresponde ao padrão de
expressão regular. Para obter mais informações sobre o mecanismo de expressão
regular do .NET, consulte Detalhes do comportamento da expressão regular. Para obter
mais informações sobre retrocesso excessivo, consulte Backtracking.

A partir do .NET Framework 4.5, você pode definir um intervalo de tempo limite para
correspondências de expressões regulares para limitar o retrocesso excessivo.
Dependendo do padrão de expressão regular e do texto de entrada, o tempo de
execução pode exceder o intervalo de tempo limite especificado, mas não gastará mais
tempo de retrocesso do que o intervalo de tempo limite especificado. Se o mecanismo
de expressão regular atingir o tempo limite, ele lançará uma
RegexMatchTimeoutException exceção. Na maioria dos casos, isso impede que o
mecanismo de expressão regular desperdice poder de processamento ao tentar
corresponder ao texto que quase corresponde ao padrão de expressão regular. Isso
também pode indicar, no entanto, que o intervalo de tempo limite foi definido muito
baixo ou que a carga atual da máquina causou uma degradação geral no desempenho.

A forma como você lida com a exceção depende da causa da exceção. Se a exceção
ocorrer porque o intervalo de tempo limite está definido muito baixo ou devido à carga
excessiva da máquina, você pode aumentar o intervalo de tempo limite e tentar
novamente a operação correspondente. Se a exceção ocorrer porque a expressão
regular depende de retrocesso excessivo, você pode assumir que uma correspondência
não existe e, opcionalmente, você pode registrar informações que o ajudarão a
modificar o padrão de expressão regular.

Você pode definir um intervalo de tempo limite chamando o Regex(String,


RegexOptions, TimeSpan) construtor ao instanciar um objeto de expressão regular. Para
métodos estáticos, você pode definir um intervalo de tempo limite chamando uma
sobrecarga de um método correspondente que tenha um matchTimeout parâmetro. Se
você não definir um valor de tempo limite explicitamente, o valor de tempo limite
padrão será determinado da seguinte maneira:

Usando o valor de tempo limite de todo o aplicativo, se existir um. Esse pode ser
qualquer valor de tempo limite que se aplica ao domínio do aplicativo no qual o
objeto Regex é instanciado ou a chamada de método estático é feita. Você pode
definir o valor de tempo limite de todo o aplicativo chamando o método
AppDomain.SetData para atribuir a representação de cadeia de caracteres de um
valor TimeSpan à propriedade "REGEX_DEFAULT_MATCH_TIMEOUT".

Usando o valor InfiniteMatchTimeout, se nenhum valor de tempo limite de todo o


aplicativo tiver sido definido.

) Importante

Recomendamos que você defina um valor de tempo limite em todas as operações


de correspondência de padrão de expressão regular. Para obter mais informações,
consulte Práticas recomendadas para expressões regulares.

Exemplos
O exemplo a seguir usa uma expressão regular para verificar ocorrências repetidas de
palavras em uma cadeia de caracteres. A expressão \b(?<word>\w+)\s+(\k<word>)\b
regular pode ser interpretada como mostrado na tabela a seguir.

ノ Expandir a tabela

Padrão Descrição

\b Inicie a partida em um limite de palavras.

(? Corresponder um ou mais caracteres de palavra até um limite de palavra. Nomeie


<word>\w+) este grupo word capturado .

\s+ Corresponde a um ou mais caracteres de espaço em branco.

(\k<word>) Corresponder ao grupo capturado chamado word .

\b Corresponder a um limite de palavra.

C#

using System;
using System.Text.RegularExpressions;
public class Test
{
public static void Main ()
{
// Define a regular expression for repeated words.
Regex rx = new Regex(@"\b(?<word>\w+)\s+(\k<word>)\b",
RegexOptions.Compiled | RegexOptions.IgnoreCase);

// Define a test string.


string text = "The the quick brown fox fox jumps over the lazy dog
dog.";

// Find matches.
MatchCollection matches = rx.Matches(text);

// Report the number of matches found.


Console.WriteLine("{0} matches found in:\n {1}",
matches.Count,
text);

// Report on each match.


foreach (Match match in matches)
{
GroupCollection groups = match.Groups;
Console.WriteLine("'{0}' repeated at positions {1} and {2}",
groups["word"].Value,
groups[0].Index,
groups[1].Index);
}
}
}

// The example produces the following output to the console:


// 3 matches found in:
// The the quick brown fox fox jumps over the lazy dog dog.
// 'The' repeated at positions 0 and 4
// 'fox' repeated at positions 20 and 25
// 'dog' repeated at positions 49 and 53

O exemplo a seguir ilustra o uso de uma expressão regular para verificar se uma cadeia
de caracteres representa um valor de moeda ou tem o formato correto para representar
um valor de moeda. Nesse caso, a expressão regular é construída dinamicamente a
partir das propriedades , , , ,
NumberFormatInfo.NegativeSignCurrencyDecimalDigitsNumberFormatInfo.CurrencySy
mbole NumberFormatInfo.PositiveSign para a
NumberFormatInfo.CurrencyDecimalSeparatorcultura en-US. A expressão regular
resultante é ^\s*[\+-]?\s?\$?\s?(\d*\.?\d{2}?){1}$ . Essa expressão regular pode ser
interpretada como mostrado na tabela a seguir.
ノ Expandir a tabela

Padrão Descrição

^ Comece no início da cadeia de caracteres.

\s* Corresponder a zero ou mais caracteres de espaço em branco.

[\+-]? Corresponder zero ou uma ocorrência do sinal positivo ou do sinal negativo.

\s? Corresponder a zero ou a um caractere de espaço em branco.

\$? Corresponda a zero ou uma ocorrência do cifrão.

\s? Corresponder a zero ou a um caractere de espaço em branco.

\d* Corresponde a zero ou mais dígitos decimais.

\.? Corresponder a zero ou um símbolo de ponto decimal.

(\d{2})? Grupo de captura 1: corresponda a dois dígitos decimais zero ou uma vez.

(\d*\.? Corresponder ao padrão de dígitos integrais e fracionários separados por um


(\d{2})?){1} símbolo de ponto decimal pelo menos uma vez.

$ Corresponder ao final da cadeia de caracteres.

Nesse caso, a expressão regular pressupõe que uma cadeia de caracteres de moeda
válida não contém símbolos separadores de grupo e que ela não tem dígitos
fracionários ou o número de dígitos fracionários definidos pela propriedade da
CurrencyDecimalDigits cultura especificada.

C#

using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
// Get the en-US NumberFormatInfo object to build the regular
// expression pattern dynamically.
NumberFormatInfo nfi = CultureInfo.GetCultureInfo("en-
US").NumberFormat;

// Define the regular expression pattern.


string pattern;
pattern = @"^\s*[";
// Get the positive and negative sign symbols.
pattern += Regex.Escape(nfi.PositiveSign + nfi.NegativeSign) + @"]?
\s?";
// Get the currency symbol.
pattern += Regex.Escape(nfi.CurrencySymbol) + @"?\s?";
// Add integral digits to the pattern.
pattern += @"(\d*";
// Add the decimal separator.
pattern += Regex.Escape(nfi.CurrencyDecimalSeparator) + "?";
// Add the fractional digits.
pattern += @"(\d{";
// Determine the number of fractional digits in currency values.
pattern += nfi.CurrencyDecimalDigits.ToString() + "})?){1}$";

Console.WriteLine($"Pattern is {pattern}\n");

Regex rgx = new Regex(pattern);

// Define some test strings.


string[] tests = { "-42", "19.99", "0.001", "100 USD",
".34", "0.34", "1,052.21", "$10.62",
"+1.43", "-$0.23" };

// Check each test string against the regular expression.


foreach (string test in tests)
{
if (rgx.IsMatch(test))
Console.WriteLine($"{test} is a currency value.");
else
Console.WriteLine($"{test} is not a currency value.");
}
}
}
// The example displays the following output:
// Pattern is ^\s*[\+-]?\s?\$?\s?(\d*\.?(\d{2})?){1}$
//
// -42 is a currency value.
// 19.99 is a currency value.
// 0.001 is not a currency value.
// 100 USD is not a currency value.
// .34 is a currency value.
// 0.34 is a currency value.
// 1,052.21 is not a currency value.
// $10.62 is a currency value.
// +1.43 is a currency value.
// -$0.23 is a currency value.

Como a expressão regular neste exemplo é criada dinamicamente, você não sabe, em
tempo de design, se o símbolo de moeda, o sinal decimal ou os sinais positivos e
negativos da cultura especificada (en-US neste exemplo) podem ser mal interpretados
pelo mecanismo de expressão regular como operadores de linguagem de expressão
regular. Para evitar qualquer interpretação incorreta, o exemplo passa cada cadeia de
caracteres gerada dinamicamente para o Escape método.
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Método
System.Text.RegularExpressions.Regex.
Match
Artigo • 31/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O Match(String, Int32) método retorna a primeira subcadeia de caracteres que


corresponde a um padrão de expressão regular, começando em ou após a posição do
startat caractere, em uma cadeia de caracteres de entrada. O padrão de expressão

regular para o qual o Match(String, Int32) método pesquisa é definido pela chamada
para um dos construtores de Regex classe. Para obter informações sobre os elementos
de linguagem usados para criar um padrão de expressão regular, consulte Linguagem
de expressão regular - referência rápida.

O startat parâmetro
Opcionalmente, você pode especificar uma posição inicial na cadeia de caracteres
usando o startat parâmetro. Todas as correspondências que começam antes startat
na cadeia de caracteres são ignoradas. Se você não especificar uma posição inicial, a
pesquisa começará na posição padrão, que é a extremidade esquerda de em uma
pesquisa da esquerda para a direita e a extremidade direita de input input em uma
pesquisa da direita para a esquerda. Apesar de começar em startat , o índice de
qualquer correspondência retornada é relativo ao início da cadeia de caracteres.

Embora o mecanismo de expressão regular não retorne nenhuma correspondência


começando antes , ele não ignora a cadeia de caracteres antes startat startat . Isso
significa que asserções como âncoras ou asserções lookbehind ainda se aplicam à
entrada como um todo. Por exemplo, o código a seguir inclui um padrão com uma
asserção lookbehind que é satisfeita mesmo que ocorra antes do startat índice de 5 na
cadeia de caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

namespace Examples
{
public class Example3
{
public static void Main()
{
string input = "Zip code: 98052";
var regex = new Regex(@"(?<=Zip code: )\d{5}");
Match match = regex.Match(input, 5);
if (match.Success)
Console.WriteLine("Match found: {0}", match.Value);
}
}
}

// This code prints the following output:


// Match found: 98052

 Dica

Se um padrão começar com a âncora ^ , mas startat for maior que 0,


nenhuma correspondência será encontrada em uma pesquisa de linha única,
pois elas são restringidas a ^ começar no índice 0.
O \G âncora está satisfeito em startat . Por isso, se você quiser restringir uma
correspondência para que ela comece exatamente em uma posição de
caractere específica na cadeia de caracteres, ancore a expressão regular com
um à esquerda para um \G padrão da esquerda para a direita. Isso restringe a
partida, então ela deve começar exatamente em startat (ou, quando várias
correspondências são desejadas, para que as partidas sejam contíguas).

Pesquisas da direita para a esquerda


Uma pesquisa da direita para a esquerda, ou seja, quando o padrão de expressão
regular é construído com a RegexOptions.RightToLeft opção, se comporta das seguintes
maneiras:

A varredura se move na direção oposta e o padrão é correspondido de trás


(direita) para frente (esquerda).
A posição inicial padrão é a extremidade direita da cadeia de caracteres de
entrada.
Se startat for especificado, a varredura da direita para a esquerda começa no
caractere em startat - 1 (não startat ).
Quando a âncora é especificada na extremidade direita de um padrão, ela
restringe a \G (primeira) correspondência para terminar exatamente em startat -
1.

Para obter mais informações sobre pesquisas da direita para a esquerda, consulte Modo
da direita para a esquerda.

Determinar se uma correspondência foi


encontrada
Você pode determinar se o padrão de expressão regular foi encontrado na cadeia de
caracteres de entrada verificando o valor da propriedade do Success objeto
retornadoMatch. Se uma correspondência for encontrada, a propriedade do Value
objeto retornado Match conterá a subcadeia de caracteres que corresponde ao padrão
de input expressão regular. Se nenhuma correspondência for encontrada, seu valor será
String.Empty.

Primeira ou várias correspondências


Esse método retorna a primeira subcadeia de caracteres encontrada em ou após a
posição do startat caractere em input que corresponde ao padrão de expressão
regular. Você pode recuperar correspondências subsequentes chamando repetidamente
o método do Match.NextMatch objeto retornadoMatch. Você também pode recuperar
todas as correspondências em uma única chamada de método chamando o
Regex.Matches(String, Int32) método.

Exceções de tempo limite


A RegexMatchTimeoutException exceção será lançada se o tempo de execução da
operação correspondente exceder o intervalo de tempo limite especificado pelo
Regex.Regex(String, RegexOptions, TimeSpan) construtor. Se você não definir um
intervalo de tempo limite ao chamar o construtor, a exceção será lançada se a operação
exceder qualquer valor de tempo limite estabelecido para o domínio do aplicativo no
qual o Regex objeto é criado. Se nenhum tempo limite for definido na chamada do
construtor ou nas propriedades do domínio do Regex aplicativo, ou se o valor de tempo
limite for Regex.InfiniteMatchTimeout, nenhuma exceção será lançada.

6 Colaborar conosco no Comentários do .NET


GitHub
A fonte deste conteúdo pode O .NET é um projeto código aberto.
ser encontrada no GitHub, onde Selecione um link para fornecer
você também pode criar e comentários:
revisar problemas e solicitações
de pull. Para obter mais  Abrir um problema de
informações, confira o nosso documentação
guia para colaboradores.
 Fornecer comentários sobre o
produto
System.Text.Rune struct
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Uma Rune instância representa um valor escalar Unicode, o que significa qualquer
ponto de código excluindo o intervalo substituto (U+D800.. U+DFFF). Os construtores e
operadores de conversão do tipo validam a entrada, para que os consumidores possam
chamar as APIs supondo que a instância subjacente Rune esteja bem formada.

Se você não estiver familiarizado com os termos valor escalar Unicode, ponto de código,
intervalo substituto e bem formado, consulte Introdução à codificação de caracteres no
.NET.

Quando usar o tipo Runa


Considere usar o tipo se o Rune seu código:

Chama APIs que exigem valores escalares Unicode


Manipula explicitamente pares substitutos

APIs que exigem valores escalares Unicode


Se seu código iterar pelas char instâncias em um string ou um ReadOnlySpan<char> ,
alguns dos char métodos não funcionarão corretamente em char instâncias que estão
no intervalo substituto. Por exemplo, as seguintes APIs exigem um valor char escalar
para funcionar corretamente:

Char.GetNumericValue
Char.GetUnicodeCategory
Char.IsDigit
Char.IsLetter
Char.IsLetterOrDigit
Char.IsLower
Char.IsNumber
Char.IsPunctuation
Char.IsSymbol
Char.IsUpper
O exemplo a seguir mostra o código que não funcionará corretamente se qualquer uma
das instâncias for pontos de código substitutos char :

C#

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.


// DO NOT DO THIS IN A PRODUCTION APPLICATION.
int CountLettersBadExample(string s)
{
int letterCount = 0;

foreach (char ch in s)
{
if (char.IsLetter(ch))
{ letterCount++; }
}

return letterCount;
}

Aqui está o código equivalente que funciona com um ReadOnlySpan<char> :

C#

// THE FOLLOWING METHOD SHOWS INCORRECT CODE.


// DO NOT DO THIS IN A PRODUCTION APPLICATION.
static int CountLettersBadExample(ReadOnlySpan<char> span)
{
int letterCount = 0;

foreach (char ch in span)


{
if (char.IsLetter(ch))
{ letterCount++; }
}

return letterCount;
}

O código anterior funciona corretamente com alguns idiomas, como o inglês:

C#

CountLettersInString("Hello")
// Returns 5

Mas não funcionará corretamente para idiomas fora do Plano Multilíngue Básico, como
o Osage:
C#

CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 0

A razão pela qual esse método retorna resultados incorretos para texto Osage é que as
instâncias para letras Osage são pontos de código substitutos char . Nenhum ponto de
código substituto tem informações suficientes para determinar se é uma letra.

Se você alterar esse código para usar Rune em vez de , o método funciona corretamente
com pontos de char código fora do plano multilíngue básico:

C#

int CountLetters(string s)
{
int letterCount = 0;

foreach (Rune rune in s.EnumerateRunes())


{
if (Rune.IsLetter(rune))
{ letterCount++; }
}

return letterCount;
}

Aqui está o código equivalente que funciona com um ReadOnlySpan<char> :

C#

static int CountLetters(ReadOnlySpan<char> span)


{
int letterCount = 0;

foreach (Rune rune in span.EnumerateRunes())


{
if (Rune.IsLetter(rune))
{ letterCount++; }
}

return letterCount;
}

O código anterior conta as letras Osage corretamente:

C#
CountLettersInString("𐓏𐓘𐓻𐓘𐓻𐓟 𐒻𐓟")
// Returns 8

Código que manipula explicitamente pares substitutos


Considere usar o Rune tipo se seu código chamar APIs que operam explicitamente em
pontos de código substitutos, como os seguintes métodos:

Char.IsSurrogate
Char.IsSurrogatePair
Char.IsHighSurrogate
Char.IsLowSurrogate
Char.ConvertFromUtf32
Char.ConvertToUtf32

Por exemplo, o método a seguir tem lógica especial para lidar com pares substitutos
char :

C#

static void ProcessStringUseChar(string s)


{
Console.WriteLine("Using char");

for (int i = 0; i < s.Length; i++)


{
if (!char.IsSurrogate(s[i]))
{
Console.WriteLine($"Code point: {(int)(s[i])}");
}
else if (i + 1 < s.Length && char.IsSurrogatePair(s[i], s[i + 1]))
{
int codePoint = char.ConvertToUtf32(s[i], s[i + 1]);
Console.WriteLine($"Code point: {codePoint}");
i++; // so that when the loop iterates it's actually +2
}
else
{
throw new Exception("String was not well-formed UTF-16.");
}
}
}

Esse código é mais simples se ele usa Rune , como no exemplo a seguir:

C#
static void ProcessStringUseRune(string s)
{
Console.WriteLine("Using Rune");

for (int i = 0; i < s.Length;)


{
if (!Rune.TryGetRuneAt(s, i, out Rune rune))
{
throw new Exception("String was not well-formed UTF-16.");
}

Console.WriteLine($"Code point: {rune.Value}");


i += rune.Utf16SequenceLength; // increment the iterator by the
number of chars in this Rune
}
}

Quando não usar Rune


Você não precisa usar o Rune tipo se seu código:

Procura correspondências exatas char


Divide uma cadeia de caracteres em um valor de caractere conhecido

Usar o Rune tipo pode retornar resultados incorretos se seu código:

Conta o número de caracteres de exibição em um string

Procure correspondências exatas char


O código a seguir itera através de uma string procura por caracteres específicos,
retornando o índice da primeira correspondência. Não há necessidade de alterar esse
código para usar Rune , pois o código está procurando caracteres que são representados
por um único char .

C#

int GetIndexOfFirstAToZ(string s)
{
for (int i = 0; i < s.Length; i++)
{
char thisChar = s[i];
if ('A' <= thisChar && thisChar <= 'Z')
{
return i; // found a match
}
}

return -1; // didn't find 'A' - 'Z' in the input string


}

Dividir uma cadeia de caracteres em um conhecido char


É comum chamar string.Split e usar delimitadores como ' ' (espaço) ou ','
(vírgula), como no exemplo a seguir:

C#

string inputString = "🐂, 🐄, 🐆";


string[] splitOnSpace = inputString.Split(' ');
string[] splitOnComma = inputString.Split(',');

Não há necessidade de usar Rune aqui, porque o código está procurando caracteres
que são representados por um único char arquivo .

Contar o número de caracteres de exibição em um


string

O número de ocorrências em uma cadeia de caracteres pode não corresponder ao


número de caracteres perceptíveis pelo usuário mostrados ao exibir a cadeia de Rune
caracteres.

Como Rune as instâncias representam valores escalares Unicode, os componentes que


seguem as diretrizes de segmentação de texto Unicode podem ser usados Rune como
um bloco de construção para contar caracteres de exibição.

O StringInfo tipo pode ser usado para contar caracteres de exibição, mas não conta
corretamente em todos os cenários para implementações do .NET diferentes do .NET
5+.

Para obter mais informações, consulte Clusters de grafema.

Como instanciar um Rune


Há várias maneiras de obter uma Rune instância. Você pode usar um construtor para
criar um Rune diretamente de:

Um ponto de código.
C#

Rune a = new Rune(0x0061); // LATIN SMALL LETTER A


Rune b = new Rune(0x10421); // DESERET CAPITAL LETTER ER

Um único char .

C#

Rune c = new Rune('a');

Um par substituto char .

C#

Rune d = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL

Todos os construtores lançam um se a entrada não representa um ArgumentException


valor escalar Unicode válido.

Há Rune.TryCreate métodos disponíveis para chamadores que não querem que


exceções sejam lançadas em caso de falha.

Rune As instâncias também podem ser lidas a partir de sequências de entrada

existentes. Por exemplo, dado um que representa dados ReadOnlySpan<char> UTF-16, o


Rune.DecodeFromUtf16 método retorna a primeira Rune instância no início da extensão
de entrada. O Rune.DecodeFromUtf8 método opera de forma semelhante, aceitando
um ReadOnlySpan<byte> parâmetro que representa dados UTF-8. Existem métodos
equivalentes para ler a partir do final do span em vez do início do span.

Propriedades de consulta de um Rune


Para obter o valor de ponto de código inteiro de uma Rune instância, use a Rune.Value
propriedade.

C#

Rune rune = new Rune('\ud83d', '\udd2e'); // U+1F52E CRYSTAL BALL


int codePoint = rune.Value; // = 128302 decimal (= 0x1F52E)

Muitas das APIs estáticas disponíveis no char tipo também estão disponíveis no Rune
tipo. Por exemplo, Rune.IsWhiteSpace e são equivalentes a Char.IsWhiteSpace e
Rune.GetUnicodeCategoryChar.GetUnicodeCategory métodos. Os Rune métodos
manipulam corretamente os pares substitutos.

O código de exemplo a seguir usa uma entrada como e corta do início e do final da
extensão, tudo Rune o que não é uma ReadOnlySpan<char> letra ou um dígito.

C#

static ReadOnlySpan<char> TrimNonLettersAndNonDigits(ReadOnlySpan<char>


span)
{
// First, trim from the front.
// If any Rune can't be decoded
// (return value is anything other than "Done"),
// or if the Rune is a letter or digit,
// stop trimming from the front and
// instead work from the end.
while (Rune.DecodeFromUtf16(span, out Rune rune, out int charsConsumed)
== OperationStatus.Done)
{
if (Rune.IsLetterOrDigit(rune))
{ break; }
span = span[charsConsumed..];
}

// Next, trim from the end.


// If any Rune can't be decoded,
// or if the Rune is a letter or digit,
// break from the loop, and we're finished.
while (Rune.DecodeLastFromUtf16(span, out Rune rune, out int
charsConsumed) == OperationStatus.Done)
{
if (Rune.IsLetterOrDigit(rune))
{ break; }
span = span[..^charsConsumed];
}

return span;
}

Existem algumas diferenças de API entre char e Rune . Por exemplo:

Não Rune há equivalente a Char.IsSurrogate(Char), uma vez que Rune as instâncias


por definição nunca podem ser pontos de código substitutos.
O Rune.GetUnicodeCategory nem sempre retorna o mesmo resultado que
Char.GetUnicodeCategoryo . Ele retorna o mesmo valor que
CharUnicodeInfo.GetUnicodeCategory. Para obter mais informações, consulte os
Comentários sobre Char.GetUnicodeCategory.
Converter a Rune para UTF-8 ou UTF-16
Como a Rune é um valor escalar Unicode, ele pode ser convertido em codificação UTF-8,
UTF-16 ou UTF-32. O Rune tipo tem suporte interno para conversão para UTF-8 e UTF-
16.

O Rune.EncodeToUtf16 converte uma Rune instância em char instâncias. Para consultar


o número de instâncias que resultariam da conversão de char uma Rune instância em
UTF-16, use a Rune.Utf16SequenceLength propriedade. Existem métodos semelhantes
para a conversão UTF-8.

O exemplo a seguir converte uma instância em uma Rune char matriz. O código
pressupõe que você tenha uma Rune instância na rune variável:

C#

char[] chars = new char[rune.Utf16SequenceLength];


int numCharsWritten = rune.EncodeToUtf16(chars);

Como a é uma sequência de caracteres UTF-16, o exemplo a string seguir também


converte uma Rune instância em UTF-16:

C#

string theString = rune.ToString();

O exemplo a seguir converte uma instância em uma Rune UTF-8 matriz de bytes:

C#

byte[] bytes = new byte[rune.Utf8SequenceLength];


int numBytesWritten = rune.EncodeToUtf8(bytes);

Os Rune.EncodeToUtf16 métodos e Rune.EncodeToUtf8 retornam o número real de


elementos gravados. Eles lançam uma exceção se o buffer de destino for muito curto
para conter o resultado. Existem métodos de não-lançamento TryEncodeToUtf8 e
TryEncodeToUtf16 também para os chamadores que querem evitar exceções.

Runa no .NET versus outras linguagens


O termo "runa" não é definido no padrão Unicode. O termo remonta à criação do UTF-
8 . Rob Pike e Ken Thompson estavam procurando um termo para descrever o que
viria a ser conhecido como um ponto de código. Eles estabeleceram o termo "runa", e
a influência posterior de Rob Pike sobre a linguagem de programação Go ajudou a
popularizar o termo.

No entanto, o tipo .NET Rune não é o equivalente do tipo Go rune . Em Go, o rune tipo
é um alias para int32 . Uma runa Go destina-se a representar um ponto de código
Unicode, mas pode ser qualquer valor de 32 bits, incluindo pontos de código
substitutos e valores que não são pontos de código Unicode legais.

Para tipos semelhantes em outras linguagens de programação, consulte o tipo primitivo


char de Rust ou o tipo de Unicode.Scalar Swift, que representam valores escalares
Unicode. Eles fornecem funcionalidade semelhante ao . Rune NET e eles não permitem a
instanciação de valores que não são valores escalares Unicode legais.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Text.StringBuilder classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A StringBuilder classe representa um objeto semelhante a uma cadeia de caracteres cujo


valor é uma sequência mutável de caracteres.

StringBuilder versus tipo de cadeia de


caracteres
Embora StringBuilder ambos representem String sequências de personagens, eles são
implementados de forma diferente. String é um tipo imutável. Ou seja, cada operação
que parece modificar um String objeto na verdade cria uma nova cadeia de caracteres.

Por exemplo, a chamada para o método no exemplo C# a seguir parece alterar o


String.Concat valor de uma variável de cadeia de caracteres chamada value . Na
verdade, o método retorna um objeto que tem um value valor e endereço diferente do
value objeto que foi passado para o Concat método. Observe que o exemplo deve ser

compilado usando a opção do /unsafe compilador.

C#

using System;

public class Example7


{
public unsafe static void Main()
{
string value = "This is the first sentence" + ".";
fixed (char* start = value)
{
value = String.Concat(value, "This is the second sentence. ");
fixed (char* current = value)
{
Console.WriteLine(start == current);
}
}
}
}
// The example displays the following output:
// False
Para rotinas que executam manipulação extensiva de cadeia de caracteres (como
aplicativos que modificam uma cadeia de caracteres várias vezes em um loop), modificar
uma cadeia de caracteres repetidamente pode exigir uma penalidade de desempenho
significativa. A alternativa é usar StringBuilder, que é uma classe de cadeia de caracteres
mutável. Mutabilidade significa que, uma vez que uma instância da classe tenha sido
criada, ela pode ser modificada acrescentando, removendo, substituindo ou inserindo
caracteres. Um StringBuilder objeto mantém um buffer para acomodar expansões para a
cadeia de caracteres. Novos dados são anexados ao buffer se houver espaço disponível;
caso contrário, um novo buffer maior será alocado, os dados do buffer original serão
copiados para o novo buffer e os novos dados serão anexados ao novo buffer.

) Importante

Embora a classe geralmente ofereça melhor desempenho do que a


StringBuilderString classe, você não deve substituir String automaticamente por
StringBuilder sempre que quiser manipular cadeias de caracteres. O desempenho
depende do tamanho da cadeia de caracteres, da quantidade de memória a ser
alocada para a nova cadeia de caracteres, do sistema no qual o código está sendo
executado e do tipo de operação. Você deve estar preparado para testar seu
código para determinar se StringBuilder realmente oferece uma melhoria
significativa de desempenho.

Considere usar a String classe sob estas condições:

Quando o número de alterações que seu código fará em uma cadeia de caracteres
é pequeno. Nesses casos, StringBuilder pode oferecer melhoria de desempenho
insignificante ou nenhuma em relação ao String.

Quando você estiver executando um número fixo de operações de concatenação,


especialmente com literais de cadeia de caracteres. Nesse caso, o compilador pode
combinar as operações de concatenação em uma única operação.

Quando você tem que executar operações de pesquisa extensivas enquanto você
está construindo sua cadeia de caracteres. A StringBuilder classe não possui
métodos de pesquisa como IndexOf ou StartsWith . Você terá que converter o
objeto em um String para essas operações, e isso pode anular o StringBuilder
benefício de desempenho do uso StringBuilderdo . Para obter mais informações,
consulte a seção Pesquisar o texto em um objeto StringBuilder.

Considere usar a StringBuilder classe sob estas condições:


Quando você espera que seu código faça um número desconhecido de alterações
em uma cadeia de caracteres em tempo de design (por exemplo, quando você
estiver usando um loop para concatenar um número aleatório de cadeias de
caracteres que contêm entrada do usuário).
Quando você espera que seu código faça um número significativo de alterações
em uma cadeia de caracteres.

Como StringBuilder funciona


A StringBuilder.Length propriedade indica o número de caracteres que o StringBuilder
objeto contém no momento. Se você adicionar caracteres ao StringBuilder objeto, seu
comprimento aumentará até ser igual ao tamanho da StringBuilder.Capacity
propriedade, que define o número de caracteres que o objeto pode conter. Se o
número de caracteres adicionados fizer com que o comprimento do objeto exceda sua
capacidade atual, nova memória será alocada, o StringBuilderCapacity valor da
propriedade será dobrado, novos caracteres serão adicionados ao StringBuilder objeto e
sua Length propriedade será ajustada. A memória adicional para o objeto é alocada
dinamicamente até atingir o StringBuilder valor definido pela StringBuilder.MaxCapacity
propriedade. Quando a capacidade máxima é atingida, nenhuma memória adicional
pode ser alocada para o StringBuilder objeto, e tentar adicionar caracteres ou expandi-
lo além de sua capacidade máxima lança uma ou uma
ArgumentOutOfRangeExceptionOutOfMemoryException exceção.

O exemplo a seguir ilustra como um StringBuilder objeto aloca nova memória e


aumenta sua capacidade dinamicamente à medida que a cadeia de caracteres atribuída
ao objeto se expande. O código cria um StringBuilder objeto chamando seu construtor
padrão (sem parâmetros). A capacidade padrão desse objeto é de 16 caracteres e sua
capacidade máxima é de mais de 2 bilhões de caracteres. Anexar a cadeia de caracteres
"Esta é uma frase." resulta em uma nova alocação de memória porque o comprimento
da cadeia de caracteres (19 caracteres) excede a capacidade padrão do StringBuilder
objeto. A capacidade do objeto dobra para 32 caracteres, a nova cadeia de caracteres é
adicionada e o comprimento do objeto agora é igual a 19 caracteres. Em seguida, o
código acrescenta a cadeia de caracteres "Esta é uma sentença adicional." ao valor do
StringBuilder objeto 11 vezes. Sempre que a operação de acréscimo faz com que o
StringBuilder comprimento do objeto exceda sua capacidade, sua capacidade existente
é dobrada e a Append operação é bem-sucedida.

C#

using System;
using System.Reflection;
using System.Text;
public class Example4
{
public static void Main()
{
StringBuilder sb = new StringBuilder();
ShowSBInfo(sb);
sb.Append("This is a sentence.");
ShowSBInfo(sb);
for (int ctr = 0; ctr <= 10; ctr++)
{
sb.Append("This is an additional sentence.");
ShowSBInfo(sb);
}
}

private static void ShowSBInfo(StringBuilder sb)


{
foreach (var prop in sb.GetType().GetProperties())
{
if (prop.GetIndexParameters().Length == 0)
Console.Write("{0}: {1:N0} ", prop.Name,
prop.GetValue(sb));
}
Console.WriteLine();
}
}
// The example displays the following output:
// Capacity: 16 MaxCapacity: 2,147,483,647 Length: 0
// Capacity: 32 MaxCapacity: 2,147,483,647 Length: 19
// Capacity: 64 MaxCapacity: 2,147,483,647 Length: 50
// Capacity: 128 MaxCapacity: 2,147,483,647 Length: 81
// Capacity: 128 MaxCapacity: 2,147,483,647 Length: 112
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 143
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 174
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 205
// Capacity: 256 MaxCapacity: 2,147,483,647 Length: 236
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 267
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 298
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 329
// Capacity: 512 MaxCapacity: 2,147,483,647 Length: 360

Alocação de memória
A capacidade padrão de um StringBuilder objeto é de 16 caracteres e sua capacidade
máxima padrão é Int32.MaxValue. Esses valores padrão são usados se você chamar os
StringBuilder() construtores e StringBuilder(String) .

Você pode definir explicitamente a capacidade inicial de um StringBuilder objeto das


seguintes maneiras:
Chamando qualquer um dos StringBuilder construtores que inclui um capacity
parâmetro quando você cria o objeto.

Atribuindo explicitamente um novo valor à StringBuilder.Capacity propriedade


para expandir um objeto existente StringBuilder . Observe que a propriedade
lançará uma exceção se a nova capacidade for menor que a capacidade existente
ou maior que a StringBuilder capacidade máxima do objeto.

Chamando o StringBuilder.EnsureCapacity método com a nova capacidade. A nova


capacidade não deve ser maior do que a StringBuilder capacidade máxima do
objeto. No entanto, ao contrário de uma atribuição para a propriedade, não lança
uma exceção se a nova capacidade desejada for menor do que a capacidade
existente, neste caso, EnsureCapacity a Capacity chamada de método não tem
efeito.

Se o comprimento da cadeia de caracteres atribuída ao StringBuilder objeto na


chamada do construtor exceder a capacidade padrão ou a capacidade especificada, a
Capacity propriedade será definida como o comprimento da cadeia de caracteres
especificada com o value parâmetro.

Você pode definir explicitamente a capacidade máxima de um StringBuilder objeto


chamando o StringBuilder(Int32, Int32) construtor. Não é possível alterar a capacidade
máxima atribuindo um novo valor à MaxCapacity propriedade, porque ela é somente
leitura.

Como mostra a seção anterior, sempre que a capacidade existente é inadequada,


memória adicional é alocada e a capacidade de um StringBuilder objeto dobra até o
valor definido pela MaxCapacity propriedade.

Em geral, a capacidade padrão e a capacidade máxima são adequadas para a maioria


dos aplicativos. Você pode considerar a definição desses valores nas seguintes
condições:

Se o tamanho eventual do StringBuilder objeto provavelmente crescerá muito,


normalmente excedendo vários megabytes. Nesse caso, pode haver algum
benefício de desempenho ao definir a propriedade inicial Capacity para um valor
significativamente alto para eliminar a necessidade de muitas realocações de
memória.

Se o código estiver sendo executado em um sistema com memória limitada. Nesse


caso, convém considerar a configuração da MaxCapacity propriedade como menor
do que Int32.MaxValue se o código estiver manipulando cadeias de caracteres
grandes que podem fazer com que ele seja executado em um ambiente com
restrição de memória.

Instanciar um objeto StringBuilder


Você instancia um objeto chamando um StringBuilder de seus seis construtores de
classe sobrecarregados, que estão listados na tabela a seguir. Três dos construtores
instanciam um StringBuilder objeto cujo valor é uma cadeia de caracteres vazia, mas
definem seus Capacity valores e MaxCapacity de forma diferente. Os três construtores
restantes definem um objeto que tem um StringBuilder valor e capacidade de cadeia de
caracteres específicos. Dois dos três construtores usam a capacidade máxima padrão de
Int32.MaxValue, enquanto o terceiro permite que você defina a capacidade máxima.

ノ Expandir a tabela

Construtor Valor da cadeia de Capacidade Capacidade


caracteres máxima

StringBuilder() String.Empty 16 Int32.MaxValue

StringBuilder(Int32) String.Empty Definido pelo Int32.MaxValue


capacity parâmetro

StringBuilder(Int32, String.Empty Definido pelo Definido pelo


Int32) capacity parâmetro maxCapacity
parâmetro

StringBuilder(String) Definido pelo value 16 ou value . Length, Int32.MaxValue


parâmetro o que for maior

StringBuilder(String, Definido pelo value Definido pelo Int32.MaxValue


Int32) parâmetro capacity parâmetro
ou value . Length, o
que for maior.

StringBuilder(String, Definido por value . Definido pelo Int32.MaxValue


Int32, Int32, Int32) Substring( startIndex , capacity parâmetro
length ) ou value . Length, o
que for maior.

O exemplo a seguir usa três dessas sobrecargas de construtor para instanciar


StringBuilder objetos.

C#
using System;
using System.Text;

public class Example8


{
public static void Main()
{
string value = "An ordinary string";
int index = value.IndexOf("An ") + 3;
int capacity = 0xFFFF;

// Instantiate a StringBuilder from a string.


StringBuilder sb1 = new StringBuilder(value);
ShowSBInfo(sb1);

// Instantiate a StringBuilder from string and define a capacity.


StringBuilder sb2 = new StringBuilder(value, capacity);
ShowSBInfo(sb2);

// Instantiate a StringBuilder from substring and define a capacity.


StringBuilder sb3 = new StringBuilder(value, index,
value.Length - index,
capacity);
ShowSBInfo(sb3);
}

public static void ShowSBInfo(StringBuilder sb)


{
Console.WriteLine("\nValue: {0}", sb.ToString());
foreach (var prop in sb.GetType().GetProperties())
{
if (prop.GetIndexParameters().Length == 0)
Console.Write("{0}: {1:N0} ", prop.Name,
prop.GetValue(sb));
}
Console.WriteLine();
}
}
// The example displays the following output:
// Value: An ordinary string
// Capacity: 18 MaxCapacity: 2,147,483,647 Length: 18
//
// Value: An ordinary string
// Capacity: 65,535 MaxCapacity: 2,147,483,647 Length: 18
//
// Value: ordinary string
// Capacity: 65,535 MaxCapacity: 2,147,483,647 Length: 15

Chamar métodos StringBuilder


A maioria dos métodos que modificam a cadeia de caracteres em uma instância retorna
uma StringBuilder referência a essa mesma instância. Isso permite que você chame
StringBuilder métodos de duas maneiras:

Você pode fazer chamadas de método individuais e ignorar o valor de retorno,


como faz o exemplo a seguir.

C#

using System;
using System.Text;

public class Example


{
public static void Main()
{
StringBuilder sb = new StringBuilder();
sb.Append("This is the beginning of a sentence, ");
sb.Replace("the beginning of ", "");
sb.Insert(sb.ToString().IndexOf("a ") + 2, "complete ");
sb.Replace(",", ".");
Console.WriteLine(sb.ToString());
}
}
// The example displays the following output:
// This is a complete sentence.

Você pode fazer uma série de chamadas de método em uma única instrução. Isso
pode ser conveniente se você quiser escrever uma única instrução que encadeia
operações sucessivas. O exemplo a seguir consolida três chamadas de método do
exemplo anterior em uma única linha de código.

C#

using System;
using System.Text;

public class Example2


{
public static void Main()
{
StringBuilder sb = new StringBuilder("This is the beginning of
a sentence, ");
sb.Replace("the beginning of ",
"").Insert(sb.ToString().IndexOf("a ") + 2,
"complete
").Replace(",", ".");
Console.WriteLine(sb.ToString());
}
}
// The example displays the following output:
// This is a complete sentence.

Executar operações do StringBuilder


Você pode usar os StringBuilder métodos da classe para iterar, adicionar, excluir ou
modificar caracteres em um StringBuilder objeto.

Iterar caracteres do StringBuilder


Você pode acessar os caracteres em um StringBuilder objeto usando a
StringBuilder.Chars[] propriedade. Em C#, é um indexador, no Visual Basic, Chars[] é a
propriedade padrão da StringBuilder classe. Isso permite que você defina ou recupere
caracteres individuais usando apenas seu índice, sem fazer referência explícita à Chars[]
propriedade. Os caracteres em um StringBuilder objeto começam no índice 0 (zero) e
continuam no índice Length - 1.

O exemplo a seguir ilustra a Chars[] propriedade. Ele acrescenta dez números aleatórios
a um StringBuilder objeto e, em seguida, itera cada caractere. Se a categoria Unicode do
caractere for , ela diminuirá o número em 1 (ou alterará o número para 9 se seu valor for
UnicodeCategory.DecimalDigitNumber0). O exemplo exibe o StringBuilder conteúdo do
objeto antes e depois que os valores de caracteres individuais foram alterados.

C#

using System;
using System.Globalization;
using System.Text;

public class Example3


{
public static void Main()
{
Random rnd = new Random();
StringBuilder sb = new StringBuilder();

// Generate 10 random numbers and store them in a StringBuilder.


for (int ctr = 0; ctr <= 9; ctr++)
sb.Append(rnd.Next().ToString("N5"));

Console.WriteLine("The original string:");


Console.WriteLine(sb.ToString());

// Decrease each number by one.


for (int ctr = 0; ctr < sb.Length; ctr++)
{
if (Char.GetUnicodeCategory(sb[ctr]) ==
UnicodeCategory.DecimalDigitNumber)
{
int number = (int)Char.GetNumericValue(sb[ctr]);
number--;
if (number < 0) number = 9;

sb[ctr] = number.ToString()[0];
}
}
Console.WriteLine("\nThe new string:");
Console.WriteLine(sb.ToString());
}
}
// The example displays the following output:
// The original string:
//
1,457,531,530.00000940,522,609.000001,668,113,564.000001,998,992,883.000001,
792,660,834.00
//
000101,203,251.000002,051,183,075.000002,066,000,067.000001,643,701,043.0000
01,702,382,508
// .00000
//
// The new string:
//
0,346,420,429.99999839,411,598.999990,557,002,453.999990,887,881,772.999990,
681,559,723.99
//
999090,192,140.999991,940,072,964.999991,955,999,956.999990,532,690,932.9999
90,691,271,497
// .99999

O uso da indexação baseada em caracteres com a propriedade Chars[] pode ser


extremamente lento nas seguintes condições:

A instância de StringBuilder é grande (por exemplo, ela consiste em várias dezenas


de milhares de caracteres).
O é "grosso StringBuilder ". Ou seja, chamadas repetidas para métodos como
StringBuilder.Append expandiram automaticamente a propriedade do
StringBuilder.Capacity objeto e alocaram novos blocos de memória para ele.

O desempenho é afetado gravemente porque cada acesso de caractere percorre toda a


lista vinculada de partes para localizar o buffer correto ao qual indexar.

7 Observação

Mesmo para um objeto "grosso" StringBuilder grande, usar a propriedade para


acesso baseado em índice a Chars[] um ou um pequeno número de caracteres tem
um impacto de desempenho insignificante, normalmente, é uma operação O(n). O
impacto significativo sobre o desempenho ocorre ao iterar os caracteres no objeto
StringBuilder, que é uma operação O(n^2).

Se encontrar problemas de desempenho ao usar a indexação baseada em caractere com


objetos StringBuilder, você poderá usar qualquer uma das seguintes alternativas:

Converter a instância StringBuilder para um String chamando o método ToString e,


em seguida, acessar os caracteres na cadeia de caracteres.

Copiar o conteúdo do objeto StringBuilder existente para um novo objeto


StringBuilder pré-dimensionado. O desempenho melhora porque o novo objeto
StringBuilder não é robusto. Por exemplo:

C#

// sbOriginal is the existing StringBuilder object


var sbNew = new StringBuilder(sbOriginal.ToString(),
sbOriginal.Length);

Definir a capacidade inicial do objeto StringBuilder como um valor


aproximadamente igual ao seu tamanho máximo esperado chamando o construtor
StringBuilder(Int32). Observe que isso aloca o bloco de memória inteiro mesmo
que o StringBuilder raramente atinja sua capacidade máxima.

Adicionar texto a um objeto StringBuilder


A StringBuilder classe inclui os seguintes métodos para expandir o conteúdo de um
StringBuilder objeto:

O Append método acrescenta uma cadeia de caracteres, uma subcadeia de


caracteres, uma parte de uma matriz de caracteres, um único caractere repetido
várias vezes ou a representação de cadeia de caracteres de um tipo de dados
primitivo a um StringBuilder objeto.

O AppendLine método acrescenta um terminador de linha ou uma cadeia de


caracteres junto com um terminador de linha a um StringBuilder objeto.

O AppendFormat método acrescenta uma cadeia de caracteres de formato


composto a um StringBuilder objeto. As representações de cadeia de caracteres de
objetos incluídos na cadeia de caracteres de resultado podem refletir as
convenções de formatação da cultura do sistema atual ou de uma cultura
especificada.
O Insert método insere uma cadeia de caracteres, uma subcadeia de caracteres,
várias repetições de uma cadeia de caracteres, uma matriz de caracteres, uma
parte de uma matriz de caracteres ou a representação de cadeia de caracteres de
um tipo de dados primitivo em uma posição especificada no StringBuilder objeto.
A posição é definida por um índice baseado em zero.

O exemplo a seguir usa os Appendmétodos , AppendLine, AppendFormate Insert para


expandir o texto de um StringBuilder objeto.

C#

using System;
using System.Text;

public class Example6


{
public static void Main()
{
// Create a StringBuilder object with no text.
StringBuilder sb = new StringBuilder();
// Append some text.
sb.Append('*', 10).Append(" Adding Text to a StringBuilder Object
").Append('*', 10);
sb.AppendLine("\n");
sb.AppendLine("Some code points and their corresponding
characters:");
// Append some formatted text.
for (int ctr = 50; ctr <= 60; ctr++)
{
sb.AppendFormat("{0,12:X4} {1,12}", ctr, Convert.ToChar(ctr));
sb.AppendLine();
}
// Find the end of the introduction to the column.
int pos = sb.ToString().IndexOf("characters:") + 11 +
Environment.NewLine.Length;
// Insert a column header.
sb.Insert(pos, String.Format("{2}{0,12:X4} {1,12}{2}", "Code Unit",
"Character", "\n"));

// Convert the StringBuilder to a string and display it.


Console.WriteLine(sb.ToString());
}
}
// The example displays the following output:
// ********** Adding Text to a StringBuilder Object **********
//
// Some code points and their corresponding characters:
//
// Code Unit Character
// 0032 2
// 0033 3
// 0034 4
// 0035 5
// 0036 6
// 0037 7
// 0038 8
// 0039 9
// 003A :
// 003B ;
// 003C <

Excluir texto de um objeto StringBuilder


A StringBuilder classe inclui métodos que podem reduzir o tamanho da instância atual
StringBuilder . O Clear método remove todos os caracteres e define a Length
propriedade como zero. O Remove método exclui um número especificado de
caracteres começando em uma posição de índice específica. Além disso, você pode
remover caracteres do final de um objeto definindo sua Length propriedade como um
StringBuilder valor menor que o comprimento da instância atual.

O exemplo a seguir remove parte do texto de um StringBuilder objeto, exibe seus


valores resultantes de capacidade, capacidade máxima e propriedade length e, em
seguida, chama o Clear método para remover todos os caracteres do StringBuilder
objeto.

C#

using System;
using System.Text;

public class Example5


{
public static void Main()
{
StringBuilder sb = new StringBuilder("A StringBuilder object");
ShowSBInfo(sb);
// Remove "object" from the text.
string textToRemove = "object";
int pos = sb.ToString().IndexOf(textToRemove);
if (pos >= 0)
{
sb.Remove(pos, textToRemove.Length);
ShowSBInfo(sb);
}
// Clear the StringBuilder contents.
sb.Clear();
ShowSBInfo(sb);
}

public static void ShowSBInfo(StringBuilder sb)


{
Console.WriteLine("\nValue: {0}", sb.ToString());
foreach (var prop in sb.GetType().GetProperties())
{
if (prop.GetIndexParameters().Length == 0)
Console.Write("{0}: {1:N0} ", prop.Name,
prop.GetValue(sb));
}
Console.WriteLine();
}
}
// The example displays the following output:
// Value: A StringBuilder object
// Capacity: 22 MaxCapacity: 2,147,483,647 Length: 22
//
// Value: A StringBuilder
// Capacity: 22 MaxCapacity: 2,147,483,647 Length: 16
//
// Value:
// Capacity: 22 MaxCapacity: 2,147,483,647 Length: 0

Modificar o texto em um objeto StringBuilder


O StringBuilder.Replace método substitui todas as ocorrências de um caractere ou uma
cadeia de caracteres em todo StringBuilder o objeto ou em um determinado intervalo
de caracteres. O exemplo a seguir usa o Replace método para substituir todos os pontos
de exclamação (!) por pontos de interrogação (?) no StringBuilder objeto.

C#

using System;
using System.Text;

public class Example13


{
public static void Main()
{
StringBuilder MyStringBuilder = new StringBuilder("Hello World!");
MyStringBuilder.Replace('!', '?');
Console.WriteLine(MyStringBuilder);
}
}
// The example displays the following output:
// Hello World?

Pesquisar o texto em um objeto StringBuilder


A StringBuilder classe não inclui métodos semelhantes aos String.Containsmétodos , e
fornecidos pela String classe, String.IndexOfque permitem pesquisar o objeto para um
caractere String.StartsWith específico ou uma subcadeia de caracteres. Determinar a
presença ou a posição inicial do caractere de uma subcadeia de caracteres requer que
você pesquise um valor usando um método de pesquisa de cadeia de caracteres ou um
String método de expressão regular. Há quatro maneiras de implementar essas
pesquisas, como mostra a tabela a seguir.

ノ Expandir a tabela

Técnica Vantagens Desvantagens

Pesquise valores de cadeia Útil para determinar se Não pode ser usado quando a
de caracteres antes de existe uma subcadeia de posição de índice de uma
adicioná-los ao StringBuilder caracteres. substring é importante.
objeto.

Chame ToString e pesquise o Fácil de usar se você Complicado chamar ToString


objeto retornado String . atribuir todo o texto a um repetidamente se você precisar
StringBuilder objeto e, em fazer modificações antes que todo
seguida, começar a o texto seja adicionado ao
modificá-lo. StringBuilder objeto.

Lembre-se de trabalhar a partir do


final do texto do StringBuilder
objeto se estiver fazendo
alterações.

Use a Chars[] propriedade Útil se você estiver Complicado se o número de


para pesquisar preocupado com caracteres caracteres a serem pesquisados for
sequencialmente um individuais ou uma grande ou se a lógica de pesquisa
intervalo de caracteres. pequena subcadeia de for complexa.
caracteres.
Resulta em desempenho muito
ruim para objetos que cresceram
muito por meio de chamadas de
método repetidas.

Converter o StringBuilder Útil se o número de Anula StringBuilder o benefício de


objeto em um String objeto e modificações for pequeno. desempenho da classe se o
executar modificações no número de modificações for
String objeto. grande.

Vamos examinar essas técnicas com mais detalhes.

Se o objetivo da pesquisa for determinar se uma subcadeia de caracteres


específica existe (ou seja, se você não estiver interessado na posição da subcadeia
de caracteres), poderá pesquisar cadeias de caracteres antes de armazená-las no
StringBuilder objeto. O exemplo a seguir fornece uma implementação possível. Ele
define uma classe cujo construtor é passada uma StringBuilderFinder referência a
um StringBuilder objeto e a substring para localizar na cadeia de caracteres. Nesse
caso, o exemplo tenta determinar se as temperaturas registradas estão em
Fahrenheit ou Celsius e adiciona o texto introdutório apropriado ao início do
StringBuilder objeto. Um gerador de números aleatórios é usado para selecionar
uma matriz que contém dados em graus Celsius ou graus Fahrenheit.

C#

using System;
using System.Text;

public class Example9


{
public static void Main()
{
Random rnd = new Random();
string[] tempF = { "47.6F", "51.3F", "49.5F", "62.3F" };
string[] tempC = { "21.2C", "16.1C", "23.5C", "22.9C" };
string[][] temps = { tempF, tempC };

StringBuilder sb = new StringBuilder();


var f = new StringBuilderFinder(sb, "F");
var baseDate = new DateTime(2013, 5, 1);
String[] temperatures = temps[rnd.Next(2)];
bool isFahrenheit = false;
foreach (var temperature in temperatures)
{
if (isFahrenheit)
sb.AppendFormat("{0:d}: {1}\n", baseDate, temperature);
else
isFahrenheit = f.SearchAndAppend(String.Format("{0:d}:
{1}\n",
baseDate,
temperature));
baseDate = baseDate.AddDays(1);
}
if (isFahrenheit)
{
sb.Insert(0, "Average Daily Temperature in Degrees
Fahrenheit");
sb.Insert(47, "\n\n");
}
else
{
sb.Insert(0, "Average Daily Temperature in Degrees
Celsius");
sb.Insert(44, "\n\n");
}
Console.WriteLine(sb.ToString());
}
}
public class StringBuilderFinder
{
private StringBuilder sb;
private String text;

public StringBuilderFinder(StringBuilder sb, String textToFind)


{
this.sb = sb;
this.text = textToFind;
}

public bool SearchAndAppend(String stringToSearch)


{
sb.Append(stringToSearch);
return stringToSearch.Contains(text);
}
}
// The example displays output similar to the following:
// Average Daily Temperature in Degrees Celsius
//
// 5/1/2013: 21.2C
// 5/2/2013: 16.1C
// 5/3/2013: 23.5C
// 5/4/2013: 22.9C

Chame o método para converter o StringBuilder.ToStringStringBuilder objeto em


um String objeto. Você pode pesquisar a cadeia de caracteres usando métodos
como String.LastIndexOf ou , ou String.StartsWithpode usar expressões regulares e
a Regex classe para pesquisar padrões. Como ambos e String objetos usam
codificação UTF-16 para armazenar caracteres, as posições de índice de caracteres,
subcadeias de caracteres e correspondências de expressão regular são as mesmas
em ambos StringBuilder os objetos. Isso permite que você use StringBuilder
métodos para fazer alterações na mesma posição em que o texto é encontrado no
String objeto.

7 Observação

Se você adotar essa abordagem, deverá trabalhar do final do StringBuilder


objeto até seu início para que não seja necessário converter repetidamente o
StringBuilder objeto em uma cadeia de caracteres.

O exemplo a seguir ilustra esta abordagem. Ele armazena quatro ocorrências de


cada letra do alfabeto inglês em um StringBuilder objeto. Em seguida, converte o
texto em um String objeto e usa uma expressão regular para identificar a posição
inicial de cada sequência de quatro caracteres. Finalmente, ele adiciona um
sublinhado antes de cada sequência de quatro caracteres, exceto para a primeira
sequência, e converte o primeiro caractere da sequência em maiúsculas.

C#

using System;
using System.Text;
using System.Text.RegularExpressions;

public class Example10


{
public static void Main()
{
// Create a StringBuilder object with 4 successive occurrences
// of each character in the English alphabet.
StringBuilder sb = new StringBuilder();
for (ushort ctr = (ushort)'a'; ctr <= (ushort)'z'; ctr++)
sb.Append(Convert.ToChar(ctr), 4);

// Create a parallel string object.


String sbString = sb.ToString();
// Determine where each new character sequence begins.
String pattern = @"(\w)\1+";
MatchCollection matches = Regex.Matches(sbString, pattern);

// Uppercase the first occurrence of the sequence, and separate


it
// from the previous sequence by an underscore character.
for (int ctr = matches.Count - 1; ctr >= 0; ctr--)
{
Match m = matches[ctr];
sb[m.Index] = Char.ToUpper(sb[m.Index]);
if (m.Index > 0) sb.Insert(m.Index, "_");
}
// Display the resulting string.
sbString = sb.ToString();
int line = 0;
do
{
int nChars = line * 80 + 79 <= sbString.Length ?
80 : sbString.Length - line * 80;
Console.WriteLine(sbString.Substring(line * 80, nChars));
line++;
} while (line * 80 < sbString.Length);
}
}
// The example displays the following output:
//
Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_O
ooo_Pppp_
// Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz
Use a StringBuilder.Chars[] propriedade para pesquisar sequencialmente um
intervalo de caracteres em um StringBuilder objeto. Essa abordagem pode não ser
prática se o número de caracteres a serem pesquisados for grande ou se a lógica
de pesquisa for particularmente complexa. Para obter as implicações de
desempenho do acesso baseado em índice caractere por caractere para objetos
muito grandes e em StringBuilder partes, consulte a documentação da
StringBuilder.Chars[] propriedade.

O exemplo a seguir é idêntico em funcionalidade ao exemplo anterior, mas difere


na implementação. Ele usa a Chars[] propriedade para detectar quando um valor
de caractere foi alterado, insere um sublinhado nessa posição e converte o
primeiro caractere na nova sequência em maiúsculas.

C#

using System;
using System.Text;

public class Example11


{
public static void Main()
{
// Create a StringBuilder object with 4 successive occurrences
// of each character in the English alphabet.
StringBuilder sb = new StringBuilder();
for (ushort ctr = (ushort)'a'; ctr <= (ushort)'z'; ctr++)
sb.Append(Convert.ToChar(ctr), 4);

// Iterate the text to determine when a new character sequence


occurs.
int position = 0;
Char current = '\u0000';
do
{
if (sb[position] != current)
{
current = sb[position];
sb[position] = Char.ToUpper(sb[position]);
if (position > 0)
sb.Insert(position, "_");
position += 2;
}
else
{
position++;
}
} while (position <= sb.Length - 1);
// Display the resulting string.
String sbString = sb.ToString();
int line = 0;
do
{
int nChars = line * 80 + 79 <= sbString.Length ?
80 : sbString.Length - line * 80;
Console.WriteLine(sbString.Substring(line * 80, nChars));
line++;
} while (line * 80 < sbString.Length);
}
}
// The example displays the following output:
//
Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_O
ooo_Pppp_
// Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz

Armazene todo o texto não modificado no StringBuilder objeto, chame o método


para converter o StringBuilder.ToStringStringBuilder objeto em um String objeto e
execute as modificações no String objeto. Você pode usar essa abordagem se tiver
apenas algumas modificações; caso contrário, o custo de trabalhar com cadeias de
caracteres imutáveis pode anular os benefícios de desempenho do uso de um
StringBuilder objeto.

O exemplo a seguir é idêntico em funcionalidade aos dois exemplos anteriores,


mas difere na implementação. Ele cria um objeto, converte-o em um
StringBuilderString objeto e, em seguida, usa uma expressão regular para executar
todas as modificações restantes na cadeia de caracteres. O Regex.Replace(String,
String, MatchEvaluator) método usa uma expressão lambda para executar a
substituição em cada correspondência.

C#

using System;
using System.Text;
using System.Text.RegularExpressions;

public class Example12


{
public static void Main()
{
// Create a StringBuilder object with 4 successive occurrences
// of each character in the English alphabet.
StringBuilder sb = new StringBuilder();
for (ushort ctr = (ushort)'a'; ctr <= (ushort)'z'; ctr++)
sb.Append(Convert.ToChar(ctr), 4);

// Convert it to a string.
String sbString = sb.ToString();

// Use a regex to uppercase the first occurrence of the


sequence,
// and separate it from the previous sequence by an underscore.
string pattern = @"(\w)(\1+)";
sbString = Regex.Replace(sbString, pattern,
m => (m.Index > 0 ? "_" : "") +
m.Groups[1].Value.ToUpper() +
m.Groups[2].Value);

// Display the resulting string.


int line = 0;
do
{
int nChars = line * 80 + 79 <= sbString.Length ?
80 : sbString.Length - line * 80;
Console.WriteLine(sbString.Substring(line * 80, nChars));
line++;
} while (line * 80 < sbString.Length);
}
}
// The example displays the following output:
//
Aaaa_Bbbb_Cccc_Dddd_Eeee_Ffff_Gggg_Hhhh_Iiii_Jjjj_Kkkk_Llll_Mmmm_Nnnn_O
ooo_Pppp_
// Qqqq_Rrrr_Ssss_Tttt_Uuuu_Vvvv_Wwww_Xxxx_Yyyy_Zzzz

Converter o objeto StringBuilder em uma


cadeia de caracteres
Você deve converter o objeto StringBuilder em um objeto Stringpara transmitir a cadeia
de caracteres representada pelo objeto StringBuilder para um método que tem um
parâmetro String ou exibi-lo na interface do usuário. Você executa essa conversão
chamando o StringBuilder.ToString método. Para obter uma ilustração, consulte o
exemplo anterior, que chama o ToString método para converter um objeto em uma
cadeia de caracteres para que ele possa ser passado para um StringBuilder método de
expressão regular.

6 Colaborar conosco no
GitHub
A fonte deste conteúdo pode
ser encontrada no GitHub, onde
você também pode criar e
revisar problemas e solicitações
de pull. Para obter mais
informações, confira o nosso
guia para colaboradores.
Comentários do .NET
O .NET é um projeto código aberto.
Selecione um link para fornecer
comentários:

 Abrir um problema de
documentação

 Fornecer comentários sobre o


produto
Expressões regulares do .NET
Artigo • 10/03/2023

Expressões regulares oferecem um método poderoso, flexível e eficiente de


processamento de texto. A extensa notação de correspondência de padrões de
expressões regulares permite que você analise rapidamente grandes quantidades de
texto para:

Localizar padrões de caractere específicos.


Validar o texto para garantir que ele corresponda a um padrão predefinido (como
um endereço de email).
Extrair, editar, substituir ou excluir substrings de texto.
Adicionar cadeias de caracteres extraídas para uma coleção para gerar um
relatório.

Para vários aplicativos que lidam com cadeias de caracteres ou que analisam grandes
blocos de texto, as expressões regulares são uma ferramenta indispensável.

Como funcionam as expressões regulares


A parte mais importante do processamento de texto com expressões regulares é o
mecanismo de expressão regular, que é representado pelo objeto
System.Text.RegularExpressions.Regex no .NET. No mínimo, o processamento de texto
usando expressões regulares exige que o mecanismo de expressões regulares seja
fornecido com os dois itens informativos a seguir:

O padrão de expressão regular a ser identificado no texto.

No .NET, padrões de expressão regular são definidos por uma sintaxe ou


linguagem especial, compatível com expressões regulares Perl 5 e inclui alguns
recursos adicionais, como correspondência da direita para a esquerda. Para obter
mais informações, consulte Linguagem de expressões regulares – referência rápida.

O texto a ser analisado para o padrão de expressão regular.

Os métodos da classe Regex permitem que você realize as seguintes operações:

Determinar se o padrão de expressão regular ocorre no texto de entrada


chamando o método Regex.IsMatch. Para obter um exemplo que usa o método
IsMatch para validar texto, confira Como verificar se cadeias de caracteres estão
em um formato de email válido.
Recuperar uma ou todas as ocorrências de texto que corresponde ao padrão de
expressão regular chamando o método Regex.Match ou Regex.Matches. O
primeiro método retorna um objeto System.Text.RegularExpressions.Match que
oferece informações sobre o texto correspondente. O último retorna um objeto
MatchCollection que contém um objeto System.Text.RegularExpressions.Match
para cada correspondência encontrada no texto analisado.

Substitua o texto que corresponde ao padrão da expressão regular chamando o


método Regex.Replace. Para ver exemplos que usam o método Replace para
alterar formatos de data e remover caracteres inválidos de uma cadeia de
caracteres, confira Como retirar caracteres inválidos de uma cadeia de caracteres e
Exemplo: alterando formatos de data.

Para obter uma visão geral do modelo de objeto de expressão regular, consulte O
modelo de objeto de expressão regular.

Para saber mais sobre a linguagem de expressão regular, confira Linguagem de


expressão regular – referência rápida ou faça download de um dos seguintes folhetos e
imprima-os:

Referência rápida no formato Word (.docx)


Referência rápida no formato PDF (.pdf)

Exemplos de expressões regulares


A classe String inclui métodos de pesquisa de cadeia de caracteres e substituição que
podem ser usados quando você quer localizar cadeias de caracteres literais em uma
cadeia de caracteres maior. Expressões regulares são mais úteis quando você quer
localizar uma dentre diversas subcadeias de caracteres em uma cadeia de caracteres
maior ou quando quer identificar padrões em uma cadeia de caracteres, como
mostrado nos exemplos a seguir.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

 Dica
O namespace System.Web.RegularExpressions contém vários objetos de expressão
regular que implementam padrões predefinidos de expressão regular para analisar
cadeias de caracteres de documentos HTML, XML e ASP.NET. Por exemplo, a classe
TagRegex identifica marcas de início em uma cadeia de caracteres e a classe
CommentRegex identifica comentários ASP.NET em uma cadeia de caracteres.

Exemplo 1: substituir substrings


Vamos presumir que uma lista de endereçamento contém nomes que, às vezes, incluem
um pronome de tratamento (Sr., Srs., Srta. ou Sra.) junto com o nome e o sobrenome.
Suponha que você não queira incluir os títulos ao gerar etiquetas de envelope usando a
lista. Nesse caso, você pode usar uma expressão regular para remover os títulos, como o
seguinte exemplo ilustra:

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "(Mr\\.? |Mrs\\.? |Miss |Ms\\.? )";
string[] names = { "Mr. Henry Hunt", "Ms. Sara Samuels",
"Abraham Adams", "Ms. Nicole Norris" };
foreach (string name in names)
Console.WriteLine(Regex.Replace(name, pattern, String.Empty));
}
}
// The example displays the following output:
// Henry Hunt
// Sara Samuels
// Abraham Adams
// Nicole Norris

O padrão de expressão regular (Mr\.? |Mrs\.? |Miss |Ms\.? ) corresponde a qualquer


ocorrência de “Sr”, “Sr.”, “Sra”, “Sra.”, “Srta” ou “Srta.”. A chamada para o método
Regex.Replace substitui a cadeia de caracteres correspondida por String.Empty; em
outras palavras, ela a remove da cadeia de caracteres original.

Exemplo 2: identificar palavras duplicadas


Duplicar palavras acidentalmente é um erro comum que escritores cometem. Use uma
expressão regular para identificar palavras duplicadas, como mostrado no seguinte
exemplo:

C#

using System;
using System.Text.RegularExpressions;

public class Class1


{
public static void Main()
{
string pattern = @"\b(\w+?)\s\1\b";
string input = "This this is a nice day. What about this? This tastes
good. I saw a a dog.";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("{0} (duplicates '{1}') at position {2}",
match.Value, match.Groups[1].Value, match.Index);
}
}
// The example displays the following output:
// This this (duplicates 'This') at position 0
// a a (duplicates 'a') at position 66

O padrão de expressão regular \b(\w+?)\s\1\b pode ser interpretado da seguinte


maneira:

Padrão Interpretação

\b Iniciar em um limite de palavra.

(\w+?) Corresponder a um ou mais caracteres de palavra, mas o menor número de caracteres


possível. Juntos, formam um grupo que pode ser chamado de \1 .

\s Corresponde a um caractere de espaço em branco.

\1 Corresponder à substring que é igual ao grupo denominado \1 .

\b Corresponder a um limite de palavra.

O método Regex.Matches é chamado com opções de expressão regular definidas como


RegexOptions.IgnoreCase. Portanto, a operação de correspondência não faz distinção
entre maiúsculas e minúsculas e o exemplo identifica a subcadeia de caracteres “This
this” como uma duplicação.

A cadeia de caracteres de entrada inclui a substring "this? This”. No entanto, devido à


pontuação no meio, ela não é identificada como uma duplicação.
Exemplo 3: criar dinamicamente uma expressão regular
com reconhecimento de cultura
O exemplo a seguir mostra a força das expressões regulares combinada à flexibilidade
oferecida pelos recursos de globalização do .NET. Ele utiliza o objeto NumberFormatInfo
para determinar o formato de valores de moeda na cultura atual do sistema. Então, essa
informação é usada para construir dinamicamente uma expressão regular que extrai
valores de moeda do texto. Para cada correspondência, ele extrai o subgrupo que
contém somente a subcadeia de caracteres, converte-a para um valor Decimal e calcula
uma soma acumulada.

C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
// Define text to be parsed.
string input = "Office expenses on 2/13/2008:\n" +
"Paper (500 sheets) $3.95\n" +
"Pencils (box of 10) $1.00\n" +
"Pens (box of 10) $4.49\n" +
"Erasers $2.19\n" +
"Ink jet printer $69.95\n\n" +
"Total Expenses $ 81.58\n";

// Get current culture's NumberFormatInfo object.


NumberFormatInfo nfi = CultureInfo.CurrentCulture.NumberFormat;
// Assign needed property values to variables.
string currencySymbol = nfi.CurrencySymbol;
bool symbolPrecedesIfPositive = nfi.CurrencyPositivePattern % 2 == 0;
string groupSeparator = nfi.CurrencyGroupSeparator;
string decimalSeparator = nfi.CurrencyDecimalSeparator;

// Form regular expression pattern.


string pattern = Regex.Escape( symbolPrecedesIfPositive ?
currencySymbol : "") +
@"\s*[-+]?" + "([0-9]{0,3}(" + groupSeparator + "[0-
9]{3})*(" +
Regex.Escape(decimalSeparator) + "[0-9]+)?)" +
(! symbolPrecedesIfPositive ? currencySymbol : "");
Console.WriteLine( "The regular expression pattern is:");
Console.WriteLine(" " + pattern);

// Get text that matches regular expression pattern.


MatchCollection matches = Regex.Matches(input, pattern,
RegexOptions.IgnorePatternWhitespace);
Console.WriteLine("Found {0} matches.", matches.Count);

// Get numeric string, convert it to a value, and add it to List


object.
List<decimal> expenses = new List<Decimal>();

foreach (Match match in matches)


expenses.Add(Decimal.Parse(match.Groups[1].Value));

// Determine whether total is present and if present, whether it is


correct.
decimal total = 0;
foreach (decimal value in expenses)
total += value;

if (total / 2 == expenses[expenses.Count - 1])


Console.WriteLine("The expenses total {0:C2}.",
expenses[expenses.Count - 1]);
else
Console.WriteLine("The expenses total {0:C2}.", total);
}
}
// The example displays the following output:
// The regular expression pattern is:
// \$\s*[-+]?([0-9]{0,3}(,[0-9]{3})*(\.[0-9]+)?)
// Found 6 matches.
// The expenses total $81.58.

Em um computador cuja cultura atual seja Inglês – Estados Unidos (en-US), o exemplo
compila dinamicamente a expressão regular \$\s*[-+]?([0-9]{0,3}(,[0-9]{3})*(\.[0-
9]+)?) . Esse padrão de expressão regular pode ser interpretado da seguinte maneira:

Padrão Interpretação

\$ Procure uma única ocorrência do símbolo de cifrão ( $ ) na cadeia de caracteres de


entrada. A cadeia de caracteres do padrão de expressão regular inclui uma barra
invertida para indicar que o símbolo de cifrão deve ser interpretado literalmente ao
invés de como uma âncora de expressão regular. O símbolo $ sozinho indicaria que o
mecanismo de expressão regular deveria tentar iniciar a correspondência no final de
uma cadeia de caracteres. Para garantir que o símbolo de moeda da cultura atual não
seja interpretado incorretamente como um símbolo de expressão regular, o exemplo
chama o método Regex.Escape para escapar o caractere.

\s* Procure zero ou mais ocorrências de um caractere de espaço em branco.

[-+]? Procure zero ou uma ocorrência de um sinal de positivo ou negativo.


Padrão Interpretação

([0-9] Os parênteses externos definem a expressão como um grupo de captura ou uma


{0,3}(, subexpressão. Se uma correspondência for localizada, informações sobre essa parte da
[0-9] cadeia de caracteres correspondente podem ser recuperadas do segundo objeto Group
{3})* no objeto GroupCollection retornado pela propriedade Match.Groups. O primeiro
(\.[0- elemento na coleção representa a correspondência inteira.
9]+)?)

[0-9] Procure de zero a três ocorrências dos dígitos decimais de 0 a 9.


{0,3}

(,[0- Procure zero ou mais ocorrências de um separador de grupo seguido por três dígitos
9] decimais.
{3})*

\. Procure uma única ocorrência do separador decimal.

[0-9]+ Procure por um ou mais dígitos decimais.

(\.[0- Procure zero ou uma ocorrência de um separador decimal seguido por pelo menos um
9]+)? dígito decimal.

Se cada subpadrão for encontrado na cadeia de caracteres de entrada, a


correspondência será bem-sucedida, e um objeto Match que contém informações sobre
a correspondência será adicionado ao objeto MatchCollection.

Artigos relacionados
Título Descrição

Linguagem de expressões Oferece informações sobre o conjunto de caracteres, operadores e


regulares – referência constructos que você pode usar para definir expressões regulares.
rápida

O modelo de objeto de Oferece informações e exemplos de código que ilustram como usar
expressão regular as classes de expressão regular.

Detalhes do Oferece informações sobre os recursos e o comportamento das


comportamento de expressões regulares do .NET.
expressões regulares

Usar expressões regulares


no Visual Studio

Referência
System.Text.RegularExpressions
System.Text.RegularExpressions.Regex
Expressões regulares - referência rápida (fazer download no formato Word)
Expressões regulares - referência rápida (fazer download no formato PDF)
Geradores de origem de expressão
regular do .NET
Artigo • 02/06/2023

Uma expressão regular, ou regex, é uma cadeia de caracteres que permite que um
desenvolvedor expresse um padrão que está sendo pesquisado, tornando-o uma
maneira muito comum de pesquisar texto e extrair resultados como um subconjunto da
cadeia de caracteres pesquisada. No .NET, o namespace
System.Text.RegularExpressions é usado para definir instâncias e métodos estáticos

Regex e corresponder a padrões definidos pelo usuário. Neste artigo, você aprenderá a
usar a geração de origem para gerar instâncias Regex para otimizar o desempenho.

7 Observação

Sempre que possível, use expressões regulares geradas pela origem em vez de
compilar expressões regulares usando a opção RegexOptions.Compiled. A geração
de origem pode ajudar seu aplicativo a iniciar com mais rapidez, ser executado com
mais agilidade e ser mais completo. Para saber quando a geração de origem é
possível, confira Quando usá-la.

Expressões regulares compiladas


Quando você escreve new Regex("somepattern") , algumas coisas acontecem. O padrão
especificado é analisado, tanto para garantir a validade do padrão quanto para
transformá-lo em uma árvore interna que representa o regex analisado. Em seguida, a
árvore é otimizada de várias maneiras, transformando o padrão em uma variação
funcionalmente equivalente que pode ser executada com mais eficiência. A árvore é
gravada em um formulário que pode ser interpretado como uma série de opcodes e
operandos que fornecem instruções ao mecanismo do interpretador regex sobre como
fazer a correspondência. Quando uma correspondência é executada, o interpretador
simplesmente percorre essas instruções, processando-as em relação ao texto de
entrada. Ao instanciar uma nova instância Regex ou chamar um dos métodos estáticos
em Regex , o interpretador é o mecanismo padrão empregado.

Quando você especificar RegexOptions.Compiled, todas as mesmas obras em tempo de


construção serão executadas. As instruções resultantes seriam transformadas ainda mais
pelo compilador baseado em emissão de reflexão em instruções IL que seriam gravadas
em alguns DynamicMethods. Quando uma correspondência era executada, esses
DynamicMethod s seriam invocados. De forma essencial, essa IL faria exatamente o que o

interpretador faria, exceto pela especialização para o padrão exato que está sendo
processado. Por exemplo, se o padrão contivesse [ac] , o interpretador veria um opcode
que diria "combinar o caractere de entrada na posição atual com o conjunto
especificado nesta descrição do conjunto", enquanto o IL compilado conteria o código
que efetivamente diria "combinar com o caractere de entrada na posição atual contra
'a' ou 'c' ". Esse caso especial e a capacidade de realizar otimizações com base no

conhecimento do padrão são algumas das principais razões para especificar


RegexOptions.Compiled , resultando em uma taxa de transferência de correspondência

muito mais rápida do que o interpretador.

Há várias desvantagens em RegexOptions.Compiled . A mais impactante é que ela incorre


em muito mais custo de construção do que usar o interpretador. Não apenas todos os
mesmos custos são pagos pelo interpretador, mas ele também precisa compilar a árvore
RegexNode resultante e os opcodes/operandos gerados no IL, o que adiciona despesas

não triviais. A IL gerada ainda precisa ser compilada por JIT no primeiro uso, levando a
ainda mais despesas na inicialização. RegexOptions.Compiled representa uma
compensação fundamental entre sobrecargas no primeiro uso e sobrecargas em cada
uso subsequente. O uso de System.Reflection.Emit também inibe o uso de
RegexOptions.Compiled em determinados ambientes; alguns sistemas operacionais não

permitem que o código gerado dinamicamente seja executado e, nesses sistemas,


Compiled se tornará uma operação não operacional.

Geração de origem
O .NET 7 apresenta um novo gerador de origem RegexGenerator . Quando o compilador
C# foi reescrito como o compilador C# "Roslyn", ele expôs modelos de objeto para todo
o pipeline de compilação, bem como analisadores. Mais recentemente, Roslyn habilitou
geradores de origem. Assim como um analisador, um gerador de origem é um
componente que se conecta ao compilador e recebe todas as mesmas informações que
um analisador, mas além de ser capaz de emitir diagnósticos, ele também pode
aumentar a unidade de compilação com código-fonte adicional. O SDK do .NET 7 inclui
um novo gerador de origem que reconhece o novo GeneratedRegexAttribute em um
método parcial que retorna Regex . O gerador de origem fornece uma implementação
desse método que implementa toda a lógica para o Regex . Por exemplo, você pode ter
escrito um código como este:

C#

private static readonly Regex s_abcOrDefGeneratedRegex =


new(pattern: "abc|def",
options: RegexOptions.Compiled | RegexOptions.IgnoreCase);

private static void EvaluateText(string text)


{
if (s_abcOrDefGeneratedRegex.IsMatch(text))
{
// Take action with matching text
}
}

Agora você pode reescrever o código anterior da seguinte maneira:

C#

[GeneratedRegex("abc|def", RegexOptions.IgnoreCase, "en-US")]


private static partial Regex AbcOrDefGeneratedRegex();

private static void EvaluateText(string text)


{
if (AbcOrDefGeneratedRegex().IsMatch(text))
{
// Take action with matching text
}
}

A implementação gerada de AbcOrDefGeneratedRegex() armazena em cache de forma


semelhante uma instância singleton Regex , portanto, nenhum cache adicional é
necessário para consumir código.

 Dica

O sinalizador RegexOptions.Compiled é ignorado pelo gerador de origem, fazendo


com que não seja mais necessário na versão gerada pela origem.

Mas como pode ser visto, não é apenas fazer new Regex(...) . Em vez disso, o gerador
de origem está emitindo como código C# uma implementação derivada de
personalizado Regex com lógica semelhante à que RegexOptions.Compiled emite na IL.
Você obtém todos os benefícios de desempenho de taxa de transferência de
RegexOptions.Compiled (mais, na verdade) e os benefícios de inicialização de
Regex.CompileToAssembly , mas sem a complexidade de CompileToAssembly . A origem
emitida faz parte do seu projeto, o que significa que ele também é facilmente acessível
e depurável.

 Dica

No Visual Studio, clique com o botão direito do mouse em sua declaração de


método parcial e selecione Ir para Definição. Ou, como alternativa, selecione o nó
do projeto no Gerenciador de Soluções e expanda
Dependências>Analisadores>System.Text.RegularExpressions.Generator>System.
Text.RegularExpressions.Generator.RegexGenerator>RegexGenerator.g.cs para ver
o código C# gerado desse gerador regex.

Você pode definir pontos de interrupção nele, percorrê-lo e usá-lo como uma
ferramenta de aprendizado para entender exatamente como o mecanismo regex está
processando seu padrão com sua entrada. O gerador gera até mesmo comentários de
barra tripla (XML) para ajudar a tornar a expressão compreensível rapidamente e onde
ela é usada.

Dentro dos arquivos gerados pela origem


Com o .NET 7, o gerador de origem e RegexCompiler foram quase inteiramente
reescritos, alterando fundamentalmente a estrutura do código gerado. Essa abordagem
foi estendida para lidar com todos os constructos (com uma ressalva), e o
RegexCompiler e o gerador de origem ainda mapeiam principalmente 1:1 entre si,

seguindo a nova abordagem. Considere a saída do gerador de origem para uma das
funções primárias da expressão (a|bc)d :

C#

private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)


{
int pos = base.runtextpos;
int matchStart = pos;
int capture_starting_pos = 0;
ReadOnlySpan<char> slice = inputSpan.Slice(pos);

// 1st capture group.


//{
capture_starting_pos = pos;

// Match with 2 alternative expressions.


//{
if (slice.IsEmpty)
{
UncaptureUntil(0);
return false; // The input didn't match.
}

switch (slice[0])
{
case 'a':
pos++;
slice = inputSpan.Slice(pos);
break;

case 'b':
// Match 'c'.
if ((uint)slice.Length < 2 || slice[1] != 'c')
{
UncaptureUntil(0);
return false; // The input didn't match.
}

pos += 2;
slice = inputSpan.Slice(pos);
break;

default:
UncaptureUntil(0);
return false; // The input didn't match.
}
//}

base.Capture(1, capture_starting_pos, pos);


//}

// Match 'd'.
if (slice.IsEmpty || slice[0] != 'd')
{
UncaptureUntil(0);
return false; // The input didn't match.
}

// The input matched.


pos++;
base.runtextpos = pos;
base.Capture(0, matchStart, pos);
return true;

// <summary>Undo captures until it reaches the specified capture


position.</summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void UncaptureUntil(int capturePosition)
{
while (base.Crawlpos() > capturePosition)
{
base.Uncapture();
}
}
}

O objetivo do código gerado pela origem é ser compreensível, com uma estrutura fácil
de seguir, com comentários explicando o que está sendo feito em cada etapa e, em
geral, com o código emitido sob o princípio norteador de que o gerador deve emitir
código como se um humano o tivesse escrito. Mesmo quando o rastreamento inverso
está envolvido, a estrutura do rastreamento inverso torna-se parte da estrutura do
código, em vez de depender de uma pilha para indicar onde saltar em seguida. Por
exemplo, aqui está o código para a mesma função de correspondência gerada quando a
expressão é [ab]*[bc] :

C#

private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)


{
int pos = base.runtextpos;
int matchStart = pos;
int charloop_starting_pos = 0, charloop_ending_pos = 0;
ReadOnlySpan<char> slice = inputSpan.Slice(pos);

// Match a character in the set [ab] greedily any number of times.


//{
charloop_starting_pos = pos;

int iteration = slice.IndexOfAnyExcept('a', 'b');


if (iteration < 0)
{
iteration = slice.Length;
}

slice = slice.Slice(iteration);
pos += iteration;

charloop_ending_pos = pos;
goto CharLoopEnd;

CharLoopBacktrack:

if (Utilities.s_hasTimeout)
{
base.CheckTimeout();
}

if (charloop_starting_pos >= charloop_ending_pos ||


(charloop_ending_pos = inputSpan.Slice(
charloop_starting_pos, charloop_ending_pos -
charloop_starting_pos)
.LastIndexOfAny('b', 'c')) < 0)
{
return false; // The input didn't match.
}
charloop_ending_pos += charloop_starting_pos;
pos = charloop_ending_pos;
slice = inputSpan.Slice(pos);

CharLoopEnd:
//}

// Advance the next matching position.


if (base.runtextpos < pos)
{
base.runtextpos = pos;
}

// Match a character in the set [bc].


if (slice.IsEmpty || !char.IsBetween(slice[0], 'b', 'c'))
{
goto CharLoopBacktrack;
}

// The input matched.


pos++;
base.runtextpos = pos;
base.Capture(0, matchStart, pos);
return true;
}

Você pode ver a estrutura do rastreamento inverso no código, com um rótulo


CharLoopBacktrack emitido para onde retroceder e um goto usado para ir para esse

local quando uma parte subsequente do regex falhar.

Se você examinar o código que implementa RegexCompiler e o gerador de origem, eles


serão extremamente semelhantes: métodos nomeados da mesma forma, estrutura de
chamada semelhante e até mesmo comentários semelhantes em toda a implementação.
Na maioria das vezes, eles resultam em código idêntico, embora um em IL e outro em
C#. É claro que o compilador C# é então responsável por traduzir o C# para IL, portanto,
a IL resultante em ambos os casos provavelmente não será idêntica. O gerador de
origem depende disso em vários casos, aproveitando o fato de que o compilador C#
otimizará ainda mais vários constructos C#. Há algumas coisas específicas que o gerador
de origem produzirá mais código de correspondência otimizado do que o
RegexCompiler . Por exemplo, em um dos exemplos anteriores, você pode ver o gerador
de origem emitindo uma instrução switch, com um branch para 'a' e outro branch
para 'b' . Como o compilador C# é muito bom na otimização de instruções switch, com
várias estratégias à sua disposição para fazer isso com eficiência, o gerador de origem
tem uma otimização especial que RegexCompiler não tem. Para alternâncias, o gerador
de origem examina todos os branches e, se puder provar que cada branch começa com
um caractere inicial diferente, emitirá uma instrução switch sobre o primeiro caractere e
evitará gerar qualquer código de rastreamento inverso para essa alternância.

Aqui está um exemplo um pouco mais complicado disso. No .NET 7, as alternâncias são
mais analisadas para determinar se é possível refatorá-las de uma maneira que as torne
mais facilmente otimizadas pelos mecanismos de rastreamento inverso e isso levará a
um código gerado pela fonte mais simples. Uma dessas otimizações dá suporte à
extração de prefixos comuns de branches e, se a alternância for atômica de modo que a
ordenação não importe, reordene branches para permitir mais extração desse tipo. Você
pode ver o impacto disso para o seguinte padrão de dia da semana
Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday , que produz uma função

correspondente como esta:

C#

private bool TryMatchAtCurrentPosition(ReadOnlySpan<char> inputSpan)


{
int pos = base.runtextpos;
int matchStart = pos;
ReadOnlySpan<char> slice = inputSpan.Slice(pos);

// Match with 5 alternative expressions, atomically.


{
if (slice.IsEmpty)
{
return false; // The input didn't match.
}

switch (slice[0])
{
case 'M':
// Match the string "onday".
if (!slice.Slice(1).StartsWith("onday"))
{
return false; // The input didn't match.
}

pos += 6;
slice = inputSpan.Slice(pos);
break;

case 'T':
// Match with 2 alternative expressions, atomically.
{
if ((uint)slice.Length < 2)
{
return false; // The input didn't match.
}

switch (slice[1])
{
case 'u':
// Match the string "esday".
if (!slice.Slice(2).StartsWith("esday"))
{
return false; // The input didn't match.
}

pos += 7;
slice = inputSpan.Slice(pos);
break;
case 'h':
// Match the string "ursday".
if (!slice.Slice(2).StartsWith("ursday"))
{
return false; // The input didn't match.
}

pos += 8;
slice = inputSpan.Slice(pos);
break;

default:
return false; // The input didn't match.
}
}

break;

case 'W':
// Match the string "ednesday".
if (!slice.Slice(1).StartsWith("ednesday"))
{
return false; // The input didn't match.
}

pos += 9;
slice = inputSpan.Slice(pos);
break;

case 'F':
// Match the string "riday".
if (!slice.Slice(1).StartsWith("riday"))
{
return false; // The input didn't match.
}

pos += 6;
slice = inputSpan.Slice(pos);
break;

case 'S':
// Match with 2 alternative expressions, atomically.
{
if ((uint)slice.Length < 2)
{
return false; // The input didn't match.
}

switch (slice[1])
{
case 'a':
// Match the string "turday".
if (!slice.Slice(2).StartsWith("turday"))
{
return false; // The input didn't match.
}

pos += 8;
slice = inputSpan.Slice(pos);
break;

case 'u':
// Match the string "nday".
if (!slice.Slice(2).StartsWith("nday"))
{
return false; // The input didn't match.
}

pos += 6;
slice = inputSpan.Slice(pos);
break;

default:
return false; // The input didn't match.
}
}

break;

default:
return false; // The input didn't match.
}
}

// The input matched.


base.runtextpos = pos;
base.Capture(0, matchStart, pos);
return true;
}

Observe como Thursday foi reordenado para ser logo após Tuesday , e como para o par
Tuesday / Thursday e para o par Saturday / Sunday , você acaba com vários níveis de

comutadores. No extremo, se você criasse uma longa alternância de muitas palavras


diferentes, o gerador de origem acabaria emitindo o equivalente lógico de um trie^1 ,
lendo cada caractere e switch indo para o branch para lidar com o restante da palavra.
Essa é uma maneira muito eficiente de corresponder palavras e é o que o gerador de
origem está fazendo aqui.

Ao mesmo tempo, o gerador de origem tem outros problemas para enfrentar que
simplesmente não existem ao gerar diretamente para IL. Se você observar alguns
exemplos de código de mais uma vez, poderá ver algumas chaves um pouco
estranhamente comentadas. Isso não é um erro. O gerador de origem reconhece que, se
essas chaves não tiverem sido comentadas, a estrutura do rastreamento inverso
dependerá do salto de fora do escopo para um rótulo definido dentro desse escopo;
esse rótulo não seria visível para tal goto e o código não seria compilado. Portanto, o
gerador de origem precisa evitar que haja um escopo no caminho. Em alguns casos, ele
simplesmente comentará o escopo como foi feito aqui. Em outros casos em que isso
não é possível, às vezes pode evitar constructos que requerem escopos (como um bloco
if de várias instruções) se isso for problemático.

O gerador de origem lida com todos os identificadores RegexCompiler , com uma


exceção. Assim como na manipulação de RegexOptions.IgnoreCase , as implementações
agora usam uma tabela de revestimento para gerar conjuntos no momento da
construção e como a correspondência de referência inversa IgnoreCase precisa
consultar essa tabela de revestimento. Essa tabela é interna para
System.Text.RegularExpressions.dll e, por enquanto, pelo menos, o código externo a

esse assembly (incluindo o código emitido pelo gerador de origem) não tem acesso a
ela. Isso torna a manipulação de referências inversas IgnoreCase um desafio no gerador
de origem e elas não têm suporte. Este é o único constructo sem suporte pelo gerador
de origem com suporte pelo RegexCompiler . Se você tentar usar um padrão que tenha
um desses (o que é raro), o gerador de origem não emitirá uma implementação
personalizada e, em vez disso, retornará ao cache de uma instância regular Regex :

Além disso, nem RegexCompiler nem o gerador de origem suportam o novo


RegexOptions.NonBacktracking . Se você especificar RegexOptions.Compiled |

RegexOptions.NonBacktracking , o sinalizador Compiled será ignorado e, se você

especificar NonBacktracking para o gerador de origem, ele retornará de maneira


semelhante ao cache de uma instância Regex regular.

Quando usar isso


A orientação geral é se você pode usar o gerador de fonte, use-o. Se você estiver
usando Regex hoje em C# com argumentos conhecidos em tempo de compilação e,
especialmente, se você já estiver usando RegexOptions.Compiled (porque o regex foi
identificado como um ponto de acesso que se beneficiaria de uma taxa de transferência
mais rápida), você deve preferir usar o gerador de origem. O gerador de origem dará ao
seu regex os seguintes benefícios:
Todos os benefícios de taxa de transferência de RegexOptions.Compiled .
Os benefícios de inicialização de não precisar fazer toda a análise de regex, análise
e compilação em tempo de execução.
A opção de usar a compilação antecipada com o código gerado para o regex.
Melhor depuração e compreensão do regex.
A possibilidade de reduzir o tamanho de seu aplicativo aparado, cortando grandes
faixas de código associadas com RegexCompiler (e potencialmente até mesmo a
reflexão em si).

Quando usada com uma opção como RegexOptions.NonBacktracking para a qual o


gerador de origem não pode gerar uma implementação personalizada, ela ainda emitirá
cache e comentários XML que descrevem a implementação, tornando-a valiosa. A
principal desvantagem do gerador de origem é que ele emite código adicional em seu
assembly, portanto, há o potencial de aumento de tamanho. Quanto mais regexes em
seu aplicativo e maiores forem, mais código será emitido para eles. Em algumas
situações, assim como RegexOptions.Compiled pode ser desnecessário, também pode
ser o gerador de origem. Por exemplo, se você tiver um regex que é necessário apenas
raramente e para o qual a taxa de transferência não importa, pode ser mais benéfico
apenas contar com o interpretador para esse uso esporádico.

) Importante

O .NET 7 inclui um analisador que identifica o uso do Regex que pode ser
convertido no gerador de origem e um reparador que faz a conversão para você:

Confira também
Diagnóstico SYSLIB para geração de origem regex
Expressões regulares do .NET
Retrocesso em expressões regulares
Compilação e reutilização em expressões regulares
Geradores de origem
Blog do .NET: aprimoramentos de expressões regulares no .NET 7

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Linguagem de expressões regulares –
referência rápida
Artigo • 10/05/2023

Uma expressão regular é um padrão ao qual o mecanismo de expressões regulares tenta


corresponder no texto de entrada. Um padrão consiste em um ou mais literais de caracteres,
operadores ou constructos. Para ver uma breve introdução, confira Expressões regulares no
.NET.

Cada seção desta referência rápida lista uma categoria específica de caracteres, operadores e
constructos que podem ser usados para definir expressões regulares.

Também fornecemos essas informações em dois formatos, que podem ser baixados e
impressos para uma referência simplificada:

Baixar no formato Word (.docx)


Baixar no formato PDF (.pdf)

Escapes de caracteres
O caractere de barra invertida (\) em uma expressão regular indica que o próximo caractere é
um caractere especial (conforme mostrado na tabela a seguir) ou se ele deve ser interpretado
literalmente. Para obter mais informações, consulte Escapes de Caracteres.

Caractere Descrição Padrão Correspondências


com
escape

\a Corresponde a um caractere de sino, \u0007. \a "\u0007" em


"Error!" +
'\u0007'

\b Em uma classe de caractere, corresponde a um [\b]{3,} "\b\b\b\b" em


backspace, \ u0008. "\b\b\b\b"

\t Corresponde a uma tabulação, \u0009. (\w+)\t "item1\t" e


"item2\t" em
"item1\titem2\t"

\r Corresponde a um retorno de carro, \u000D. ( \r não é \r\n(\w+) "\r\nThese" em


equivalente ao caractere newline, \n .) "\r\nThese
are\ntwo lines."

\v Corresponde a uma tabulação vertical, \u000B. [\v]{2,} "\v\v\v" em


"\v\v\v"
Caractere Descrição Padrão Correspondências
com
escape

\f Corresponde a um avanço de página, \u000C. [\f]{2,} "\f\f\f" em


"\f\f\f"

\n Corresponde a uma nova linha, \u000A. \r\n(\w+) "\r\nThese" em


"\r\nThese
are\ntwo lines."

\e Corresponde a um escape, \u001B. \e "\x001B" em


"\x001B"

\ nnn Usa representação octal para especificar um caractere \w\040\w "a b" e "c d" em
(nnn consiste em dois ou três dígitos). "a bc d"

\x nn Usa representação hexadecimal para especificar um \w\x20\w "a b" e "c d" em
caractere (nn consiste exatamente em dois dígitos). "a bc d"

\c X Corresponde ao caractere de controle ASCII \cC "\x0003" em


especificado por X ou x, em que X ou x é a letra do "\x0003" (Ctrl-C)
\c x caractere de controle.

\u nnnn Corresponde a um caractere Unicode usando \w\u0020\w "a b" e "c d" em
representação hexadecimal (exatamente quatro dígitos, "a bc d"
como representado por nnnn).

\ Quando seguido por um caractere que não é \d+[\+- "2+2" e "3*9" em


reconhecido como um caractere de escape nesta e em x\*]\d+ "(2+2) * 3*9"
outras tabelas deste tópico, corresponde a esse
caractere. Por exemplo, \* é igual a \x2A , e \. é igual a
\x2E . Isso permite que o mecanismo de expressões
regulares remova ambiguidades de elementos da
linguagem (como * ou ?) e caracteres literais
(representados por \* ou \? ).

Classes de caracteres
Uma classe de caractere corresponde a qualquer um dos conjuntos de caracteres. As classes
de caracteres incluem os elementos de linguagem listados na tabela a seguir. Para saber mais,
confira Classes de caracteres.

Classe de caractere Descrição Padrão Correspondências

[ character_group ] Corresponde a qualquer caractere único [ae] "a" em "gray"


em character_group. Por padrão, a
correspondência diferencia maiúsculas de "a" e "e" em
minúsculas. "lane"
Classe de caractere Descrição Padrão Correspondências

[^ character_group ] Negação: corresponde a qualquer [^aei] "r" , "g" e "n"


caractere único que não esteja em em "reign"
character_group. Por padrão, caracteres
em character_group diferenciam
maiúsculas de minúsculas.

[ first - last ] Intervalo de caracteres: corresponde a [A-Z] "A" e "B" em


qualquer caractere único no intervalo "AB123"
entre first e last.

. Curinga: corresponde a qualquer a.e "ave" em "nave"


caractere único, exceto \n.
"ate" em "water"
Para corresponder a um caractere de
período literal (. ou \u002E ), você deve
precedê-lo com o caractere de escape
( \. ).

\p{ name } Corresponde a qualquer caractere único \p{Lu} "C" e "L" em


na categoria geral Unicode ou no bloco "City Lights"
nomeado especificado por name. \p{IsCyrillic}
"Д" e "Ж" em
"ДЖem"

\P{ name } Corresponde a qualquer caractere único \P{Lu} "i" , "t" e "y"
que não esteja na categoria geral em "City"
Unicode ou no bloco nomeado \P{IsCyrillic}
especificado por name. "e" e "m" em
"ДЖem"

\w Corresponde a qualquer caractere de \w "I" , "D" , "A" ,


palavra. "1" e "3" em "ID
A1.3"

\W Corresponde a qualquer caractere sem \W " " e "." em "ID


palavra. A1.3"

\s Corresponde a qualquer caractere de \w\s "D " em "ID


espaço em branco. A1.3"

\S Corresponde a qualquercaractere sem \s\S " _" em "int


espaço em branco. __ctr"

\d Corresponde a qualquer dígito decimal. \d "4" em "4 = IV"

\D Corresponde a qualquer caractere que \D " " , "=" , " " ,


não seja um dígito decimal. "I" e "V" em "4
= IV"
Âncoras
Âncoras ou asserções atômicas de largura zero, fazem com que uma correspondência tenha
êxito ou falha dependendo da posição atual na cadeia de caracteres, mas não fazem com que
o mecanismo avance na cadeia de caracteres ou consuma caracteres. Os metacaracteres
listados na tabela a seguir são âncoras. Para saber mais, confira Âncoras.

Asserção Descrição Padrão Correspondências

^ Por padrão, a correspondência precisa começar no ^\d{3} "901" em "901-


início da cadeia de caracteres. No modo multilinha, 333-"
precisa começar no início da linha.

$ Por padrão, a correspondência deve ocorrer no fim da -\d{3}$ "-333" em "-901-


cadeia de caracteres ou antes de \n no fim da cadeia 333"
de caracteres. No modo multilinha, deve antes do fim
da linha ou antes de \n no fim da linha.

\A A correspondência deve ocorrer no início da cadeia de \A\d{3} "901" em "901-


caracteres. 333-"

\Z A correspondência deve ocorrer no final da cadeia de -\d{3}\Z "-333" em "-901-


caracteres ou antes de \n no final da cadeia de 333"
caracteres.

\z A correspondência deve ocorrer no final da cadeia de -\d{3}\z "-333" em "-901-


caracteres. 333"

\G A correspondência deve ocorrer no ponto em que a \G\(\d\) "(1)" , "(3)" e "


correspondência anterior terminou, ou, caso não haja (5)" em "(1)(3)
correspondência anterior, na posição na cadeia de (5)[7](9)"
caracteres em que a correspondência foi iniciada.

\b A correspondência deve ocorrer em um limite entre \b\w+\s\w+\b "them theme" e


um caractere \w (alfanumérico) e um caractere \W "them them" em
(não alfanumérico). "them theme them
them"

\B A correspondência não deve ocorrer em um limite \b . \Bend\w*\b "ends" e "ender"


em "end sends
endure lender"

Constructos de agrupamento
Os constructos de agrupamento delineiam subexpressões de uma expressão regular e, em
geral, capturam subcadeias de caracteres de uma cadeia de caracteres de entrada. Os
constructos de agrupamento incluem os elementos de linguagem listados na tabela a seguir.
Para saber mais, confira Constructos de agrupamento.
Constructo de agrupamento Descrição Padrão Correspondências

( subexpression ) Captura a (\w)\1 "ee" em "deep"


subexpressão
correspondente
e atribui a ela
um número
ordinal com
base um.

(?< name > subexpression ) Captura a (? "ee" em "deep"


ou subexpressão <double>\w)\k<double>
(?' name ' subexpression ) correspondente
em um grupo
nomeado.

(?< name1 - name2 > subexpression ) Especifica uma (((?'Open'\()[^\(\)]*)+ "((1-3)*(3-1))" em


ou definição de ((?'Close-Open'\))[^\ "3+2^((1-3)*(3-1))"
(?' name1 - name2 ' subexpression ) grupo de (\)]*)+)*(?(Open)(?!))$
balanceamento.
Para saber mais,
confira a seção
"Definição de
grupo de
balanceamento"
em Constructos
de
agrupamento.

(?: subexpression ) Define um Write(?:Line)? "WriteLine" em


grupo de não "Console.WriteLine()"
captura.
"Write" em
"Console.Write(value)"

(?imnsx-imnsx: subexpression ) Aplica ou A\d{2}(?i:\w+)\b "A12xl" e "A12XL" em


desabilita as "A12xl A12XL a12xl"
opções
especificadas
em
subexpressão.
Para obter mais
informações,
consulte
Opções de
expressões
regulares.

(?= subexpression ) Asserções \b\w+\b(?=.+and.+) "cats" , "dogs"


lookahead em
positivas de "cats, dogs and some
largura zero. mice."
Constructo de agrupamento Descrição Padrão Correspondências

(?! subexpression ) Asserções \b\w+\b(?!.+and.+) "and" , "some" , "mice"


lookahead em
negativas de "cats, dogs and some
largura zero. mice."

(?<= subexpression ) Asserção \b\w+\b(?<=.+and.+) "some" , "mice"


lookbehind em
positiva de ——————————— "cats, dogs and some
largura zero. mice."
\b\w+\b(?<=.+and.*) ————————————
"and" , "some" , "mice"
em
"cats, dogs and some
mice."

(?<! subexpression ) Asserção \b\w+\b(?<!.+and.+) "cats" , "dogs" , "and"


lookbehind em
negativa de ——————————— "cats, dogs and some
largura zero. mice."
\b\w+\b(?<!.+and.*) ————————————
"cats" , "dogs"
em
"cats, dogs and some
mice."

(?> subexpression ) Grupo atômico. (?>a|ab)c "ac" in "ac"

nothing in "abc"

Visão geral rápida de lookarounds


Quando o mecanismo de expressão regular atinge uma expressão lookaround, ele usa uma
substring que alcança da posição atual até o início (lookbehind) ou final (lookahead) da cadeia
de caracteres original e, em seguida, executa Regex.IsMatch nessa substring usando o padrão
lookaround. O sucesso do resultado dessa subexpressão é então determinado se é uma
declaração positiva ou negativa.

Lookaround Nome Função

(?=check) Lookahead Declara que o que segue imediatamente a posição atual na cadeia de
positivo caracteres é "check"

(?<=check) Lookbehind Declara que o que precede imediatamente a posição atual na cadeia de
positivo caracteres é "check"

(?!check) Lookahead Declara que o que segue imediatamente a posição atual na cadeia de
negativo caracteres não é "check"
Lookaround Nome Função

(?<!check) Lookbehind Declara que o que precede imediatamente a posição atual na cadeia de
negativo caracteres não é "check"

Depois de corresponderem, os grupos atômicos não serão reavaliados outra vez, mesmo
quando o restante do padrão falhar devido à correspondência. Isso pode melhorar
significativamente o desempenho quando os quantificadores ocorrem dentro do grupo
atômico ou no restante do padrão.

Quantificadores
Um quantificador especifica quantas instâncias do elemento anterior (que pode ser um
caractere, um grupo ou uma classe de caracteres) devem estar presentes na cadeia de
caracteres de entrada para que uma correspondência ocorra. Os quantificadores incluem os
elementos de linguagem listados na tabela a seguir. Para saber mais, confira Quantificadores.

Quantificador Descrição Padrão Correspondências

* Corresponde ao elemento anterior zero ou a.*c "abcbc" em "abcbc"


mais vezes.

+ Corresponde ao elemento anterior uma ou "be+" "bee" em "been" , "be"


mais vezes. em "bent"

? Corresponde ao elemento anterior zero ou "rai?" "rai" em "rain"


uma vez.

{n} Corresponde ao elemento anterior ",\d{3}" ",043" em "1,043.6" ,


exatamente n vezes. ",876" , ",543" e ",210"
em "9,876,543,210"

{ n ,} Corresponde ao elemento anterior pelo "\d{2,}" "166" , "29" , "1930"


menos n vezes.

{n,m} Corresponde ao elemento anterior pelo "\d{3,5}" "166" , "17668"


menos n vezes, mas não mais do que m
vezes. "19302" em "193024"

*? Corresponde ao elemento anterior zero ou a.*?c "abc" em "abcbc"


mais vezes, mas o menor número de vezes
possível.

+? Corresponde ao elemento anterior uma ou "be+?" "be" em "been" , "be"


mais vezes, mas o menor número de vezes em "bent"
possível.

?? Corresponde ao elemento anterior zero ou "rai??" "ra" em "rain"


uma vez, mas o menor número de vezes
possível.
Quantificador Descrição Padrão Correspondências

{ n }? Corresponde ao elemento anterior ",\d{3}?" ",043" em "1,043.6" ,


exatamente n vezes. ",876" , ",543" e ",210"
em "9,876,543,210"

{ n ,}? Corresponde ao elemento anterior pelo "\d{2,}?" "166" , "29" , "1930"


menos n vezes, mas o menor número de
vezes possível.

{ n , m }? Corresponde ao elemento anterior entre n e "\d{3,5}?" "166" , "17668"


m vezes, mas o menor número de vezes
possível. "193" e "024" em
"193024"

Construtores de referência inversa


Um referência inversa permite que uma subexpressão correspondida anteriormente seja
identificada posteriormente na mesma expressão regular. A tabela a seguir lista os constructos
de referência inversa tem suporte nas expressões regulares do .NET. Para saber mais, confira
Constructos de referência inversa.

Constructo de Descrição Padrão Correspondências


referência
inversa

\ number Referência inversa. Corresponde ao valor (\w)\1 "ee" em "seek"


de uma subexpressão numerada.

\k< name > Referência inversa nomeada. (? "ee" em "seek"


Corresponde ao valor de uma expressão <char>\w)\k<char>
nomeada.

Construtores de alternância
Os constructos de alternância modificam uma expressão regular para habilitar uma
correspondência do tipo um/ou outro. Esses constructos incluem os elementos de linguagem
listados na tabela a seguir. Para saber mais, confira Constructos de alternância.

Constructo de Descrição Padrão Correspondências


alternância

| Corresponde a qualquer th(e|is|at) "the" e "this"


elemento separado pelo em "this is the
caractere de barra vertical ( | ). day."
Constructo de Descrição Padrão Correspondências
alternância

(? Corresponde a yes se o padrão (? "A10" e "910" em


( expression ) yes | no ) de expressão regular designado (A)A\d{2}\b|\b\d{3}\b) "A10 C103 910"
ou por expression for
(?( expression ) yes ) correspondente. Do contrário,
corresponde à parte no
opcional. expressão é
interpretado como uma
asserção de largura zero.

Para evitar ambiguidade com


um grupo de captura nomeado
ou numerado, você pode usar
opcionalmente uma declaração
explícita, como esta:
(?( (?= expression ) ) yes | no )

(?( name ) yes | no ) Corresponde a yes se name, um (?<quoted>")?(? "Dogs.jpg " e


ou grupo de captura nomeado ou (quoted).+?"|\S+\s) "\"Yiska
(?( name ) yes ) numerado, apresenta uma playing.jpg\""
correspondência. Do contrário, em "Dogs.jpg
corresponde a no opcional. \"Yiska
playing.jpg\""

Substituições
As substituições são elementos de linguagem de expressões regulares com suporte em
padrões de substituição. Para saber mais, confira Substituições. Os metacaracteres listados na
tabela a seguir são asserções atômicas de largura zero.

Caractere Descrição Padrão Padrão de Cadeia de Cadeia de


substituição caracteres caracteres
de entrada de resultado

$ number Substitui a subcadeia de \b(\w+)(\s) $3$2$1 "one two" "two one"


caracteres correspondida (\w+)\b
pelo grupo number.

${ name } Substitui a substring de \b(? ${word2} "one two" "two one"


caracteres correspondida <word1>\w+) ${word1}
pelo grupo chamado nome. (\s)(?
<word2>\w+)\b

$$ Substitui um literal "$". \b(\d+)\s? $$$1 "103 USD" "$103"


USD

$& Substitui uma cópia da \$?\d*\.?\d+ **$&** "$1.30" "**$1.30**"


correspondência inteira.
Caractere Descrição Padrão Padrão de Cadeia de Cadeia de
substituição caracteres caracteres
de entrada de resultado

$` Substitui todo o texto da B+ $` "AABBCC" "AAAACC"


cadeia de caracteres de
entrada antes da
correspondência.

$' Substitui todo o texto da B+ $' "AABBCC" "AACCCC"


cadeia de caracteres de
entrada após a
correspondência.

$+ Substitui o último grupo que B+(C+) $+ "AABBCCDD" "AACCDD"


foi capturado.

$_ Substitui a cadeia de B+ $_ "AABBCC" "AAAABBCCCC"


caracteres de entrada inteira.

Opções de expressões regulares


Você pode especificar opções que controlam como o mecanismo de expressões regulares
interpreta uma expressão regular padrão. Muitas dessas opções podem ser especificadas de
maneira embutida (no padrão da expressão regular) ou como uma ou mais constantes
RegexOptions. Essa referência rápida lista somente as opções embutidas. Para obter mais
informações sobre opções embutidas e RegexOptions, consulte o artigo Opções de
expressões regulares.

É possível especificar uma opção embutida de duas formas:

Usando o constructo diverso (?imnsx-imnsx) , em que um sinal de subtração (-) antes de


uma opção ou um conjunto de opções desativa essas opções. Por exemplo, (?i-mn)
ativa a correspondência sem diferenciação de maiúsculas e minúsculas ( i ), desativa o
modo de várias linhas ( m ) e desativa capturas de grupo sem nome ( n ). A opção se aplica
ao padrão de expressão regular no ponto em que a opção é definida e entra em vigor no
final do padrão ou no ponto em que outro constructo inverte a opção.
Usando o construtor de agrupamento (?imnsx-imnsx: subexpressão ) , que define opções
somente para o grupo especificado.

O mecanismo de expressões regulares do .NET dá suporte às opções em linha a seguir:

Opção Descrição Padrão Correspondências


Opção Descrição Padrão Correspondências

i Use correspondência sem \b(?i)a(?-i)a\w+\b "aardvark" e


diferenciação de maiúsculas e "aaaAuto" em
minúsculas. "aardvark AAAuto
aaaAuto Adam
breakfast"

m Use o modo multilinha. ^ e $ Para um exemplo, consulte a


correspondem ao início e ao fim de seção "Modo multilinha" em
uma linha em vez do início e fim de Opções de expressões
uma cadeia de caracteres. regulares.

n Não capture grupos sem nome. Para ver um exemplo, consulte


a seção "Apenas capturas
explícitas" em Opções de
expressões regulares.

s Use o modo de linha única. Para ver um exemplo, consulte


a seção "Modo de linha única"
em Opções de expressões
regulares.

x Ignore espaços em branco sem \b(?x) \d+ \s \w+ "1 aardvark" e "2
escape no padrão da expressão cats" em "1
regular. aardvark 2 cats IV
centurions"

Constructos diversos
Os constructos diversos modificam um expressão regular padrão ou fornecem informações
sobre ela. A tabela a seguir lista os constructos diversos que têm suporte no .NET. Para saber
mais, confira Constructos diversos.

Constructo Definição Exemplo

(?imnsx- Define ou desabilita opções como não diferenciação de \bA(?i)b\w+\b


imnsx) maiúsculas e minúsculas no meio de um padrão. Para saber corresponde a "ABA" e
mais, confira Opções de expressão regular. "Able" em "ABA Able
Act"

(? Comentário embutido. O comentário é encerrado no primeiro \bA(?#Matches words


# comment ) caractere de fechar parênteses. starting with A)\w+\b

# [até o Comentário do modo X. O comentário começa em um # sem (?x)\bA\w+\b#Matches


final da escape e continua até o final da linha. words starting with A
linha]

Confira também
System.Text.RegularExpressions
System.Text.RegularExpressions.Regex
Regular Expressions
Classes de expressões regulares
Expressões regulares - referência rápida (fazer download no formato Word)
Expressões regulares - referência rápida (fazer download no formato PDF)
Escapes de caracteres em expressões
regulares
Artigo • 10/05/2023

A barra invertida (\) em uma expressão regular indica uma destas situações:

O caractere que segue é um caractere especial, conforme mostrado na tabela na


seção a seguir. Por exemplo, \b é uma âncora que indica que uma
correspondência da expressão regular deve começar em um limite de palavra, \t
representa uma tabulação e \x020 representa um espaço.

Um caractere que, caso contrário, seria interpretado como um constructo de


linguagem sem escape deve ser interpretado literalmente. Por exemplo, uma chave
( { ) inicia a definição de um quantificador, mas uma barra invertida seguida por
uma chave ( \{ ) indica que o mecanismo de expressão regular deve corresponder
à chave. Da mesma forma, uma única barra invertida marca o início de um
constructo de linguagem com escape, mas duas barras invertidas ( \\ ) indicam que
o mecanismo de expressão regular deve corresponder à barra invertida.

7 Observação

Escapes de caracteres são reconhecidos em padrões de expressão regulares, mas


não em padrões de substituição.

Escapes de caracteres em .NET


A tabela a seguir lista os escapes de caracteres com suporte das expressões regulares
em .NET.

Caractere Descrição
ou
sequência
Caractere Descrição
ou
sequência

Todos os Caracteres diferentes dos listados na coluna Caractere ou sequência não têm
caracteres, significado especial em expressões regulares; eles correspondem a si mesmos.
exceto
pelos Os caracteres incluídos na coluna Caractere ou sequência são elementos especiais
seguintes: na linguagem de expressão regular. Para que seja feita a correspondência com eles
em uma expressão regular, eles devem receber um escape ou ser incluídos em um
.$^{[(| grupo de caracteres positivo. Por exemplo, a expressão regular \$\d+ ou [$]\d+
)*+?\ corresponde a "$1200".

\a Corresponde a um caractere de sino (alarme), \u0007 .

\b Em uma classe de caracteres [ character_group ] , corresponde a uma barra invertida,


\u0008 . (Consulte Classes de caracteres.) Fora de uma classe de caracteres, \b é uma
âncora que corresponde a um limite de palavra. (Confira Âncoras).

\t Corresponde a uma tabulação, \u0009 .

\r Corresponde a um retorno de carro, \u000D . Observe que \r não é equivalente ao


caractere de nova linha, \n .

\v Corresponde a uma tabulação vertical, \u000B .

\f Corresponde a um avanço de página, \u000C .

\n Corresponde a uma nova linha, \u000A .

\e Corresponde a um escape, \u001B .

\ nnn Corresponde a um caractere ASCII, em que nnn consiste em dois ou três dígitos que
representam o código de caracteres octal. Por exemplo, \040 representa um
caractere de espaço. Esse constructo é interpretado como referência inversa se tiver
apenas um dígito (por exemplo, \2 ) ou se corresponder ao número de um grupo de
captura. (Confira Constructos de referência inversa).

\x nn Corresponde a um caractere ASCII, em que nn é um código de caractere


hexadecimal com dois dígitos.

\c X Corresponde a um caractere de controle ASCII, em que X é a letra do caractere de


controle. Por exemplo, \cC é CTRL-C.

\u nnnn Corresponde a uma unidade de código UTF-16 cujo valor é nnnn hexadecimal.
Observação: o escape de caractere Perl 5 que é usado para especificar Unicode não
tem suporte no .NET. O escape de caractere Perl 5 tem o formato \x{ #### …} , em
que #### … é uma série de dígitos hexadecimais. Em vez disso, use \u nnnn.
Caractere Descrição
ou
sequência

\ Quando seguido por um caractere que não é reconhecido como um caractere com
escape, corresponde a esse caractere. Por exemplo, \* corresponde a um asterisco
(*) e é igual a \x2A .

Um Exemplo
O exemplo a seguir ilustra o uso de escapes de caracteres em uma expressão regular.
Ele analisa uma cadeia de caracteres que contém os nomes das maiores cidades do
mundo e suas populações em 2009. O nome de cada cidade é separado da sua
população por uma tabulação ( \t ) ou uma barra vertical (| ou \u007c ). Cidades
individuais e suas populações são separadas umas das outras por um retorno de carro e
uma alimentação de linha.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string delimited = @"\G(.+)[\t\u007c](.+)\r?\n";
string input = "Mumbai, India|13,922,125\t\n" +
"Shanghai, China\t13,831,900\n" +
"Karachi, Pakistan|12,991,000\n" +
"Delhi, India\t12,259,230\n" +
"Istanbul, Türkiye|11,372,613\n";
Console.WriteLine("Population of the World's Largest Cities, 2009");
Console.WriteLine();
Console.WriteLine("{0,-20} {1,10}", "City", "Population");
Console.WriteLine();
foreach (Match match in Regex.Matches(input, delimited))
Console.WriteLine("{0,-20} {1,10}", match.Groups[1].Value,
match.Groups[2].Value);
}
}
// The example displays the following output:
// Population of the World's Largest Cities, 2009
//
// City Population
//
// Mumbai, India 13,922,125
// Shanghai, China 13,831,900
// Karachi, Pakistan 12,991,000
// Delhi, India 12,259,230
// Istanbul, Türkiye 11,372,613

A expressão regular \G(.+)[\t\u007c](.+)\r?\n é interpretada conforme mostrado na


tabela a seguir.

Padrão Descrição

\G Inicia a correspondência onde a última correspondência terminou.

(.+) Corresponde qualquer caractere uma ou mais vezes. Este é o primeiro grupo de
captura.

[\t\u007c] Corresponde a uma tabulação ( \t ) ou a uma barra vertical (|).

(.+) Corresponde qualquer caractere uma ou mais vezes. Este é o segundo grupo de
captura.

\r?\n Corresponde a zero ou uma ocorrência de um retorno de carro, seguida por uma
nova linha.

Confira também
Linguagem de expressões regulares – referência rápida
Classes de caracteres em expressões
regulares
Artigo • 08/06/2023

Uma classe de caracteres define um conjunto de caracteres, qualquer dos quais pode
ocorrer em uma cadeia de caracteres de entrada para que uma correspondência seja
bem-sucedida. A linguagem de expressões regulares no .NET dá suporte às seguintes
classes de caracteres:

Grupos de caracteres positivos. Um caractere na cadeia de caracteres de entrada


deve corresponder a um de um conjunto especificado de caracteres. Para obter
mais informações, consulte Positive Character Group (Grupo de caracteres
positivos).

Grupo de caracteres negativos. Um caractere na cadeia de caracteres de entrada


não deve corresponder a um de um conjunto especificado de caracteres. Para
obter mais informações, consulte Negative Character Group (Grupo de caracteres
negativos).

Qualquer caractere. O . (ponto) em uma expressão regular é um caractere curinga


que corresponde a qualquer caractere, exceto \n . Para obter mais informações,
consulte Qualquer caractere.

Uma categoria geral de Unicode ou bloco nomeado. Um caractere na cadeia de


caracteres de entrada deve ser um membro de uma categoria de Unicode
específica ou deve estar dentro de um intervalo contíguo de caracteres Unicode
para que uma correspondência seja bem-sucedida. Para obter mais informações,
consulte Unicode Category or Unicode Block (Bloco de Unicode ou Categoria de
Unicode).

Uma categoria geral de Unicode ou bloco nomeado negativos. Um caractere na


cadeia de caracteres de entrada não deve ser um membro de uma categoria de
Unicode específica ou não deve estar dentro de um intervalo contíguo de
caracteres Unicode para que uma correspondência seja bem-sucedida. Para obter
mais informações, consulte Negative Unicode Category or Unicode Block
(Categoria Unicode Negativa ou bloco Unicode).

Um caractere de palavra. Um caractere na cadeia de caracteres de entrada pode


pertencer a qualquer uma das categorias Unicode que são apropriados para
caracteres usados em palavras. Para obter mais informações, consulte Word
Character (Caractere de palavra).
Um caractere não pertencente a palavras. Um caractere na cadeia de caracteres de
entrada pode pertencer a qualquer categoria Unicode que não seja um caractere
de palavra. Para obter mais informações, consulte Non-Word Character (Caractere
não pertencente a palavras).

Um caractere de espaço em branco. Um caractere na cadeia de caracteres de


entrada pode ser qualquer caractere separador Unicode, bem como qualquer um
entre vários caracteres de controle. Para obter mais informações, consulte White-
Space Character (Caractere de espaço em branco).

Um caractere diferente de espaço em branco. Um caractere na cadeia de


caracteres de entrada pode ser qualquer caractere que não seja um caractere de
espaço em branco. Para obter mais informações, consulte Non-White-Space
Character (Caractere diferente de espaço em branco).

Um dígito decimal. Um caractere na cadeia de caracteres de entrada pode ser


qualquer um entre vários caracteres classificados como dígitos decimais Unicode.
Para obter mais informações, consulte Decimal Digit Character (Caractere de dígito
decimal).

Um dígito não decimal. Um caractere na cadeia de caracteres de entrada pode ser


qualquer coisa que não seja um dígito decimal Unicode. Para obter mais
informações, consulte Decimal Digit Character (Caractere de dígito decimal).

O .NET dá suporte a expressões de subtração de classes de caracteres, o que permite a


você definir um conjunto de caracteres como resultado da exclusão de uma classe de
caracteres de outra classe de caracteres. Para obter mais informações, consulte
Subtração de classe de caracteres.

7 Observação

Classes de caractere que fazem a correspondência de caracteres por categoria,


como \w para a correspondência de caracteres de palavra ou \p{} para uma
categoria de Unicode, dependem da classe CharUnicodeInfo para fornecer
informações sobre categorias de caractere. No .NET Framework 4.6.2 e em versões
posteriores, as categorias de caracteres se baseiam no Padrão Unicode, versão
8.0.0 .

Grupo de caracteres positivo: [ ]


Um grupo de caracteres positivos especifica uma lista de caracteres, qualquer caractere
dela pode aparecer em uma cadeia de caracteres de entrada para que uma
correspondência ocorra. Essa lista de caracteres pode ser especificada individualmente,
como um intervalo ou ambos.

A sintaxe para especificar uma lista de caracteres individuais é a seguinte:

[*character_group*]

em que character_group é uma lista dos caracteres individuais que podem aparecer na
cadeia de caracteres de entrada para que uma correspondência seja bem-sucedida.
character_group pode ser composto por qualquer combinação de um ou mais caracteres
literais, caracteres de escape ou classes de caracteres.

A sintaxe para especificar um intervalo de caracteres é a seguinte:

[firstCharacter-lastCharacter]

em que firstCharacter é o caractere que inicia o intervalo e lastCharacter é o caractere


que encerra o intervalo. Um intervalo de caracteres é uma série contígua de caracteres
definida pela especificação do primeiro caractere na série, um hífen (-) e o último
caractere na série. Dois caracteres são contíguos se eles têm pontos de código Unicode
adjacentes. firstCharacter precisa ser o caractere com o ponto de código menor,
enquanto lastCharacter precisa ser o caractere com o ponto de código maior.

7 Observação

Visto que um grupo de caracteres positivos pode incluir um conjunto de caracteres


e um intervalo de caracteres, um caractere de hífen ( - ) é sempre interpretado
como o separador de intervalo, a menos que ele seja o primeiro ou último
caractere do grupo.

Alguns padrões de expressões regulares comuns que contêm classes de caracteres


positivos são listados na tabela a seguir.

Padrão Descrição

[aeiou] Corresponder a todas as vogais.

[\p{P}\d] Corresponder a todos os caracteres de pontuação e dígitos decimais.

[\s\p{P}] Corresponder a todos os espaços em branco e à pontuação.


O exemplo a seguir define um grupo de caracteres positivos que contém os caracteres
“a” e “e” para que a cadeia de caracteres de entrada contenha as palavras “grey” ou
“gray” seguida de outra palavra para que uma correspondência ocorra.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"gr[ae]y\s\S+?[\s\p{P}]";
string input = "The gray wolf jumped over the grey wall.";
MatchCollection matches = Regex.Matches(input, pattern);
foreach (Match match in matches)
Console.WriteLine($"'{match.Value}'");
}
}
// The example displays the following output:
// 'gray wolf '
// 'grey wall.'

A expressão regular gr[ae]y\s\S+?[\s|\p{P}] é definida da seguinte forma:

Padrão Descrição

gr Corresponder aos caracteres literais “gr”.

[ae] Corresponder a um "a" ou "e".

y\s Corresponder ao caractere literal “y” seguido por um caractere de espaço em branco.

\S+? Corresponder a um ou mais caracteres diferentes de espaço em branco, mas o


mínimo possível.

[\s\p{P}] Corresponder a um caractere de espaço em branco ou a um sinal de pontuação.

O exemplo a seguir corresponde a palavras que começam com qualquer letra


maiúscula. Ele usa a subexpressão [A-Z] para representar o intervalo de letras
maiúsculas de A a Z.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b[A-Z]\w*\b";
string input = "A city Albany Zulu maritime Marseilles";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// A
// Albany
// Zulu
// Marseilles

A expressão regular \b[A-Z]\w*\b é definida conforme mostrado na tabela a seguir.

Padrão Descrição

\b Iniciar em um limite de palavra.

[A-Z] Corresponder a qualquer caractere maiúsculo de A a Z.

\w* Corresponder a zero ou mais caracteres de palavra.

\b Corresponder a um limite de palavra.

Grupo de caracteres negativos: [^]


Um grupo de caracteres negativos especifica uma lista de caracteres que não devem
aparecer em uma cadeia de caracteres de entrada para que uma correspondência
ocorra. A lista de caracteres pode ser especificada individualmente, como um intervalo
ou ambos.

A sintaxe para especificar uma lista de caracteres individuais é a seguinte:

[*^character_group*]

em que character_group é uma lista dos caracteres individuais que não podem aparecer
na cadeia de caracteres de entrada para que uma correspondência seja bem-sucedida.
character_group pode ser composto por qualquer combinação de um ou mais caracteres
literais, caracteres de escape ou classes de caracteres.

A sintaxe para especificar um intervalo de caracteres é a seguinte:

[^*firstCharacter*-*lastCharacter*]
em que firstCharacter é o caractere que inicia o intervalo e lastCharacter é o caractere
que encerra o intervalo. Um intervalo de caracteres é uma série contígua de caracteres
definida pela especificação do primeiro caractere na série, um hífen (-) e o último
caractere na série. Dois caracteres são contíguos se eles têm pontos de código Unicode
adjacentes. firstCharacter precisa ser o caractere com o ponto de código menor,
enquanto lastCharacter precisa ser o caractere com o ponto de código maior.

7 Observação

Visto que um grupo de caracteres negativos pode incluir um conjunto de


caracteres e um intervalo de caracteres, um caractere de hífen ( - ) é sempre
interpretado como o separador de intervalo, a menos que ele seja o primeiro ou
último caractere do grupo.

Dois ou mais intervalos de caracteres podem ser concatenados. Por exemplo, para
especificar o intervalo de dígitos decimais de "0 " a "9", o intervalo de letras minúsculas
de "a" a "f" e o intervalo de letras maiúsculas "A" a "F", use [0-9a-fA-F] .

O caractere de circunflexo à esquerda ( ^ ) em um grupo de caracteres negativos é


obrigatório e indica que o grupo de caracteres é um grupo de caracteres negativos, e
não um grupo de caracteres positivos.

) Importante

Um grupo de caracteres negativos em uma expressão regular maior não é uma


asserção de largura zero. Ou seja, depois de avaliar o grupo de caracteres
negativos, o mecanismo de expressões regulares avança um caractere na cadeia de
caracteres de entrada.

Alguns padrões de expressões regulares comuns que contêm grupos de caracteres


negativos são listados na tabela a seguir.

Padrão Descrição

[^aeiou] Corresponder a todos os caracteres, exceto vogais.

[^\p{P}\d] Corresponder a todos os caracteres, exceto de pontuação e dígitos decimais.

O exemplo a seguir corresponde a todas as palavras que começam com os caracteres


“th” e não são seguidas por um “o”.

C#
using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\bth[^o]\w+\b";
string input = "thought thing though them through thus thorough this";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// thing
// them
// through
// thus
// this

A expressão regular \bth[^o]\w+\b é definida conforme mostrado na tabela a seguir.

Padrão Descrição

\b Iniciar em um limite de palavra.

th Corresponder aos caracteres literais “th”.

[^o] Corresponder a qualquer caractere que não seja um “o”.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

\b Terminar em um limite de palavra.

Qualquer caractere: .
O caractere de ponto (.) corresponde a qualquer caractere, exceto \n (o caractere de
nova linha, \u000A), com estas duas qualificações:

Se um padrão de expressão regular é modificado pela opção


RegexOptions.Singleline, ou se a parte do padrão que contém a classe de
caracteres . é modificada pela opção s , . corresponde a qualquer caractere. Para
obter mais informações, consulte Opções de expressões regulares.

O exemplo a seguir ilustra o comportamento diferente da classe de caracteres .


por padrão e com a opção RegexOptions.Singleline. A expressão regular ^.+
começa no início da cadeia de caracteres e corresponde a todos os caracteres. Por
padrão, a correspondência termina no final da primeira linha; o padrão de
expressão regular corresponde ao caractere de retorno de carro, \r ou \ u000D,
mas não corresponde a \n . Como a opção RegexOptions.Singleline interpreta toda
a cadeia de caracteres de entrada como uma única linha, ela corresponde a cada
caractere na cadeia de caracteres de entrada, incluindo \n .

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "^.+";
string input = "This is one line and" + Environment.NewLine +
"this is the second.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(Regex.Escape(match.Value));

Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.Singleline))
Console.WriteLine(Regex.Escape(match.Value));
}
}
// The example displays the following output:
// This\ is\ one\ line\ and\r
//
// This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.

7 Observação

Como ela corresponde a qualquer caractere, exceto \n , a classe de caracteres .


também corresponde a \r (o caractere de retorno de carro, \u000D).

Em um grupo de caracteres positivos ou negativos, um ponto é tratado como um


caractere de ponto literal e não como uma classe de caracteres. Para saber mais,
confira Grupo de caracteres positivos e Grupo de caracteres negativos
anteriormente neste tópico. O exemplo a seguir fornece uma ilustração ao definir
uma expressão regular que inclui o caractere de ponto ( . ) como uma classe de
caracteres e como um membro de um grupo de caracteres positivos. A expressão
regular \b.*[.?!;:](\s|\z) começa em um limite de palavra, corresponde a
qualquer caractere até encontrar um de cinco sinais de pontuação, incluindo um
ponto, e corresponde a um caractere de espaço em branco ou ao final da cadeia
de caracteres.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b.*[.?!;:](\s|\z)";
string input = "this. what: is? go, thing.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// this. what: is? go, thing.

7 Observação

Como ele corresponde a qualquer caractere, o elemento de linguagem . é


frequentemente usado com um quantificador lento se um padrão de expressão
regular tenta corresponder a qualquer caractere várias vezes. Para saber mais,
confira Quantificadores.

Categoria de Unicode ou bloco Unicode: \p{}


O padrão Unicode atribui a cada caractere uma categoria geral. Por exemplo, um
caractere específico pode ser uma letra maiúscula (representada pela categoria Lu ), um
dígito decimal (a categoria Nd ), um símbolo matemático (a categoria Sm ) ou um
separador de parágrafo (a categoria Zl ). Os conjuntos de caracteres específicos no
padrão Unicode também ocupam um intervalo específico ou um bloco de pontos de
código consecutivos. Por exemplo, o conjunto de caracteres latinos básico é de \u0000 a
\u007F, enquanto que o conjunto de caracteres árabe vai de \u0600 a \u06FF.

A constructo da expressão regular

\p{ name }

corresponde a qualquer caractere que pertence a uma categoria geral de Unicode ou a


um bloco nomeado, em que name é a abreviação da categoria ou o nome do bloco
nomeado. Para obter uma lista de abreviações de categoria, consulte a seção Categorias
gerais Unicode com suporte posteriormente neste tópico. Para obter uma lista dos
blocos nomeados, consulte a seção Blocos nomeados com suporte posteriormente
neste tópico.

 Dica

A correspondência poderá ser melhorada se a cadeia de caracteres for normalizada


pela primeira vez chamando o método String.Normalize.

O exemplo a seguir usa o constructo \p{ name } para corresponder a uma categoria de
Unicode geral (nesse caso, a categoria Pd ou Pontuação, Traço) e a um bloco nomeado
(os blocos nomeados IsGreek e IsBasicLatin ).

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\p{IsGreek}+(\s)?)+\p{Pd}\s(\p{IsBasicLatin}+
(\s)?)+";
string input = "Κατα Μαθθαίον - The Gospel of Matthew";

Console.WriteLine(Regex.IsMatch(input, pattern)); // Displays


True.
}
}

A expressão regular \b(\p{IsGreek}+(\s)?)+\p{Pd}\s(\p{IsBasicLatin}+(\s)?)+ é


definida conforme mostrado na tabela a seguir.

Padrão Descrição

\b Iniciar em um limite de palavra.

\p{IsGreek}+ Corresponder a um ou mais caracteres gregos.

(\s)? Corresponder a zero ou a um caractere de espaço em branco.

(\p{IsGreek}+ Corresponder ao padrão de um ou mais caracteres gregos seguidos por


(\s)?)+ zero ou um caractere de espaço em branco uma ou mais vezes.
Padrão Descrição

\p{Pd} Corresponder a um caractere de pontuação, traço.

\s Corresponde a um caractere de espaço em branco.

\p{IsBasicLatin}+ Corresponder a um ou mais caracteres latinos.

(\s)? Corresponder a zero ou a um caractere de espaço em branco.

(\p{IsBasicLatin}+ Corresponder ao padrão de um ou mais caracteres latinos básicos seguidos


(\s)?)+ por zero ou um caractere de espaço em branco uma ou mais vezes.

Categoria Unicode negativa ou bloco Unicode:


\P{}
O padrão Unicode atribui a cada caractere uma categoria geral. Por exemplo, um
caractere específico pode ser uma letra maiúscula (representada pela categoria Lu ), um
dígito decimal (a categoria Nd ), um símbolo matemático (a categoria Sm ) ou um
separador de parágrafo (a categoria Zl ). Os conjuntos de caracteres específicos no
padrão Unicode também ocupam um intervalo específico ou um bloco de pontos de
código consecutivos. Por exemplo, o conjunto de caracteres latinos básico é de \u0000 a
\u007F, enquanto que o conjunto de caracteres árabe vai de \u0600 a \u06FF.

A constructo da expressão regular

\P{ name }

corresponde a qualquer caractere que não pertença a uma categoria geral de Unicode
ou a um bloco nomeado, em que name é a abreviação da categoria ou o nome do
bloco nomeado. Para obter uma lista de abreviações de categoria, consulte a seção
Categorias gerais Unicode com suporte posteriormente neste tópico. Para obter uma
lista dos blocos nomeados, consulte a seção Blocos nomeados com suporte
posteriormente neste tópico.

 Dica

A correspondência poderá ser melhorada se a cadeia de caracteres for normalizada


pela primeira vez chamando o método String.Normalize.

O exemplo a seguir usa o constructo \P{ name } para remover quaisquer símbolos de
moeda (nesse caso, categoria Sc , ou Símbolo, Moeda) de cadeias de caracteres
numéricas.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(\P{Sc})+";

string[] values = { "$164,091.78", "£1,073,142.68", "73¢", "€120" };


foreach (string value in values)
Console.WriteLine(Regex.Match(value, pattern).Value);
}
}
// The example displays the following output:
// 164,091.78
// 1,073,142.68
// 73
// 120

O padrão de expressão regular (\P{Sc})+ corresponde a um ou mais caracteres que


não são símbolos de moeda; ele remove efetivamente qualquer símbolo de moeda da
cadeia de caracteres de resultado.

Caractere de palavra: \w
\w corresponde a qualquer caractere de palavra. Um caractere de palavra é um membro
de qualquer uma das categorias Unicode listadas na tabela a seguir.

Categoria Descrição

Ll Letra, Lominúscula

Lu Letra, maiúscula

Lt Letra, título

Lo Letra, outra

Lm Letra, modificador

Mn Marca, sem espaçamento

Nd Número, dígito decimal


Categoria Descrição

Pc Pontuação, conector. Essa categoria inclui dez caracteres, o mais comumente usado
deles é o LOWLINE (_), u+005F.

Se o comportamento compatível com ECMAScript for especificado, \w será equivalente


a [a-zA-Z_0-9] . Para obter informações sobre expressões regulares ECMAScript,
consulte a seção “Comportamento de correspondência ECMAScript" em Opções de
expressões regulares.

7 Observação

Como ele corresponde a qualquer caractere de palavra, o elemento de linguagem


\w é frequentemente usado com um quantificador lento se um padrão de

expressão regular tenta corresponder a qualquer caractere de palavra várias vezes,


seguido por um caractere de palavra específico. Para saber mais, confira
Quantificadores.

O exemplo a seguir usa o elemento de linguagem \w para corresponder a caracteres


duplicados em uma palavra. O exemplo define um padrão de expressão regular, (\w)\1 ,
que pode ser interpretado como a seguir.

Elemento Descrição

(\w) Corresponder a um caractere de palavra. Este é o primeiro grupo de captura.

\1 Corresponder ao valor da primeira captura.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(\w)\1";
string[] words = { "trellis", "seer", "latter", "summer",
"hoarse", "lesser", "aardvark", "stunned" };
foreach (string word in words)
{
Match match = Regex.Match(word, pattern);
if (match.Success)
Console.WriteLine("'{0}' found in '{1}' at position {2}.",
match.Value, word, match.Index);
else
Console.WriteLine("No double characters in '{0}'.", word);
}
}
}
// The example displays the following output:
// 'll' found in 'trellis' at position 3.
// 'ee' found in 'seer' at position 1.
// 'tt' found in 'latter' at position 2.
// 'mm' found in 'summer' at position 2.
// No double characters in 'hoarse'.
// 'ss' found in 'lesser' at position 2.
// 'aa' found in 'aardvark' at position 0.
// 'nn' found in 'stunned' at position 3.

Caractere não pertencente a palavras: \W


\W corresponde a qualquer caractere que não seja uma palavra. O elemento de

linguagem \W é equivalente à seguinte classe de caracteres:

[^\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}]

Em outras palavras, ele corresponde a qualquer caractere, com exceção dos listados nas
categorias de Unicode na tabela a seguir.

Categoria Descrição

Ll Letra, Lominúscula

Lu Letra, maiúscula

Lt Letra, título

Lo Letra, outra

Lm Letra, modificador

Mn Marca, sem espaçamento

Nd Número, dígito decimal

Pc Pontuação, conector. Essa categoria inclui dez caracteres, o mais comumente usado
deles é o LOWLINE (_), u+005F.

Se o comportamento compatível com ECMAScript for especificado, \W será equivalente


a [^a-zA-Z_0-9] . Para obter informações sobre expressões regulares ECMAScript,
consulte a seção “Comportamento de correspondência ECMAScript" em Opções de
expressões regulares.
7 Observação

Como ele corresponde a qualquer caractere não pertencente a palavras, o


elemento de linguagem \W é frequentemente usado com um quantificador lento
se um padrão de expressão regular tenta corresponder a qualquer caractere não
pertencente a palavras várias vezes, seguido por um caractere não pertencente a
palavras específico. Para saber mais, confira Quantificadores.

O exemplo a seguir ilustra a classe de caracteres \W . Ele define um padrão de expressão


regular, \b(\w+)(\W){1,2} , que corresponde a uma palavra seguida por um ou dois
caracteres não pertencentes a palavras, como espaço em branco ou pontuação. A
expressão regular é interpretada conforme mostrado na tabela a seguir.

Elemento Descrição

\b Começar a correspondência em um limite de palavra.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Este é o primeiro grupo


de captura.

(\W){1,2} Corresponde a um caractere não pertencente a palavras uma ou duas vezes. Este é o
segundo grupo de captura.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\w+)(\W){1,2}";
string input = "The old, grey mare slowly walked across the narrow,
green pasture.";
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine(match.Value);
Console.Write(" Non-word character(s):");
CaptureCollection captures = match.Groups[2].Captures;
for (int ctr = 0; ctr < captures.Count; ctr++)
Console.Write(@"'{0}' (\u{1}){2}", captures[ctr].Value,

Convert.ToUInt16(captures[ctr].Value[0]).ToString("X4"),
ctr < captures.Count - 1 ? ", " : "");
Console.WriteLine();
}
}
}
// The example displays the following output:
// The
// Non-word character(s):' ' (\u0020)
// old,
// Non-word character(s):',' (\u002C), ' ' (\u0020)
// grey
// Non-word character(s):' ' (\u0020)
// mare
// Non-word character(s):' ' (\u0020)
// slowly
// Non-word character(s):' ' (\u0020)
// walked
// Non-word character(s):' ' (\u0020)
// across
// Non-word character(s):' ' (\u0020)
// the
// Non-word character(s):' ' (\u0020)
// narrow,
// Non-word character(s):',' (\u002C), ' ' (\u0020)
// green
// Non-word character(s):' ' (\u0020)
// pasture.
// Non-word character(s):'.' (\u002E)

Como o objeto Group do segundo grupo de captura contém apenas um único caractere
capturado não pertencente a palavras, o exemplo recupera todos os caracteres
capturados não pertencentes a palavras do objeto CaptureCollection retornado pela
propriedade Group.Captures.

Caractere de espaço em branco: \s


\s corresponde a um caractere de espaço em branco. É equivalente às sequências de

escape e às categorias de Unicode listadas na tabela a seguir.

Categoria Descrição

\f O caractere de avanço de página, \u000C.

\n O caractere de nova linha, \u000A.

\r O caractere de retorno de carro, \u000D.

\t O caractere de tabulação, \u0009.

\v O caractere de tabulação vertical, \u000B.

\x85 O caractere NEXT LINE (NEL), \u0085.


Categoria Descrição

\p{Z} Corresponde a todos os caracteres separadores. Isso inclui as categorias Zs , Zl e


Zp .

Se o comportamento compatível com ECMAScript for especificado, \s será equivalente


a [ \f\n\r\t\v] . Para obter informações sobre expressões regulares ECMAScript,
consulte a seção “Comportamento de correspondência ECMAScript" em Opções de
expressões regulares.

O exemplo a seguir ilustra a classe de caracteres \s . Ele define um padrão de expressão


regular, \b\w+(e)?s(\s|$) , que corresponde a uma palavra que termina em “s” ou em
“es” seguida por um caractere de espaço em branco ou pelo final da cadeia de
caracteres de entrada. A expressão regular é interpretada conforme mostrado na tabela
a seguir.

Elemento Descrição

\b Começar a correspondência em um limite de palavra.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

(e)? Corresponder a um “e” zero ou uma vez.

s Corresponder a um “s”.

(\s|$) Corresponder a um caractere de espaço em branco ou ao fim da cadeia de caracteres


de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b\w+(e)?s(\s|$)";
string input = "matches stores stops leave leaves";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// matches
// stores
// stops
// leaves
Caractere diferente de espaço em branco: \S
\S corresponde a qualquer caractere que não seja um caractere de espaço em branco.

Ele é equivalente ao padrão de expressão regular [^\f\n\r\t\v\x85\p{Z}] ou o oposto


do padrão de expressão regular equivalente a \s , o qual corresponde a caracteres de
espaço em branco. Para saber mais, confira Caractere de espaço em branco: \s.

Se o comportamento compatível com ECMAScript for especificado, \S será equivalente


a [^ \f\n\r\t\v] . Para obter informações sobre expressões regulares ECMAScript,
consulte a seção “Comportamento de correspondência ECMAScript" em Opções de
expressões regulares.

O exemplo a seguir ilustra o elemento de linguagem \S . O padrão de expressão regular


\b(\S+)\s? corresponde a cadeias de caracteres que são delimitadas por caracteres de
espaço em branco. O segundo elemento no objeto GroupCollection da correspondência
contém a cadeia de caracteres correspondida. A expressão regular pode ser interpretada
conforme mostrado na tabela a seguir.

Elemento Descrição

\b Começar a correspondência em um limite de palavra.

(\S+) Corresponder a um ou mais caracteres diferentes de espaço em branco. Este é o


primeiro grupo de captura.

\s? Corresponder a zero ou a um caractere de espaço em branco.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\S+)\s?";
string input = "This is the first sentence of the first paragraph. " +
"This is the second sentence.\n" +
"This is the only sentence of the second
paragraph.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Groups[1]);
}
}
// The example displays the following output:
// This
// is
// the
// first
// sentence
// of
// the
// first
// paragraph.
// This
// is
// the
// second
// sentence.
// This
// is
// the
// only
// sentence
// of
// the
// second
// paragraph.

Caractere de dígito decimal: \d


\d corresponde a qualquer dígito decimal. É equivalente ao padrão de expressão

regular \p{Nd} , o qual inclui os dígitos decimais padrão 0-9, bem como os dígitos
decimais de vários outros conjuntos de caracteres.

Se o comportamento compatível com ECMAScript for especificado, \d será equivalente


a [0-9] . Para obter informações sobre expressões regulares ECMAScript, consulte a
seção “Comportamento de correspondência ECMAScript" em Opções de expressões
regulares.

O exemplo a seguir ilustra o elemento de linguagem \d . Ele testa se uma cadeia de


caracteres de entrada representa um número de telefone válido nos Estados Unidos e
no Canadá. O padrão de expressão regular ^(\(?\d{3}\)?[\s-])?\d{3}-\d{4}$ é
definido conforme mostrado na tabela a seguir.

Elemento Descrição

^ Começar a correspondência no início da cadeia de caracteres de entrada.

\(? Corresponder a zero ou a um caractere "(" literal.


Elemento Descrição

\d{3} Corresponder a três dígitos decimais.

\)? Corresponder a zero ou a um caractere ")" literal.

[\s-] Corresponder a um hífen ou a caractere de espaço em branco.

(\(? Corresponder a um parêntese de abertura opcional seguido por três dígitos decimais,
\d{3}\)? um parêntese de fechamento opcional e um caractere de espaço em branco ou um
[\s-])? hífen zero ou uma vez. Este é o primeiro grupo de captura.

\d{3}- Corresponder aos três dígitos decimais seguidos por um hífen e mais quatro dígitos
\d{4} decimais.

$ Corresponder ao final da cadeia de caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"^(\(?\d{3}\)?[\s-])?\d{3}-\d{4}$";
string[] inputs = { "111 111-1111", "222-2222", "222 333-444",
"(212) 111-1111", "111-AB1-1111",
"212-111-1111", "01 999-9999" };

foreach (string input in inputs)


{
if (Regex.IsMatch(input, pattern))
Console.WriteLine(input + ": matched");
else
Console.WriteLine(input + ": match failed");
}
}
}
// The example displays the following output:
// 111 111-1111: matched
// 222-2222: matched
// 222 333-444: match failed
// (212) 111-1111: matched
// 111-AB1-1111: match failed
// 212-111-1111: matched
// 01 999-9999: match failed

Caractere que não seja dígito: \D


\D corresponde a qualquer caractere que não seja um dígito. É equivalente ao padrão

de expressão regular \P{Nd} .

Se o comportamento compatível com ECMAScript for especificado, \D será equivalente


a [^0-9] . Para obter informações sobre expressões regulares ECMAScript, consulte a
seção “Comportamento de correspondência ECMAScript" em Opções de expressões
regulares.

O exemplo a seguir ilustra o elemento de linguagem \D. Ele testa se uma cadeia de
caracteres, como um número de peça, consiste na combinação apropriada de caracteres
decimais e não decimais. O padrão de expressão regular ^\D\d{1,5}\D*$ é definido
conforme mostrado na tabela a seguir.

Elemento Descrição

^ Começar a correspondência no início da cadeia de caracteres de entrada.

\D Corresponder a um caractere que não seja um dígito.

\d{1,5} Corresponder a um a cinco dígitos decimais.

\D* Corresponder a zero ou a um ou mais caracteres não decimais.

$ Corresponder ao final da cadeia de caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"^\D\d{1,5}\D*$";
string[] inputs = { "A1039C", "AA0001", "C18A", "Y938518" };

foreach (string input in inputs)


{
if (Regex.IsMatch(input, pattern))
Console.WriteLine(input + ": matched");
else
Console.WriteLine(input + ": match failed");
}
}
}
// The example displays the following output:
// A1039C: matched
// AA0001: match failed
// C18A: matched
// Y938518: match failed

Categorias gerais Unicode com suporte


O Unicode define as categorias gerais listadas na tabela a seguir. Para obter mais
informações, consulte os subtópicos "Formato de arquivo UCD" e "Valores de categoria
geral" no Banco de dados de caractere Unicode , Sec. 5.7.1, Tabela 12.

Categoria Descrição

Lu Letra, maiúscula

Ll Letra, Lominúscula

Lt Letra, título

Lm Letra, modificador

Lo Letra, outra

L Todos os caracteres de letras. Isso inclui os caracteres Lu , Ll , Lt , Lm e Lo .

Mn Marca, sem espaçamento

Mc Marca, combinação de espaçamento

Me Marca, delimitador

M Todas as marcas de combinação. Isso inclui as categorias Mn , Mc e Me .

Nd Número, dígito decimal

Nl Número, letra

No Número, outro

N Todos os números. Isso inclui as categorias Nd , Nl e No .

Pc Pontuação, conector

Pd Pontuação, traço

Ps Pontuação, abertura

Pe Pontuação, fechamento

Pi Pontuação, aspas iniciais (podem se comportar como Ps ou Pe, dependendo do uso)


Categoria Descrição

Pf Pontuação, aspas finais (podem se comportar como Ps ou Pe, dependendo do uso)

Po Pontuação, outros

P Todos os caracteres de pontuação. Isso inclui as categorias Pc , Pd , Ps , Pe , Pi , Pf e


Po .

Sm Símbolo, matemático

Sc Símbolo, moeda

Sk Símbolo, modificador

So Símbolo, outros

S Todos os símbolos. Isso inclui as categorias Sm , Sc , Sk e So .

Zs Separador, espaço

Zl Separador, linha

Zp Separador, parágrafo

Z Todos os caracteres de separador. Isso inclui as categorias Zs , Zl e Zp .

Cc Outros, controle

Cf Outros, formato

Cs Outros, substitutos

Co Outros, uso privado

Cn Outro, não atribuído ou não caractere

C Todos os outros caracteres. Isso inclui as categorias Cc , Cf , Cs , Co e Cn .

Você pode determinar a categoria Unicode de qualquer caractere específico ao passar


esse caractere para o método GetUnicodeCategory. O exemplo a seguir usa o método
GetUnicodeCategory para determinar a categoria de cada elemento em uma matriz que
contém caracteres latinos selecionados.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
char[] chars = { 'a', 'X', '8', ',', ' ', '\u0009', '!' };

foreach (char ch in chars)


Console.WriteLine("'{0}': {1}", Regex.Escape(ch.ToString()),
Char.GetUnicodeCategory(ch));
}
}
// The example displays the following output:
// 'a': LowercaseLetter
// 'X': UppercaseLetter
// '8': DecimalDigitNumber
// ',': OtherPunctuation
// '\ ': SpaceSeparator
// '\t': Control
// '!': OtherPunctuation

Blocos nomeados compatíveis


O .NET fornece os blocos nomeados listados na tabela a seguir. O conjunto de blocos
nomeados com suporte baseia-se no Unicode 4.0 e no Perl 5.6. Para uma expressão
regular que usa blocos nomeados, consulte a seção Categoria Unicode ou bloco
Unicode: \p{}.

Intervalo de ponto de código Nome do bloco

0000 - 007F IsBasicLatin

0080 - 00FF IsLatin-1Supplement

0100 - 017F IsLatinExtended-A

0180 - 024F IsLatinExtended-B

0250 - 02AF IsIPAExtensions

02B0 - 02FF IsSpacingModifierLetters

0300 - 036F IsCombiningDiacriticalMarks

0370 - 03FF IsGreek

-ou-

IsGreekandCoptic

0400 - 04FF IsCyrillic


Intervalo de ponto de código Nome do bloco

0500 - 052F IsCyrillicSupplement

0530 - 058F IsArmenian

0590 - 05FF IsHebrew

0600 - 06FF IsArabic

0700 - 074F IsSyriac

0780 - 07BF IsThaana

0900 - 097F IsDevanagari

0980 - 09FF IsBengali

0A00 - 0A7F IsGurmukhi

0A80 - 0AFF IsGujarati

0B00 - 0B7F IsOriya

0B80 - 0BFF IsTamil

0C00 - 0C7F IsTelugu

0C80 - 0CFF IsKannada

0D00 - 0D7F IsMalayalam

0D80 - 0DFF IsSinhala

0E00 - 0E7F IsThai

0E80 - 0EFF IsLao

0F00 - 0FFF IsTibetan

1000 - 109F IsMyanmar

10A0 - 10FF IsGeorgian

1100 - 11FF IsHangulJamo

1200 - 137F IsEthiopic

13A0 - 13FF IsCherokee

1400 - 167F IsUnifiedCanadianAboriginalSyllabics


Intervalo de ponto de código Nome do bloco

1680 - 169F IsOgham

16A0 - 16FF IsRunic

1700 - 171F IsTagalog

1720 - 173F IsHanunoo

1740 - 175F IsBuhid

1760 - 177F IsTagbanwa

1780 - 17FF IsKhmer

1800 - 18AF IsMongolian

1900 - 194F IsLimbu

1950 - 197F IsTaiLe

19E0 - 19FF IsKhmerSymbols

1D00 - 1D7F IsPhoneticExtensions

1E00 - 1EFF IsLatinExtendedAdditional

1F00 - 1FFF IsGreekExtended

2000 - 206F IsGeneralPunctuation

2070 - 209F IsSuperscriptsandSubscripts

20A0 - 20CF IsCurrencySymbols

20D0 - 20FF IsCombiningDiacriticalMarksforSymbols

-ou-

IsCombiningMarksforSymbols

2100 - 214F IsLetterlikeSymbols

2150 - 218F IsNumberForms

2190 - 21FF IsArrows

2200 - 22FF IsMathematicalOperators

2300 - 23FF IsMiscellaneousTechnical


Intervalo de ponto de código Nome do bloco

2400 - 243F IsControlPictures

2440 - 245F IsOpticalCharacterRecognition

2460 - 24FF IsEnclosedAlphanumerics

2500 - 257F IsBoxDrawing

2580 - 259F IsBlockElements

25A0 - 25FF IsGeometricShapes

2600 - 26FF IsMiscellaneousSymbols

2700 - 27BF IsDingbats

27C0 - 27EF IsMiscellaneousMathematicalSymbols-A

27F0 - 27FF IsSupplementalArrows-A

2800 - 28FF IsBraillePatterns

2900 - 297F IsSupplementalArrows-B

2980 - 29FF IsMiscellaneousMathematicalSymbols-B

2A00 - 2AFF IsSupplementalMathematicalOperators

2B00 - 2BFF IsMiscellaneousSymbolsandArrows

2E80 - 2EFF IsCJKRadicalsSupplement

2F00 - 2FDF IsKangxiRadicals

2FF0 - 2FFF IsIdeographicDescriptionCharacters

3000 - 303F IsCJKSymbolsandPunctuation

3040 - 309F IsHiragana

30A0 - 30FF IsKatakana

3100 - 312F IsBopomofo

3130 - 318F IsHangulCompatibilityJamo

3190 - 319F IsKanbun

31A0 - 31BF IsBopomofoExtended


Intervalo de ponto de código Nome do bloco

31F0 - 31FF IsKatakanaPhoneticExtensions

3200 - 32FF IsEnclosedCJKLettersandMonths

3300 - 33FF IsCJKCompatibility

3400 - 4DBF IsCJKUnifiedIdeographsExtensionA

4DC0 - 4DFF IsYijingHexagramSymbols

4E00 - 9FFF IsCJKUnifiedIdeographs

A000 - A48F IsYiSyllables

A490 - A4CF IsYiRadicals

AC00 - D7AF IsHangulSyllables

D800 - DB7F IsHighSurrogates

DB80 - DBFF IsHighPrivateUseSurrogates

DC00 - DFFF IsLowSurrogates

E000 - F8FF IsPrivateUse ou IsPrivateUseArea

F900 - FAFF IsCJKCompatibilityIdeographs

FB00 - FB4F IsAlphabeticPresentationForms

FB50 - FDFF IsArabicPresentationForms-A

FE00 - FE0F IsVariationSelectors

FE20 - FE2F IsCombiningHalfMarks

FE30 - FE4F IsCJKCompatibilityForms

FE50 - FE6F IsSmallFormVariants

FE70 - FEFF IsArabicPresentationForms-B

FF00 - FFEF IsHalfwidthandFullwidthForms

FFF0 - FFFF IsSpecials

Subtração de classes de caractere: [base_group


- [excluded_group]]
Uma classe de caracteres define um conjunto de caracteres. A subtração de classes de
caracteres fornece um conjunto de caracteres que é o resultado da exclusão dos
caracteres em uma classe de caracteres em outra classe de caracteres.

A expressão de subtração de classes de caracteres tem a seguinte forma:

[ base_group -[ excluded_group ]]

Os colchetes ( [] ) e hífen ( - ) são obrigatórios. O base_group é um grupo de caracteres


positivos ou um grupo de caracteres negativos. O componente excluded_group é outro
grupo de caracteres positivos ou negativos ou outra expressão de subtração de classes
de caracteres (ou seja, você pode aninhar expressões de subtração de classes de
caracteres).

Por exemplo, suponha que você tenha um grupo base que consiste no intervalo de
caracteres de "a" a "z". Para definir o conjunto de caracteres que consiste no grupo
base, exceto pelo caractere "m", use [a-z-[m]] . Para definir o conjunto de caracteres
que consiste no grupo base, exceto pelo conjunto de caracteres "d", "j" e "p", use [a-z-
[djp]] . Para definir o conjunto de caracteres que consiste no base consiste, exceto pelo

intervalo de "m" a "p", use [a-z-[m-p]] .

Considere a expressão de subtração de classes de caracteres aninhada, [a-z-[d-w-[m-


o]]] . A expressão é calculada do intervalo de caracteres mais interno para fora.

Primeiro, o intervalo de caracteres de "m" a "o" é subtraído do intervalo de caractere de


"d" a "w", o que produz o conjunto de caracteres de "d" a "l" e "p" a "w". Esse conjunto é
subtraído do intervalo de caracteres de "a" a "z", o que produz o conjunto de caracteres
[abcmnoxyz] .

Você pode usar qualquer classe de caracteres com a subtração de classes de caracteres.
Para definir o conjunto de caracteres que consiste em todos os caracteres Unicode de
\u0000 a \uFFFF, exceto caracteres de espaço em branco ( \s ), os caracteres na
categoria geral de pontuação ( \p{P} ), os caracteres no bloco nomeado IsGreek
( \p{IsGreek} ) e o caractere de controle Unicode NEXT LINE (\x85), utilize [\u0000-
\uFFFF-[\s\p{P}\p{IsGreek}\x85]] .

Escolha classes de caracteres para uma expressão de subtração de classes de caracteres


que produza resultados úteis. Evite uma expressão que gere um conjunto de caracteres
vazio, que não podem corresponder a nada ou uma expressão equivalente ao grupo
base original. Por exemplo, o conjunto vazio é o resultado da expressão
[\p{IsBasicLatin}-[\x00-\x7F]] , que subtrai todos os caracteres do intervalo de

caracteres IsBasicLatin da categoria geral IsBasicLatin . Da mesma forma, o grupo


base original é o resultado da expressão [a-z-[0-9]] . Isso ocorre, porque o grupo base,
que é o intervalo de caracteres de letras de "a" a "z", não contém quaisquer caracteres
no grupo excluído, que é o intervalo de caracteres de dígitos decimais de "0" a "9".

O exemplo a seguir define uma expressão regular, ^[0-9-[2468]]+$ , que corresponde


aos dígitos zero e ímpares em uma cadeia de caracteres de entrada. A expressão regular
é interpretada conforme mostrado na tabela a seguir.

Elemento Descrição

^ Começar a correspondência no início da cadeia de caracteres de entrada.

[0-9- Corresponder a uma ou mais ocorrências de qualquer caractere de 0 a 9, com


[2468]]+ exceção de 2, 4, 6 e 8. Em outras palavras, corresponder a uma ou mais ocorrências
de zero ou de um dígito ímpar.

$ Finalizar a correspondência no final da cadeia de caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "123", "13579753", "3557798", "335599901" };
string pattern = @"^[0-9-[2468]]+$";

foreach (string input in inputs)


{
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine(match.Value);
}
}
}
// The example displays the following output:
// 13579753
// 335599901

Confira também
GetUnicodeCategory
Linguagem de expressões regulares – referência rápida
Opções de expressões regulares
Âncoras em expressões regulares
Artigo • 10/05/2023

Âncoras ou asserções atômicas de largura zero, especificam uma posição na cadeia de


caracteres em que uma correspondência deve ocorrer. Quando você usa uma âncora na
sua expressão de pesquisa, o mecanismo de expressões regulares não avança pela
cadeia de caracteres ou consome caracteres, ele procura uma correspondência apenas
na posição especificada. Por exemplo, ^ Especifica que a correspondência deve começar
no início de uma linha ou cadeia de caracteres. Portanto, a expressão regular ^http:
corresponde a "http:" apenas quando ele ocorre no início de uma linha. A tabela a
seguir lista as âncoras com suporte pelas expressões regulares no .NET.

Âncora Descrição

^ Por padrão, a correspondência deve ocorrer no início da cadeia de caracteres. No modo


multilinha, deve ocorrer no início da linha. Para saber mais, veja Início da cadeia de
caracteres ou linha.

$ Por padrão, a correspondência deve ocorrer no fim da cadeia de caracteres ou antes de


\n no fim da cadeia de caracteres. No modo multilinha, deve ocorrer no fim da linha ou
antes de \n no fim da linha. Para saber mais, veja Fim da cadeia de caracteres ou linha.

\A A correspondência deve ocorrer no início da cadeia de caracteres apenas (sem suporte


a multilinha). Para saber mais, veja Início da cadeia de caracteres apenas.

\Z A correspondência deve ocorrer no final da cadeia de caracteres ou antes de \n no


final da cadeia de caracteres. Para saber mais, veja Final da Cadeia de Caracteres ou
Antes de Terminar Nova Linha.

\z A correspondência deve ocorrer apenas no final da cadeia de caracteres. Para saber


mais, veja Fim da cadeia de caracteres apenas.

\G A correspondência deve começar na posição em que a correspondência anterior


terminou, ou, caso não haja correspondência anterior, na posição na cadeia de
caracteres em que a correspondência foi iniciada. Para saber mais, veja
Correspondências contíguas.

\b A correspondência deve ocorrer em um limite de palavra. Para saber mais, veja Limite
de palavra.

\B A correspondência não deve ocorrer em um limite de palavra. Para saber mais, veja
Limite não pertencente a palavras.

Início da Cadeia de Caracteres ou Linha: ^


Por padrão, a âncora ^ especifica que o seguinte padrão deve começar na posição do
primeiro caractere da cadeia de caracteres. Se você usar ^ com a opção
RegexOptions.Multiline (veja Opções de expressões regulares), a correspondência
deverá ocorrer no início de cada linha.

O exemplo a seguir usa a âncora ^ em uma expressão regular que extrai informações
sobre os anos durante os quais algumas equipes de profissionais de beisebol existiram.
O exemplo chama duas sobrecargas do método Regex.Matches:

A chamada para a sobrecarga Matches(String, String) localiza apenas a primeira


subcadeia de caracteres na cadeia de caracteres de entrada que corresponde ao
padrão de expressão regular.

A chamada para a sobrecarga do Matches(String, String, RegexOptions) com o


parâmetro options definido como RegexOptions.Multiline localiza todas as cinco
subcadeias de caracteres.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "Brooklyn Dodgers, National League, 1911, 1912, 1932-
1957\n" +
"Chicago Cubs, National League, 1903-present\n" +
"Detroit Tigers, American League, 1901-present\n" +
"New York Giants, National League, 1885-1957\n" +
"Washington Senators, American League, 1901-1960\n";
string pattern = @"^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-
(\d{4}|present))?,?)+";
Match match;

match = Regex.Match(input, pattern);


while (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);

Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();

match = Regex.Match(input, pattern, RegexOptions.Multiline);


while (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);

Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
}
}
// The example displays the following output:
// The Brooklyn Dodgers played in the National League in 1911, 1912,
1932-1957.
//
// The Brooklyn Dodgers played in the National League in 1911, 1912,
1932-1957.
// The Chicago Cubs played in the National League in 1903-present.
// The Detroit Tigers played in the American League in 1901-present.
// The New York Giants played in the National League in 1885-1957.
// The Washington Senators played in the American League in 1901-1960.

O padrão de expressão regular ^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-


(\d{4}|present))?,?)+ é definido conforme mostrado na tabela a seguir.

Padrão Descrição

^ Começa a correspondência no início da cadeia de caracteres de entrada


(ou o início da linha se o método for chamado com a opção
RegexOptions.Multiline).

((\w+(\s?)){2,} Corresponde a um ou mais caracteres de palavra seguidos por zero ou


um espaço, pelo menos, duas vezes. Este é o primeiro grupo de captura.
Essa expressão também define um segundo e um terceiro grupos de
captura: o segundo consiste na palavra capturada e o terceiro consiste
nos espaços em branco capturados.

,\s Corresponde a uma vírgula seguida por um caractere de espaço em


branco.

(\w+\s\w+) Corresponde a um ou mais caracteres de palavra seguidos por um


espaço, seguido por um ou mais caracteres de palavra. Este é o quarto
grupo de captura.

, Corresponde a uma vírgula.

\s\d{4} Corresponde a um espaço seguido por quatro dígitos decimais.


Padrão Descrição

(-(\d{4}|present))? Corresponde a zero ou uma ocorrência de um hífen seguido por quatro


dígitos decimais ou a cadeia de caracteres "present". Este é o sexto
grupo de captura. Ele também inclui um sétimo grupo de captura.

,? Corresponde a zero ou uma ocorrência de uma vírgula.

(\s\d{4}(- Corresponde a uma ou mais ocorrências do seguinte: um espaço, quatro


(\d{4}|present))?,?)+ dígitos decimais, zero ou uma ocorrência de um hífen seguido por
quatro dígitos decimais ou a cadeia de caracteres "present" e zero ou
uma vírgula. Este é o quinto grupo de captura.

Final da Cadeia de Caracteres ou da Linha: $


A âncora $ especifica que o padrão anterior deve ocorrer no final da cadeia de
caracteres de entrada ou antes de \n no final da cadeia de caracteres de entrada.

Se você usar $ com a opção RegexOptions.Multiline, a correspondência também pode


ocorrer no final de uma linha. Observe que $ corresponde a \n , mas não corresponde a
\r\n (a combinação de caracteres de nova linha e de retorno de carro ou CR/LF). De
acordo com a combinação de caracteres CR/LF, inclua \r?$ no padrão de expressão
regular.

O exemplo a seguir adiciona a âncora $ ao padrão de expressão regular usada no


exemplo na seção Início da cadeia de caracteres ou linha. Quando usado com a cadeia
de caracteres de entrada original, que inclui cinco linhas de texto, o método
Regex.Matches(String, String) não pode localizar uma correspondência, pois o final da
primeira linha não corresponde ao padrão $ . Quando a cadeia de caracteres de entrada
original é dividida em uma matriz de cadeia de caracteres, o método
Regex.Matches(String, String) obtém sucesso na correspondência de cada uma das cinco
linhas. Quando o método Regex.Matches(String, String, RegexOptions) for chamado
com o parâmetro options definido como RegexOptions.Multiline, nenhuma
correspondência será encontrada porque o padrão de expressão regular não considera
o elemento de retorno de carro (\u+000D). No entanto, quando o padrão de expressão
regular é modificado substituindo $ por \r?$ , chamar o método Regex.Matches(String,
String, RegexOptions) com o parâmetro options definido como RegexOptions.Multiline
novamente encontra cinco correspondências.

C#

using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string cr = Environment.NewLine;
string input = "Brooklyn Dodgers, National League, 1911, 1912, 1932-
1957" + cr +
"Chicago Cubs, National League, 1903-present" + cr +
"Detroit Tigers, American League, 1901-present" + cr
+
"New York Giants, National League, 1885-1957" + cr +
"Washington Senators, American League, 1901-1960" +
cr;
Match match;

string basePattern = @"^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-


(\d{4}|present))?,?)+";
string pattern = basePattern + "$";
Console.WriteLine("Attempting to match the entire input string:");
match = Regex.Match(input, pattern);
while (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);

Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();

string[] teams = input.Split(new String[] { cr },


StringSplitOptions.RemoveEmptyEntries);
Console.WriteLine("Attempting to match each element in a string
array:");
foreach (string team in teams)
{
match = Regex.Match(team, pattern);
if (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);
Console.WriteLine(".");
}
}
Console.WriteLine();

Console.WriteLine("Attempting to match each line of an input string


with '$':");
match = Regex.Match(input, pattern, RegexOptions.Multiline);
while (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);

Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();

pattern = basePattern + "\r?$";


Console.WriteLine(@"Attempting to match each line of an input string
with '\r?$':");
match = Regex.Match(input, pattern, RegexOptions.Multiline);
while (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);

Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
}
}
// The example displays the following output:
// Attempting to match the entire input string:
//
// Attempting to match each element in a string array:
// The Brooklyn Dodgers played in the National League in 1911, 1912,
1932-1957.
// The Chicago Cubs played in the National League in 1903-present.
// The Detroit Tigers played in the American League in 1901-present.
// The New York Giants played in the National League in 1885-1957.
// The Washington Senators played in the American League in 1901-1960.
//
// Attempting to match each line of an input string with '$':
//
// Attempting to match each line of an input string with '\r?$':
// The Brooklyn Dodgers played in the National League in 1911, 1912,
1932-1957.
// The Chicago Cubs played in the National League in 1903-present.
// The Detroit Tigers played in the American League in 1901-present.
// The New York Giants played in the National League in 1885-1957.
// The Washington Senators played in the American League in 1901-1960.

Apenas Início da Cadeia de Caracteres: \A


A âncora \A especifica que uma correspondência deve ocorrer no início da cadeia de
caracteres de entrada. Ela é idêntica à âncora ^ , exceto que \A ignora a opção
RegexOptions.Multiline. Portanto, ela pode corresponder apenas ao início da primeira
linha em uma cadeia de caracteres de entrada multilinhas.

O exemplo a seguir é semelhante aos exemplos das âncoras ^ e $ . Ele usa a âncora \A
em uma expressão regular que extrai informações sobre os anos durante os quais
algumas equipes de profissionais de beisebol existiram. A cadeia de caracteres de
entrada inclui cinco linhas. A chamada para o método Regex.Matches(String, String,
RegexOptions) localiza apenas a primeira subcadeia de caracteres na cadeia de
caracteres de entrada que corresponde ao padrão de expressão regular. Como o
exemplo mostra, a opção Multiline não tem efeito.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "Brooklyn Dodgers, National League, 1911, 1912, 1932-
1957\n" +
"Chicago Cubs, National League, 1903-present\n" +
"Detroit Tigers, American League, 1901-present\n" +
"New York Giants, National League, 1885-1957\n" +
"Washington Senators, American League, 1901-1960\n";

string pattern = @"\A((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-


(\d{4}|present))?,?)+";

Match match = Regex.Match(input, pattern, RegexOptions.Multiline);


while (match.Success)
{
Console.Write("The {0} played in the {1} in",
match.Groups[1].Value, match.Groups[4].Value);
foreach (Capture capture in match.Groups[5].Captures)
Console.Write(capture.Value);

Console.WriteLine(".");
match = match.NextMatch();
}
Console.WriteLine();
}
}
// The example displays the following output:
// The Brooklyn Dodgers played in the National League in 1911, 1912,
1932-1957.
Final da Cadeia de Caracteres ou Antes de
Terminar Nova Linha: \ Z
A âncora \Z especifica que a correspondência deve ocorrer no final da cadeia de
caracteres de entrada ou antes de \n no final da cadeia de caracteres de entrada. Ela é
idêntica à âncora $ , exceto que \Z ignora a opção RegexOptions.Multiline. Portanto,
em uma cadeia de caracteres multilinhas, ela pode corresponder apenas ao final da
última linha ou à última linha antes de \n .

Observe que \Z corresponde a \n mas não corresponde a \r\n (a combinação de


caracteres CR/LF). Para corresponder a CR/LF, inclua \r?\Z no padrão da expressão
regular.

O exemplo a seguir usa a âncora \Z em uma expressão regular semelhante ao exemplo


na seção Início da Cadeia de Caracteres ou Linha, que extrai informações sobre os anos
durante os quais algumas equipes de profissionais de beisebol existiram. A
subexpressão \r?\Z na expressão regular ^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-
(\d{4}|present))?,?)+\r?\Z corresponde ao final de uma cadeia de caracteres e

também corresponde a uma cadeia de caracteres que termina com \n ou \r\n . Como
resultado, cada elemento da matriz corresponde ao padrão da expressão regular.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "Brooklyn Dodgers, National League, 1911, 1912,
1932-1957",
"Chicago Cubs, National League, 1903-present" +
Environment.NewLine,
"Detroit Tigers, American League, 1901-present" +
Regex.Unescape(@"\n"),
"New York Giants, National League, 1885-1957",
"Washington Senators, American League, 1901-1960"
+ Environment.NewLine};
string pattern = @"^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-
(\d{4}|present))?,?)+\r?\Z";

foreach (string input in inputs)


{
Console.WriteLine(Regex.Escape(input));
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine(" Match succeeded.");
else
Console.WriteLine(" Match failed.");
}
}
}
// The example displays the following output:
// Brooklyn\ Dodgers,\ National\ League,\ 1911,\ 1912,\ 1932-1957
// Match succeeded.
// Chicago\ Cubs,\ National\ League,\ 1903-present\r\n
// Match succeeded.
// Detroit\ Tigers,\ American\ League,\ 1901-present\n
// Match succeeded.
// New\ York\ Giants,\ National\ League,\ 1885-1957
// Match succeeded.
// Washington\ Senators,\ American\ League,\ 1901-1960\r\n
// Match succeeded.

Apenas Final da Cadeia de Caracteres: \ z


A âncora \z especifica que uma correspondência deve ocorrer no final da cadeia de
caracteres de entrada. Como o elemento de linguagem $ , o \z ignora a opção
RegexOptions.Multiline. Diferentemente do elemento de linguagem \Z , \z não
corresponde ao caractere \n no final de uma cadeia de caracteres. Portanto, ele pode
corresponder somente à última linha da cadeia de caracteres de entrada.

O exemplo a seguir usa a âncora \z em uma expressão regular que é de outro modo
idêntica ao exemplo na seção anterior, que extrai informações sobre os anos durante os
quais algumas equipes de profissionais de beisebol existiram. O exemplo tenta
corresponder cada um dos cinco elementos em uma matriz de cadeia de caracteres com
um padrão de expressão regular ^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-
(\d{4}|present))?,?)+\r?\z . Duas das cadeias de caracteres terminam com os

caracteres de alimentação de linha e retorno de carro, uma termina com um caractere


de alimentação de linha e duas terminam sem um caractere de retorno de carro nem
um caractere de alimentação de linha. Como a saída mostra, apenas as cadeias de
caracteres sem um caractere de retorno de carro ou de alimentação de linha
correspondem ao padrão.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "Brooklyn Dodgers, National League, 1911, 1912,
1932-1957",
"Chicago Cubs, National League, 1903-present" +
Environment.NewLine,
"Detroit Tigers, American League, 1901-present\n",
"New York Giants, National League, 1885-1957",
"Washington Senators, American League, 1901-1960"
+ Environment.NewLine };
string pattern = @"^((\w+(\s?)){2,}),\s(\w+\s\w+),(\s\d{4}(-
(\d{4}|present))?,?)+\r?\z";

foreach (string input in inputs)


{
Console.WriteLine(Regex.Escape(input));
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine(" Match succeeded.");
else
Console.WriteLine(" Match failed.");
}
}
}
// The example displays the following output:
// Brooklyn\ Dodgers,\ National\ League,\ 1911,\ 1912,\ 1932-1957
// Match succeeded.
// Chicago\ Cubs,\ National\ League,\ 1903-present\r\n
// Match failed.
// Detroit\ Tigers,\ American\ League,\ 1901-present\n
// Match failed.
// New\ York\ Giants,\ National\ League,\ 1885-1957
// Match succeeded.
// Washington\ Senators,\ American\ League,\ 1901-1960\r\n
// Match failed.

Correspondências Contíguas: \ G
A âncora \G especifica que uma correspondência deve ocorrer no ponto em que a
correspondência anterior terminou, ou, caso não haja correspondência anterior, na
posição na cadeia de caracteres em que a correspondência foi iniciada. Quando você
usa essa âncora com o método Regex.Matches ou Match.NextMatch, ela garante que
todas as correspondências são contíguas.

 Dica

Normalmente, você coloca uma âncora \G na extremidade esquerda do seu


padrão. No caso incomum de você estar executando uma pesquisa da direita para
a esquerda, coloque a âncora \G na extremidade direita do seu padrão.
O exemplo a seguir usa uma expressão regular para extrair os nomes de espécies de
roedores de uma cadeia de caracteres delimitada por vírgulas.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "capybara,squirrel,chipmunk,porcupine,gopher," +
"beaver,groundhog,hamster,guinea pig,gerbil," +
"chinchilla,prairie dog,mouse,rat";
string pattern = @"\G(\w+\s?\w*),?";
Match match = Regex.Match(input, pattern);
while (match.Success)
{
Console.WriteLine(match.Groups[1].Value);
match = match.NextMatch();
}
}
}
// The example displays the following output:
// capybara
// squirrel
// chipmunk
// porcupine
// gopher
// beaver
// groundhog
// hamster
// guinea pig
// gerbil
// chinchilla
// prairie dog
// mouse
// rat

A expressão regular \G(\w+\s?\w*),? é interpretada conforme mostrado na tabela a


seguir.

Padrão Descrição

\G Começa onde a última correspondência terminou.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

\s? Corresponde a zero ou um espaço.


Padrão Descrição

\w* Corresponder a zero ou mais caracteres de palavra.

(\w+\s? Corresponde a um ou mais caracteres de palavra seguidos por zero ou um espaço,


\w*) seguido por zero ou mais caracteres de palavra. Este é o primeiro grupo de captura.

,? Corresponde a zero ou uma ocorrência de um caractere de vírgula literal.

Limite de Palavra: \b
A âncora \b especifica que a correspondência deve ocorrer em um limite entre um
caractere de palavra (o elemento de linguagem \w ) e um caractere não pertencente a
palavras (o elemento de linguagem \W ). Os caracteres de palavra consistem em
caracteres alfanuméricos e sublinhados. Um caractere não pertencente a palavras é
qualquer caractere que não seja alfanumérico ou um sublinhado. (Para obter mais
informações, confira Classes de caracteres.) A correspondência também pode ocorrer
em um limite de palavra no início ou no final da cadeia de caracteres.

A âncora \b frequentemente é usada para garantir que uma subexpressão corresponda


a uma palavra inteira, em vez de apenas ao início ou final de uma palavra. A expressão
regular \bare\w*\b no exemplo a seguir ilustra esse uso. Ela corresponde a qualquer
palavra que comece com a subcadeia de caracteres "are". A saída do exemplo também
ilustra que \b corresponde ao início e ao final da cadeia de caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "area bare arena mare";
string pattern = @"\bare\w*\b";
Console.WriteLine("Words that begin with 'are':");
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index);
}
}
// The example displays the following output:
// Words that begin with 'are':
// 'area' found at position 0
// 'arena' found at position 10
O padrão da expressão regular é interpretado conforme a tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

are Corresponde à subcadeia de caracteres “are”.

\w* Corresponder a zero ou mais caracteres de palavra.

\b Termina a correspondência em um limite de palavra.

Limite de Não Palavra: \B


A âncora \B especifica que a correspondência não deve ocorrer em um limite de
palavra. É o oposto da âncora \b .

O exemplo a seguir usa a âncora \B para localizar ocorrências da subcadeia de


caracteres "qu" em uma palavra. O padrão de expressão regular \Bqu\w+ corresponde a
uma subcadeia de caracteres que começa com um "qu" que não inicia uma palavra e
que continua até o final da palavra.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "equity queen equip acquaint quiet";
string pattern = @"\Bqu\w+";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index);
}
}
// The example displays the following output:
// 'quity' found at position 1
// 'quip' found at position 14
// 'quaint' found at position 21

O padrão da expressão regular é interpretado conforme a tabela a seguir.

Padrão Descrição
Padrão Descrição

\B Não começa a correspondência em um limite de palavra.

qu Corresponde à subcadeia de caracteres “qu”.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

Confira também
Linguagem de expressões regulares – referência rápida
Opções de expressões regulares
Constructos de agrupamento em
expressões regulares
Artigo • 05/06/2023

As construções de agrupamento delineiam as subexpressões de uma expressão regular


e capturam a subcadeia de caracteres de uma cadeia de caracteres de entrada. Você
pode usar construções de agrupamento para fazer isto:

Faz a correspondência de uma subexpressão que é repetida na cadeia de


caracteres de entrada.
Aplicar um quantificador a uma subexpressão com diversos elementos de
linguagem de expressão regular. Para saber mais sobre quantificadores, confira
Quantificadores.
Inclui uma subexpressão na cadeia de caracteres retornada pelos métodos
Regex.Replace e Match.Result.
Recupere subexpressões individuais da propriedade Match.Groups e processe-as
separadamente do texto correspondente como um todo.

A tabela a seguir lista os constructos de agrupamento com suporte do mecanismo de


expressões regulares .NET e indica captura ou não captura.

Constructo de agrupamento Captura ou não captura

Subexpressões correspondentes Capturando

Subexpressões correspondentes nomeadas Capturando

Definições de grupo de balanceamento Capturando

Grupos de não captura Não captura

Opções de grupo Não captura

Asserções lookahead positivas de largura zero Não captura

Asserções lookahead negativas de largura zero Não captura

Asserções lookbehind positivas de largura zero Não captura

Asserções lookbehind negativas de largura zero Não captura

Grupos atômicos Não captura

Para saber mais sobre grupos e modelos de objetos de expressão regular, veja
Constructos de agrupamento e objetos de expressão regular.
Subexpressões correspondentes
O constructo de agrupamento a seguir captura uma subexpressão correspondente:

( subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular válido. As capturas que


usam parênteses são numeradas automaticamente, da esquerda para a direita, com
base na ordem do parêntese de abertura na expressão regular, começando em 1. No
entanto, grupos de captura nomeados sempre são ordenados por último, após grupos
de captura não nomeados. A captura que recebe o número 0 é o texto que coincide
com todo o padrão da expressão regular.

7 Observação

Por padrão, o elemento da linguagem ( subexpression ) captura a subexpressão


correspondente. Porém, se o parâmetro RegexOptions de um padrão de expressão
regular que corresponde ao método inclui o sinalizador
RegexOptions.ExplicitCapture ou se a opção n é aplicada a essa subexpressão
(consulte Opções de grupo mais adiante neste artigo), a subexpressão coincidente
não é capturada.

Você pode acessar os grupos capturados de quatro formas:

Usando o constructo de referência inversa na expressão regular. A subexpressão


correspondente é referenciada na mesma expressão regular usando a sintaxe
\ number, em que number é o número ordinal da subexpressão capturada.

Usando o constructo de referência inversa nomeado na expressão regular. A


subexpressão coincidente é referenciada na mesma expressão regular usando a
sintaxe \k< name > , em que name é o nome de um grupo de captura, ou
\k< number > , em que number é o número ordinal de um grupo de captura. O
grupo de captura tem um nome padrão que é idêntico a seu número ordinal. Para
saber mais, confira Subexpressões coincidentes nomeadas mais adiante neste
tópico.

Ao usar a sequência de substituição $ number em uma chamada de método


Regex.Replace ou Match.Result, em que number é o número ordinal da
subexpressão capturada.

Usando de forma programática o objeto GroupCollection retornado pela


propriedade Match.Groups. O membro na posição zero da coleção representa
toda a correspondência da expressão regular. Cada membro subsequente
representa uma subexpressão correspondente. Para obter mais informações, veja a
seção Construções de agrupamento e objetos de expressão regular.

O exemplo a seguir mostra uma expressão regular que identifica palavras duplicadas no
texto. Os dois grupos de captura padrão da expressão regular representam as duas
instâncias da palavra duplicada. A segunda instância é capturada para relatar a posição
inicial na cadeia de caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(\w+)\s(\1)\W";
string input = "He said that that was the the correct answer.";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("Duplicate '{0}' found at positions {1} and
{2}.",
match.Groups[1].Value, match.Groups[1].Index,
match.Groups[2].Index);
}
}
// The example displays the following output:
// Duplicate 'that' found at positions 8 and 13.
// Duplicate 'the' found at positions 22 and 26.

Este é o padrão da expressão regular:

(\w+)\s(\1)\W

A tabela a seguir mostra como o padrão da expressão regular é interpretado.

Padrão Descrição

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Este é o primeiro grupo de


captura.

\s Corresponde a um caractere de espaço em branco.

(\1) Corresponde à cadeia de caracteres no primeiro grupo capturado. Este é o segundo


grupo de captura. O exemplo o atribui a um grupo capturado para que seja possível
recuperar a posição inicial da palavra duplicada da propriedade Match.Index .
Padrão Descrição

\W Estabeleça a correspondência com caracteres que não compõem palavras, como


espaços em branco e pontuação. Isso impede a correspondência de um padrão de
expressão regular com uma expressão que começa com a palavra do primeiro grupo
capturado.

Subexpressões correspondentes nomeadas


O constructo de agrupamento a seguir captura uma subexpressão correspondente e
permite acessá-la usando seu nome ou número:

(?<name>subexpression)

ou:

(?'name'subexpression)

Aqui, nome é um nome de grupo válido e subexpressão é qualquer padrão de expressão


regular válido. name não deve conter caracteres de pontuação nem começar com um
número.

7 Observação

Se o parâmetro RegexOptions de um padrão de expressão regular que


corresponde ao método inclui o sinalizador RegexOptions.ExplicitCapture ou se a
opção n é aplicada a essa subexpressão (consulte Opções de grupo mais adiante
neste tópico), a única forma de capturar uma subexpressão é atribuir nomes
explícitos a grupos de captura.

Você pode acessar grupos capturados nomeados destas formas:

Usando o constructo de referência inversa nomeado na expressão regular. A


subexpressão coincidente é referenciada na mesma expressão regular usando a
sintaxe \k< name > , em que name é o nome da subexpressão capturada.

Usando o constructo de referência inversa na expressão regular. A subexpressão


correspondente é referenciada na mesma expressão regular usando a sintaxe
\ number, em que number é o número ordinal da subexpressão capturada. As

subexpressões correspondentes nomeadas são numeradas em ordem de


sequência, da esquerda da direita, após as subexpressões correspondentes.
Ao usar a sequência de substituição ${ name } em uma chamada de método
Regex.Replace ou Match.Result, em que name é o nome da subexpressão
capturada.

Ao usar a sequência de substituição $ number em uma chamada de método


Regex.Replace ou Match.Result, em que number é o número ordinal da
subexpressão capturada.

Usando de forma programática o objeto GroupCollection retornado pela


propriedade Match.Groups. O membro na posição zero da coleção representa
toda a correspondência da expressão regular. Cada membro subsequente
representa uma subexpressão correspondente. Os grupos capturados nomeados
são armazenados na coleção depois dos grupos capturados numerados.

De forma programática, ao fornecer o nome da subexpressão ao indexador do


objeto GroupCollection (no C#) ou a sua propriedade Item[] (no Visual Basic).

Um padrão de expressão regular simples mostra como os grupos numerados (sem


nome) e nomeados podem ser usados como referência de forma programática ou
usando sintaxe de linguagem de expressão regular. A expressão regular ((?
<One>abc)\d+)?(?<Two>xyz)(.*) gera os seguintes grupos de captura por número e
nome. O primeiro grupo de captura (o número 0) sempre faz referência a todo o
padrão. (Os grupos nomeados sempre são ordenados por último.)

Número Nome Padrão

0 0 (nome padrão) ((?<One>abc)\d+)?(?<Two>xyz)(.*)

1 1 (nome padrão) ((?<One>abc)\d+)

2 2 (nome padrão) (.*)

3 Um (?<One>abc)

4 Dois (?<Two>xyz)

O exemplo a seguir mostra uma expressão regular que identifica palavras duplicadas e a
palavra que aparece logo na sequência. O padrão da expressão regular define duas
subexpressões nomeadas: duplicateWord , que representa a palavra duplicada e
nextWord , que representa a palavra que aparece logo na sequência.

C#

using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"(?<duplicateWord>\w+)\s\k<duplicateWord>\W(?
<nextWord>\w+)";
string input = "He said that that was the the correct answer.";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("A duplicate '{0}' at position {1} is followed by
'{2}'.",
match.Groups["duplicateWord"].Value,
match.Groups["duplicateWord"].Index,
match.Groups["nextWord"].Value);
}
}
// The example displays the following output:
// A duplicate 'that' at position 8 is followed by 'was'.
// A duplicate 'the' at position 22 is followed by 'correct'.

Este é o padrão da expressão regular:

(?<duplicateWord>\w+)\s\k<duplicateWord>\W(?<nextWord>\w+)

A tabela a seguir mostra como a expressão regular é interpretada.

Padrão Descrição

(? Fazer a correspondência a um ou mais caracteres de palavra. Atribua um


<duplicateWord>\w+) nome ao grupo de captura duplicateWord .

\s Corresponde a um caractere de espaço em branco.

\k<duplicateWord> Estabeleça a correspondência com o grupo de captura chamado


duplicateWord .

\W Estabeleça a correspondência com caracteres que não compõem palavras,


como espaços em branco e pontuação. Isso impede a correspondência de
um padrão de expressão regular com uma expressão que começa com a
palavra do primeiro grupo capturado.

(?<nextWord>\w+) Fazer a correspondência a um ou mais caracteres de palavra. Atribua um


nome ao grupo de captura nextWord .

O nome de um grupo pode ser repetido em uma expressão regular. Por exemplo, é
possível que mais de um grupo seja nomeado como digit , como mostra o exemplo a
seguir. No caso de nomes duplicados, o valor do objeto Group é determinado pela
última captura bem-sucedida na cadeia de caracteres de entrada. Além disso, a
CaptureCollection é preenchida com informações sobre cada captura exatamente como
seria se o nome do grupo não fosse duplicado.

No exemplo a seguir, a expressão regular \D+(?<digit>\d+)\D+(?<digit>\d+)? inclui


duas ocorrências de um grupo chamado digit . O primeiro grupo chamado digit
captura um ou mais caracteres de dígito. O primeiro grupo chamado digit captura
zero ou uma ocorrência de um ou mais caracteres de dígito. Como a saída do exemplo
apresentado mostra, se o segundo grupo de captura corresponder ao texto com êxito, o
valor desse texto definirá o valor do objeto Group. Se o segundo grupo de captura não
corresponder à cadeia de caracteres de entrada, o valor da última correspondência
bem-sucedida definirá o valor do objeto Group.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
String pattern = @"\D+(?<digit>\d+)\D+(?<digit>\d+)?";
String[] inputs = { "abc123def456", "abc123def" };
foreach (var input in inputs) {
Match m = Regex.Match(input, pattern);
if (m.Success) {
Console.WriteLine("Match: {0}", m.Value);
for (int grpCtr = 1; grpCtr < m.Groups.Count; grpCtr++) {
Group grp = m.Groups[grpCtr];
Console.WriteLine("Group {0}: {1}", grpCtr, grp.Value);
for (int capCtr = 0; capCtr < grp.Captures.Count; capCtr++)
Console.WriteLine(" Capture {0}: {1}", capCtr,
grp.Captures[capCtr].Value);
}
}
else {
Console.WriteLine("The match failed.");
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: abc123def456
// Group 1: 456
// Capture 0: 123
// Capture 1: 456
//
// Match: abc123def
// Group 1: 123
// Capture 0: 123
A tabela a seguir mostra como a expressão regular é interpretada.

Padrão Descrição

\D+ Corresponder a um ou mais caracteres de dígito não decimal.

(? Corresponder a um ou mais caracteres de dígito decimal. Atribuir a


<digit>\d+) correspondência ao grupo chamado digit .

\D+ Corresponder a um ou mais caracteres de dígito não decimal.

(? Faz a correspondência de zero ou uma ocorrência de um período seguido por um


<digit>\d+)? ou mais caracteres de dígito decimal. Atribuir a correspondência ao grupo
chamado digit .

Definições de grupo de balanceamento


Uma definição de grupo de balanceamento exclui a definição de um grupo definido
anteriormente e armazena, no grupo atual, o intervalo entre o grupo definido
anteriormente e o atual. Esse constructo de agrupamento tem o seguinte formato:

(?<name1-name2>subexpression)

ou:

(?'name1-name2' subexpression)

Aqui, name1 é o grupo atual (opcional), name2 é um grupo definido anteriormente e


subexpressão é qualquer padrão de expressão regular válido. A definição de grupo de
balanceamento exclui a definição de name2 e armazena o intervalo entre name2 e
name1 em name1. Se nenhum grupo name2 for definido, a correspondência retrocede.
Como a exclusão da última definição de name2 revela a definição anterior de name2,
essa construção permite que você use a pilha de capturas para o grupo name2 como
um contador, a fim de registrar construções aninhadas, como parênteses e colchetes.

A definição de grupo de balanceamento usa name2 como pilha. O caractere inicial de


cada construção aninhada é colocado no grupo e em sua coleção Group.Captures.
Quando o caractere de fechamento passa pela correspondência, seu caractere de
abertura é removido do grupo e a coleção Captures diminui em um. Depois da
correspondência dos caracteres de abertura e fechamento de todas as construções
aninhadas, name2 fica vazio.

7 Observação
Depois de modificar a expressão regular do exemplo a seguir para usar o caractere
de abertura e fechamento adequado de um constructo aninhado, você pode usá-lo
para gerenciar a maioria dos constructo aninhados, como expressões matemáticas
ou linhas do código do programa que incluem diversas chamadas de método
aninhado.

O exemplo a seguir usa uma definição de grupo de balanceamento para estabelecer a


correspondência entre os sinais de menor e maior (<>) em uma cadeia de caracteres de
entrada. O exemplo define dois grupos nomeados, Open e Close , que são usados como
pilha para acompanhar a correspondência entre esses sinais. Cada sinal de menor
capturado é enviado para a coleção da captura do grupo Open e cada sinal de maior
capturado é enviado para a coleção de captura do grupo Close . A definição do grupo
de balanceamento assegura que há um sinal de maior para cada sinal de menor. Caso
não haja, o subpadrão final (?(Open)(?!)) , só será avaliado se o grupo Open não estiver
vazio, ou seja, se todas as construções aninhadas não tiverem sido fechadas. Se o
subpadrão final for avaliado, a correspondência falha, porque o subpadrão (?!) é uma
asserção lookahead negativa de largura zero que sempre falha.

C#

using System;
using System.Text.RegularExpressions;

class Example
{
public static void Main()
{
string pattern = "^[^<>]*" +
"(" +
"((?'Open'<)[^<>]*)+" +
"((?'Close-Open'>)[^<>]*)+" +
")*" +
"(?(Open)(?!))$";
string input = "<abc><mno<xyz>>";

Match m = Regex.Match(input, pattern);


if (m.Success == true)
{
Console.WriteLine("Input: \"{0}\" \nMatch: \"{1}\"", input, m);
int grpCtr = 0;
foreach (Group grp in m.Groups)
{
Console.WriteLine(" Group {0}: {1}", grpCtr, grp.Value);
grpCtr++;
int capCtr = 0;
foreach (Capture cap in grp.Captures)
{
Console.WriteLine(" Capture {0}: {1}", capCtr,
cap.Value);
capCtr++;
}
}
}
else
{
Console.WriteLine("Match failed.");
}
}
}
// The example displays the following output:
// Input: "<abc><mno<xyz>>"
// Match: "<abc><mno<xyz>>"
// Group 0: <abc><mno<xyz>>
// Capture 0: <abc><mno<xyz>>
// Group 1: <mno<xyz>>
// Capture 0: <abc>
// Capture 1: <mno<xyz>>
// Group 2: <xyz
// Capture 0: <abc
// Capture 1: <mno
// Capture 2: <xyz
// Group 3: >
// Capture 0: >
// Capture 1: >
// Capture 2: >
// Group 4:
// Group 5: mno<xyz>
// Capture 0: abc
// Capture 1: xyz
// Capture 2: mno<xyz>

O padrão da expressão regular é:

^[^<>]*(((?'Open'<)[^<>]*)+((?'Close-Open'>)[^<>]*)+)*(?(Open)(?!))$

A expressão regular é interpretada da seguinte forma:

Padrão Descrição

^ Começa no início da cadeia de caracteres.

[^<>]* Corresponde a zero ou mais caracteres que não são sinais de menor nem maior.

(?'Open'<) Corresponde a um sinal de menor e o atribui a um grupo nomeado Open .

[^<>]* Corresponde a zero ou mais caracteres que não são sinais de menor nem maior.
Padrão Descrição

((?'Open'<) Corresponde uma ou mais ocorrências de um sinal de menor, seguida por zero ou
[^<>]*)+ mais caracteres que não são sinais de menor ou maior. Este é o segundo grupo de
captura.

(?'Close- Corresponde a um sinal de maior, atribui a subcadeia de caracteres entre o grupo


Open'>) Open e o grupo atual ao grupo Close e exclui a definição do grupo Open .

[^<>]* Corresponde a zero ou mais ocorrências de caracteres que não são sinais de
menor ou maior.

((?'Close- Corresponde a uma ou mais ocorrências de sinal de menor seguidas por zero ou
Open'>) mais ocorrências de qualquer caractere que não seja um sinal de menor ou maior.
[^<>]*)+ Ao estabelecer a correspondência de um sinal de maior, atribua a subcadeia de
caracteres entre o grupo Open e o grupo atual ao grupo Close e exclua a
definição do grupo Open . Este é o terceiro grupo de captura.

(((?'Open'<) Corresponde a zero ou mais ocorrências do seguinte padrão: uma ou mais


[^<>]*)+ ocorrências de sinal de menor seguidas por zero ou mais ocorrências de qualquer
((?'Close- caractere que não seja um sinal de menor ou maior, seguida por uma ou mais
Open'>) ocorrências de sinal de maior, seguida por zero ou mais ocorrências de caracteres
[^<>]*)+)* que não sejam sinais de menor ou maior. Ao estabelecer a correspondência de
um sinal de maior, exclua a definição do grupo Open e atribua a subcadeia entre o
grupo Open e o grupo atual ao grupo Close . Este é o primeiro grupo de captura.

(?(Open) Se o grupo Open existir, abandone a correspondência se for possível estabelecer a


(?!)) correspondência de uma cadeia de caracteres vazia, mas não avance a posição do
mecanismo de expressão regular na cadeia de caracteres. Trata-se de uma
asserção lookahead negativa de largura zero. Como sempre há cadeias de
caracteres vazias presentes em cadeias de caracteres de entrada, essa
correspondência sempre falha. A falha nessa correspondência indica que os sinais
de menor e maior não estão balanceados.

$ Corresponder ao final da cadeia de caracteres de entrada.

A subexpressão final, (?(Open)(?!)) , indica se a construção de aninhamento da cadeia


de caracteres de entrada está balanceada corretamente (por exemplo, se cada sinal de
maior corresponde a um sinal de menor). Ela usa a correspondência condicional com
base em um grupo capturado válido; para obter mais informações, veja Construções de
alternância. Se o grupo Open for definido, o mecanismo de expressão regular tenta
estabelecer a correspondência da subexpressão (?!) na cadeia de caracteres de saída.
O grupo Open só deve ser definido se as construções de aninhamento não estiverem
balanceadas. Portanto, o padrão para correspondência na cadeia de caracteres de
entrada sempre deve fazer com que a correspondência falhe. Nesse caso, (?!) é uma
asserção lookahead negativa de largura zero que sempre falha, porque sempre há
cadeias de caracteres vazias presentes na próxima posição da cadeia de caracteres de
entrada.

No exemplo, o mecanismo da expressão regular avalia a cadeia de caracteres de


entrada "<abc><mno<xyz>>" como mostra a tabela a seguir.

Etapa Padrão Result

1 ^ Começa a correspondência no início da cadeia de caracteres de entrada

2 [^<>]* Procura caracteres que não sejam sinais de menor e maior antes do sinal
de menor e não encontra correspondências.

3 (((?'Open'<) Corresponde ao sinal de menor em "<abc>" e o atribui ao grupo Open .

4 [^<>]* Corresponde a "abc".

5 )+ "<abc" é o valor do segundo grupo capturado.

O próximo caractere na cadeia de caracteres de entrada não é um sinal


de menor. Por isso, o mecanismo de expressão regular não volta para o
subpadrão (?'Open'<)[^<>]*) .

6 ((?'Close- Corresponde ao sinal de maior de "<abc>", atribui "abc", que é a


Open'>) substring entre o grupo Open e o sinal de menor, para o grupo Close e
exclui o valor atual ("<") do grupo Open , deixando-o vazio.

7 [^<>]* Procura caracteres que não sejam sinais de menor e maior depois do
sinal de maior e não encontra correspondências.

8 )+ O valor do terceiro grupo capturado é ">".

O próximo caractere na cadeia de caracteres de entrada não é um sinal


de maior. Por isso, o mecanismo de expressão regular não volta para o
subpadrão ((?'Close-Open'>)[^<>]*) .

9 )* O valor do primeiro grupo capturado é "<abc>".

O próximo caractere na cadeia de caracteres de entrada é um sinal de


menor. Por isso, o mecanismo de expressão regular volta para o
subpadrão (((?'Open'<) .

10 (((?'Open'<) Corresponde ao sinal de menor em "<mno" e o atribui ao grupo Open .


Agora, sua coleção Group.Captures tem um só valor, "<".

11 [^<>]* Corresponde a "mno".


Etapa Padrão Result

12 )+ "<mno" é o valor do segundo grupo capturado.

O próximo caractere na cadeia de caracteres de entrada é um sinal de


menor. Por isso, o mecanismo de expressão regular volta para o
subpadrão (?'Open'<)[^<>]*) .

13 (((?'Open'<) Corresponde ao sinal de menor em "<xyz>" e o atribui ao grupo Open .


Agora, a coleção Group.Captures do grupo Open inclui duas capturas: o
sinal de menor de "<mno" e o sinal de menor de "<xyz>".

14 [^<>]* Corresponde a "xyz".

15 )+ "<xyz" é o valor do segundo grupo capturado.

O próximo caractere na cadeia de caracteres de entrada não é um sinal


de menor. Por isso, o mecanismo de expressão regular não volta para o
subpadrão (?'Open'<)[^<>]*) .

16 ((?'Close- Corresponde ao sinal de maior em "<xyz>". "xyz" atribui a subcadeia de


Open'>) caracteres entre o grupo Open e o sinal de maior ao grupo Close e exclui
o valor atual do grupo Open . O valor da captura anterior (o sinal de
menor em "<mno") torna-se o valor atual do grupo Open . Agora, a
coleção Captures do grupo Open inclui uma só captura: o sinal de menor
de "<xyz>".

17 [^<>]* Procura caracteres que não sejam sinais de menor e maior e não
encontra correspondências.

18 )+ O valor do terceiro grupo capturado é ">".

O próximo caractere na cadeia de caracteres de entrada é um sinal de


maior. Por isso, o mecanismo de expressão regular volta para o
subpadrão ((?'Close-Open'>)[^<>]*) .

19 ((?'Close- Corresponde ao sinal de maior final de "xyz>>", atribui "mno<xyz>" (a


Open'>) substring entre o grupo Open e o sinal de maior) ao grupo Close e exclui
o valor atual do grupo Open . Agora, o grupo Open está vazio.

20 [^<>]* Procura caracteres que não sejam sinais de menor e maior e não
encontra correspondências.

21 )+ O valor do terceiro grupo capturado é ">".

O próximo caractere na cadeia de caracteres de entrada não é um sinal


de maior. Por isso, o mecanismo de expressão regular não volta para o
subpadrão ((?'Close-Open'>)[^<>]*) .
Etapa Padrão Result

22 )* O valor do primeiro grupo capturado é "<mno<xyz>>".

O próximo caractere na cadeia de caracteres de entrada não é um sinal


de menor. Por isso, o mecanismo de expressão regular não volta para o
subpadrão (((?'Open'<) .

23 (?(Open) O grupo Open não é definido. Por isso, não há tentativa de


(?!)) correspondência.

24 $ Corresponde ao final da cadeia de caracteres de entrada.

Grupos de não captura


O constructo de grupo a seguir não captura a substring correspondente a uma
subexpressão:

(?:subexpression)

Aqui, subexpressão é qualquer padrão de expressão regular válido. O constructo de


grupo de não captura geralmente é usada quando um quantificador é aplicado a um
grupo, mas as subcadeias de caracteres capturadas pelo grupo não são úteis.

7 Observação

Se uma expressão regular inclui constructos aninhados de agrupamento, um


constructo de grupo de não captura externo não se aplica às construções
aninhadas internas de grupo.

O exemplo a seguir mostra uma expressão regular que inclui grupos de não captura. O
resultado não inclui grupos capturados.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(?:\b(?:\w+)\W*)+\.";
string input = "This is a short sentence.";
Match match = Regex.Match(input, pattern);
Console.WriteLine("Match: {0}", match.Value);
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
Console.WriteLine(" Group {0}: {1}", ctr,
match.Groups[ctr].Value);
}
}
// The example displays the following output:
// Match: This is a short sentence.

A expressão regular (?:\b(?:\w+)\W*)+\. corresponde a uma sentença terminada por


um ponto final. Como a expressão regular concentra-se em sentenças, e não em
palavras, as construções de agrupamento são usadas apenas como quantificadores. O
padrão da expressão regular é interpretado conforme a tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(?:\w+) Fazer a correspondência a um ou mais caracteres de palavra. Não atribui o


texto coincidente a um grupo capturado.

\W* Corresponde a zero ou mais caracteres que não compõem palavras.

(?:\b(?:\w+)\W*)+ Corresponde ao padrão de um ou mais caracteres de palavra que começam


com um limite de palavra, seguido por zero ou um espaço em branco uma
ou mais vezes. Não atribui o texto coincidente a um grupo capturado.

\. Corresponde a um ponto final.

Opções de grupo
O constructo de agrupamento a seguir aplica ou desabilita as opções especificadas em
uma subexpressão:

(?imnsx-imnsx: subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular válido. Por exemplo, (?i-
s:) ativa a diferenciação de maiúsculas e minúsculas e desabilita o modo de linha única.

Para saber mais sobre as opções embutidas que você pode especificar, veja Opções de
expressões regulares.

7 Observação

Você pode especificar opções que se aplicam a toda uma expressão regular, em vez
de a uma subexpressão, usando um construtor de classe
System.Text.RegularExpressions.Regex ou método estático. Você também pode
especificar opções embutidas que são aplicadas após um ponto específico de uma
expressão regular, usando o constructo de linguagem (?imnsx-imnsx) .

O constructo de opções de grupo não é um grupo de captura. Ou seja, embora todas as


porções de uma cadeia de caracteres que são capturadas pela subexpressão sejam
incluídas na correspondência, elas não são incluídas em grupos capturados, nem usadas
para preencher o objeto GroupCollection.

Por exemplo, a expressão regular \b(?ix: d \w+)\s no exemplo a seguir usa opções
embutidas em um constructo de agrupamento para habilitar a correspondência sem
diferenciação de maiúsculas e minúsculas e ignorar os espaços em branco do padrão,
ao identificar todas as palavras que começam com a letra "d". A expressão regular é
definida como mostrado na tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(?ix: Usa a correspondência sem diferenciar letras maiúsculas e minúsculas e ignora espaços
d \w+) em branco nesse padrão e corresponde um "d" seguido por um ou mais caracteres que
compõem palavras.

\s Corresponde a um caractere de espaço em branco.

C#

string pattern = @"\b(?ix: d \w+)\s";


string input = "Dogs are decidedly good pets.";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine("'{0}// found at index {1}.", match.Value,
match.Index);
// The example displays the following output:
// 'Dogs // found at index 0.
// 'decidedly // found at index 9.

Asserções lookahead positivas de largura zero


O constructo de agrupamento a seguir define uma asserção lookahead positiva de
largura zero:

(?= subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular. Para que a correspondência


seja executada com êxito, a cadeia de caracteres de entrada deve corresponder ao
padrão de expressão regular na subexpression, embora a subcadeia de caracteres com a
qual a correspondência foi estabelecida não conste no resultado. A asserção lookahead
positiva de largura zero não retrocede.

Geralmente, as asserções desse tipo podem ser encontradas no final de um padrão de


expressão regular. Isso define uma subcadeia de caracteres que deve estar presente no
final da cadeia de caracteres para que seja possível estabelecer a correspondência, mas
que não seja incluída na correspondência. Isso também é útil para evitar retrocessos em
excesso. Você pode usar asserções lookahead positivas de largura zero que garantam
que um grupo capturado específico seja iniciado por um texto que corresponda a um
subconjunto do padrão definido para o grupo capturado. Por exemplo, se um grupo de
captura corresponder a caracteres de palavras em sequência, você pode usar uma
asserção desse tipo para que o primeiro caractere seja uma caractere alfabético
maiúsculo.

O exemplo a seguir usa asserção lookahead positiva de largura zero para estabelecer a
correspondência da palavra que precede o verbo "is" na cadeia de caracteres de
entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b\w+(?=\sis\b)";
string[] inputs = { "The dog is a Malamute.",
"The island has beautiful birds.",
"The pitch missed home plate.",
"Sunday is a weekend day." };

foreach (string input in inputs)


{
Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine("'{0}' precedes 'is'.", match.Value);
else
Console.WriteLine("'{0}' does not match the pattern.", input);
}
}
}
// The example displays the following output:
// 'dog' precedes 'is'.
// 'The island has beautiful birds.' does not match the pattern.
// 'The pitch missed home plate.' does not match the pattern.
// 'Sunday' precedes 'is'.
A expressão regular \b\w+(?=\sis\b) é interpretada conforme mostrado na tabela a
seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

(? Determina se os caracteres que compõem palavras são seguidos por um caractere de


=\sis\b) espaço em branco e pela cadeia de caracteres "is", que termina com um limite de
palavra. Nesse caso, a correspondência ocorreu com êxito.

Asserções lookahead negativas de largura zero


O constructo de agrupamento a seguir define uma asserção lookahead negativa de
largura zero:

(?! subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular. Para que a correspondência


seja executada com êxito, a cadeia de caracteres de entrada não deve corresponder ao
padrão de expressão regular na subexpression, embora a cadeia de caracteres com a
qual a correspondência foi estabelecida não conste no resultado.

Geralmente, as asserções desse tipo podem ser encontradas no início ou no final de


uma expressão regular. No início da expressão regular, elas podem definir um padrão
específico que não deve ser correspondido quando o início da expressão regular definir
um padrão parecido, mas mais geral, para a correspondência. Nesse caso, ela
geralmente é usada para limitar o retrocesso. No final de uma expressão regular, pode
definir uma subexpressão que pode não ocorrer no final de uma correspondência.

O exemplo a seguir define uma expressão regular que usa uma asserção lookahead de
largura zero no início da expressão regular para fazer a correspondência de palavras que
não começam com "un".

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(?!un)\w+\b";
string input = "unite one unethical ethics use untie ultimate";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// one
// ethics
// use
// ultimate

A expressão regular \b(?!un)\w+\b é interpretada conforme mostrado na tabela a


seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(?!un) Determina se os dois caracteres seguintes são "un". Caso não sejam, é possível
estabelecer a correspondência.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

\b Termina a correspondência em um limite de palavra.

O exemplo a seguir define uma expressão regular que usa uma asserção lookahead de
largura zero no final da expressão regular para fazer a correspondência de palavras que
não terminam com um caractere de pontuação.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b\w+\b(?!\p{P})";
string input = "Disconnected, disjointed thoughts in a sentence
fragment.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// disjointed
// thoughts
// in
// a
// sentence

A expressão regular \b\w+\b(?!\p{P}) é interpretada conforme mostrado na tabela a


seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

\b Termina a correspondência em um limite de palavra.

\p{P}) Se o caractere seguinte não for um símbolo de pontuação (por exemplo, um ponto final
ou uma vírgula), a correspondência é estabelecida.

Asserções lookbehind positivas de largura zero


O constructo de agrupamento a seguir define uma asserção lookbehind positiva de
largura zero:

(?<= subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular. Para que a correspondência


seja executada com êxito, a subexpressão deve ocorrer na cadeia de caracteres de
entrada à esquerda da posição atual, embora subexpression não conste no resultado. A
asserção lookbehind positiva de largura zero não retrocede.

As asserções lookbehind positivas de largura zero geralmente são usadas no início de


expressões regulares. O padrão que elas definem é uma pré-condição para a
correspondência, embora não constem no resultado.

Por exemplo, o exemplo a seguir estabelece a correspondência para os dois últimos


dígitos de ano no século XXI (ou seja, ele requer que os dígitos "20" apareçam antes da
cadeia de caracteres coincidente).

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "2010 1999 1861 2140 2009";
string pattern = @"(?<=\b20)\d{2}\b";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// 10
// 09

O padrão da expressão regular (?<=\b20)\d{2}\b é interpretado conforme mostrado na


tabela a seguir.

Padrão Descrição

\d{2} Corresponde a dois dígitos decimais.

(? Continua a estabelecer a correspondência se os dois dígitos decimais forem precedidos


<=\b20) por dígitos decimais "20" em um limite de palavra.

\b Termina a correspondência em um limite de palavra.

As asserções lookbehind positivas de largura zero também são usadas para limitar o
retrocesso quando o último caractere de um grupo capturado precisar ser um
subconjunto de caracteres que corresponde ao padrão de expressão regular desse
grupo. Por exemplo, se um grupo captura todos os caracteres de palavras em sequência,
você pode usar uma asserção lookbehind positivas de largura zero tipo para que o
primeiro caractere seja uma letra.

Asserções lookbehind negativas de largura zero


O constructo de agrupamento a seguir define uma asserção lookbehind negativa de
largura zero:

(?<! subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular. Para que a correspondência


seja executada com êxito, a subexpressão não deve ocorrer na cadeia de caracteres de
entrada à esquerda da posição atual. No entanto, todas as subcadeias de caracteres que
não corresponderem a subexpression , não serão incluídas no resultado.

As asserções lookbehind negativas de largura zero geralmente são usadas no início de


expressões regulares. O padrão definido por elas elimina uma correspondência na
cadeia de caracteres seguinte. Elas também são usadas para limitar o retrocesso quando
o último caractere de um grupo capturado não precisar ser um ou mais caracteres que
corresponde ao padrão de expressão regular desse grupo. Por exemplo, se um grupo
captura todos os caracteres de palavras em sequência, você pode usar uma asserção
lookbehind positiva de largura zero para que o primeiro caractere não seja um
sublinhado (_).

O exemplo a seguir estabelece a correspondência de data para qualquer dia da semana


que não seja sábado nem domingo.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] dates = { "Monday February 1, 2010",
"Wednesday February 3, 2010",
"Saturday February 6, 2010",
"Sunday February 7, 2010",
"Monday, February 8, 2010" };
string pattern = @"(?<!(Saturday|Sunday) )\b\w+ \d{1,2}, \d{4}\b";

foreach (string dateValue in dates)


{
Match match = Regex.Match(dateValue, pattern);
if (match.Success)
Console.WriteLine(match.Value);
}
}
}
// The example displays the following output:
// February 1, 2010
// February 3, 2010
// February 8, 2010

O padrão da expressão regular (?<!(Saturday|Sunday) )\b\w+ \d{1,2}, \d{4}\b é


interpretado conforme mostrado na tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\w+ Corresponde a um ou mais caracteres de palavra seguido por um espaço em


branco.
Padrão Descrição

\d{1,2}, Corresponde a um ou dois dígitos decimais seguidos por uma caractere de


espaço em branco e uma vírgula.

\d{4}\b Corresponde a quatro dígitos decimais e encerra a correspondência em um


limite de palavra.

(?<! Se a correspondência for precedida por algo que não seja as cadeias de
(Saturday|Sunday) caracteres "Sábado" ou "Domingo" seguidas por um espaço, a
) correspondência é realizada com êxito.

Grupos atômicos
O seguinte constructo de agrupamento representa um grupo atômico (conhecido em
alguns outros mecanismos de expressão regulares como uma subexpressão sem
retrocesso, uma subexpressão atômica ou uma subexpressão somente uma vez):

(?> subexpression )

Aqui, subexpressão é qualquer padrão de expressão regular.

Geralmente, se uma expressão regular incluir um padrão de correspondência opcional


ou alternativo e a correspondência não for realizada com êxito, o mecanismo da
expressão regular pode ramificar em diversas orientações para estabelecer a
correspondência entre uma cadeia de caracteres de entrada e um padrão. Se a
correspondência não for encontrada na primeira ramificação, o mecanismo da
expressão regular pode voltar ou retroceder ao ponto da primeira correspondência e
tentar estabelecer a correspondência usando a segunda ramificação. Esse processo
pode continuar até que as tentativas se esgotem.

O constructo de linguagem da (?> subexpression ) desabilita o retrocesso. O mecanismo


de expressão regular estabelece a correspondência entre o máximo de caracteres da
cadeia de caracteres de entrada. Quando não for possível estabelecer outras
correspondências, ele não retrocede para tentar alternativas. Ou seja, a subexpressão
corresponde somente às cadeias de caracteres que poderiam corresponder somente à
subexpressão. Ela não tenta fazer a correspondência de uma cadeia de caracteres com
base na subexpressão em questão e nas subexpressões seguintes.

Essa opção é recomendada quando você sabe que o retrocesso não terá êxito. Evitar
que o mecanismo de expressão regular faça pesquisas desnecessárias, melhora o
desempenho.
O exemplo a seguir mostra como um grupo atômico modifica os resultados de uma
correspondência padrão. A expressão regular de retrocesso estabelece a
correspondência de diversos caracteres repetidos, seguidos por outra ocorrência do
mesmo caractere de um limite de palavra, mas a expressão regular sem retrocesso não
faz isso.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "cccd.", "aaad", "aaaa" };
string back = @"(\w)\1+.\b";
string noback = @"(?>(\w)\1+).\b";

foreach (string input in inputs)


{
Match match1 = Regex.Match(input, back);
Match match2 = Regex.Match(input, noback);
Console.WriteLine("{0}: ", input);

Console.Write(" Backtracking : ");


if (match1.Success)
Console.WriteLine(match1.Value);
else
Console.WriteLine("No match");

Console.Write(" Nonbacktracking: ");


if (match2.Success)
Console.WriteLine(match2.Value);
else
Console.WriteLine("No match");
}
}
}
// The example displays the following output:
// cccd.:
// Backtracking : cccd
// Nonbacktracking: cccd
// aaad:
// Backtracking : aaad
// Nonbacktracking: aaad
// aaaa:
// Backtracking : aaaa
// Nonbacktracking: No match
A expressão regular sem retrocesso (?>(\w)\1+).\b é definida como mostra a tabela a
seguir.

Padrão Descrição

(\w) Corresponde a um único caractere que compõe palavras e o atribui ao primeiro grupo
de captura.

\1+ Corresponde ao valor da primeira subcadeia de caracteres de captura uma ou mais


vezes.

. Corresponde a qualquer caractere.

\b Termina a correspondência em um limite de palavra.

(?> Corresponde a uma ou mais ocorrências de um caractere de palavra duplicado, mas


(\w)\1+) não executa o retrocesso para estabelecer a correspondência com o último caractere
em um limite de palavra.

Agrupando construções e objetos de expressão


regulares
As subcadeias de caracteres que correspondem a um grupo de captura de expressão
regular são representadas por objetos System.Text.RegularExpressions.Group, que
podem ser recuperados do objeto System.Text.RegularExpressions.GroupCollection que
é retornado pela propriedade Match.Groups. O objeto GroupCollection é preenchido
desta forma:

O primeiro objeto Group da coleção (o objeto de índice zero) representa a


correspondência total.
O conjunto seguinte de objetos Group representa os grupos de captura não
nomeados (numerados). Eles aparecem na ordem em que são definidos na
expressão regular, da esquerda para a direita. Os valores dos índices desses grupos
variam de 1 até o número de grupos de captura não nomeados presentes na
coleção. (O índice de um determinado grupo é equivalente à sua referência inversa
numerada. Para mais informações sobre referência inversa, confira Constructos de
referência inversa.)
O conjunto final de objetos Group representa os grupos de captura nomeados.
Eles aparecem na ordem em que são definidos na expressão regular, da esquerda
para a direita. O valor do índice do primeiro grupo de captura nomeado é um
número maior que o índice do último grupo de captura não nomeado. Se não
houver grupos de captura não nomeados na expressão regular, o valor do índice
do primeiro grupo de captura nomeado será um.
Se você aplicar um quantificador a um grupo de captura, as propriedades
GroupCapture.Value, Capture.Index e Capture.Length do objeto refletirão a última
subcadeia de caracteres capturada por um grupo de captura. Você pode recuperar todo
o conjunto de subcadeias de caracteres capturadas por grupos que têm quantificadores
do objeto CaptureCollection e que são retornados pela propriedade Group.Captures.

O exemplo a seguir esclarece a relação entre os objetos Group e Capture.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(\b(\w+)\W+)+";
string input = "This is a short sentence.";
Match match = Regex.Match(input, pattern);
Console.WriteLine("Match: '{0}'", match.Value);
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
{
Console.WriteLine(" Group {0}: '{1}'", ctr,
match.Groups[ctr].Value);
int capCtr = 0;
foreach (Capture capture in match.Groups[ctr].Captures)
{
Console.WriteLine(" Capture {0}: '{1}'", capCtr,
capture.Value);
capCtr++;
}
}
}
}
// The example displays the following output:
// Match: 'This is a short sentence.'
// Group 1: 'sentence.'
// Capture 0: 'This '
// Capture 1: 'is '
// Capture 2: 'a '
// Capture 3: 'short '
// Capture 4: 'sentence.'
// Group 2: 'sentence'
// Capture 0: 'This'
// Capture 1: 'is'
// Capture 2: 'a'
// Capture 3: 'short'
// Capture 4: 'sentence'
O padrão da expressão regular (\b(\w+)\W+)+ extrai palavras, uma a uma, da cadeia de
caracteres. Ele é definido conforme mostrado na tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Juntos, esses


caracteres compõem uma palavra. Este é o segundo grupo de captura.

\W+ Estabeleça a correspondência com um ou mais caracteres que não compõem


palavras.

(\b(\w+)\W+) Corresponde ao padrão de um ou mais caracteres de palavra, seguido por um ou


mais caracteres que não compõem palavras, uma ou mais vezes. Este é o primeiro
grupo de captura.

O segundo grupo de captura corresponde a cada palavra da frase. O primeiro grupo de


captura corresponde a cada palavra com a pontuação e o espaço em branco que a
segue. O objeto Group, cujo índice é 2, fornece informações sobre o texto
correspondente ao segundo grupo de captura. O conjunto completo de palavras
capturadas pelo grupo de captura está disponível no objeto CaptureCollection,
retornado pela propriedade Group.Captures.

Confira também
Linguagem de expressões regulares – referência rápida
Retrocesso
Quantificadores em expressões
regulares
Artigo • 09/05/2023

Os quantificadores especificam quantas instâncias de um caractere, grupo ou classe de


caracteres devem estar presentes na entrada para encontrar uma correspondência. A
tabela a seguir lista os quantificadores com suporte no .NET:

Quantificador Greedy Quantificador lento Descrição

* *? Corresponde a zero ou mais vezes.

+ +? Corresponde a uma ou mais vezes.

? ?? Corresponde a zero ou uma vez.

{n} { n }? Corresponde exatamente a n vezes.

{ n ,} { n ,}? Corresponde a, pelo menos, n vezes.

{n,m} { n , m }? Corresponde de n a m vezes.

As quantidades n e m são constantes inteiras. Normalmente, os quantificadores são


greedy. Eles fazem com que o mecanismo de expressões regulares corresponda a
quantas ocorrências de padrões determinados forem possíveis. Acrescentar o caractere
? a um quantificador torna-o lento. Isso faz com que o mecanismo de expressão

regular corresponda ao menor número possível de ocorrências. Para obter uma


descrição completa da diferença entre quantificadores greedy e lentos, confira a seção
Quantificadores greedy e lentos mais adiante neste artigo.

) Importante

Aninhar quantificadores, como o padrão de expressão regular (a*)* , pode


aumentar o número de comparações que o mecanismo de expressão regular
precisa executar. O número de comparações pode aumentar como uma função
exponencial do número de caracteres na cadeia de caracteres de entrada. Para
saber mais sobre esse comportamento e suas soluções, veja Retrocesso.

Quantificadores em expressões regulares


As seções a seguir listam os quantificadores com suporte nas expressões regulares no
.NET:

7 Observação

Se os caracteres *, +, ?, { e } forem encontrados em um padrão de expressão


regular, o mecanismo de expressões regulares vai interpretá-los como
quantificadores ou parte de constructos de quantificador, exceto se estiverem
incluídos em uma classe de caracteres. Para interpretá-los como caracteres literais
fora de uma classe de caracteres, você precisa fazer o escape, antecedendo-os com
uma barra invertida. Por exemplo, a cadeia de caracteres \* em um padrão de
expressão regular é interpretada como um caractere asterisco (“*”) integral.

Corresponder a zero ou mais vezes: *


O quantificador * corresponde ao elemento anterior zero ou mais vezes. É equivalente
ao quantificador {0,} . * é um quantificador Greedy, cujo equivalente lento é *? .

O exemplo a seguir ilustra essa expressão regular. Cinco dos grupos de nove dígitos na
cadeia de caracteres de entrada correspondem ao padrão e quatro ( 95 , 929 , 9219 e
9919 ) não.

C#

string pattern = @"\b91*9*\b";


string input = "99 95 919 929 9119 9219 999 9919 91119";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// '99' found at position 0.
// '919' found at position 6.
// '9119' found at position 14.
// '999' found at position 24.
// '91119' found at position 33.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Especifica que a correspondência deve começar em um limite de palavra.

91* Corresponde a uma classe 9 seguida por zero ou mais 1 caracteres.


Padrão Descrição

9* Corresponde a zero ou mais caracteres 9 .

\b Especifica que a correspondência deve terminar em um limite de palavra.

Corresponder a um ou mais vezes: +


O quantificador + corresponde ao elemento anterior uma ou mais vezes. É equivalente
a {1,} . + é um quantificador Greedy, cujo equivalente lento é +? .

Por exemplo, a expressão regular \ban+\w*?\b tenta corresponder a palavras inteiras


que começam com a letra a seguidas por uma ou mais instâncias da letra n . O exemplo
a seguir ilustra essa expressão regular. A expressão regular corresponde às palavras an ,
annual , announcement e antique , e não correspondem corretamente a autumn e all .

C#

string pattern = @"\ban+\w*?\b";

string input = "Autumn is a great time for an annual announcement to all


antique collectors.";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'an' found at position 27.
// 'annual' found at position 30.
// 'announcement' found at position 37.
// 'antique' found at position 57.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

an+ Corresponde a um a seguido por um ou mais caracteres n .

\w*? Corresponde a um caractere de palavra zero ou mais vezes, mas o menor número de
vezes possível.

\b Terminar em um limite de palavra.


Corresponder a zero ou uma vez: ?
O quantificador ? corresponde ao elemento anterior zero ou uma vez. É equivalente a
{0,1} . ? é um quantificador Greedy, cujo equivalente lento é ?? .

Por exemplo, a expressão regular \ban?\b tenta corresponder a palavras inteiras que
começam com a letra a seguidas por zero ou uma instância da letra n . Em outras
palavras, ele tenta corresponder às palavras a e an . O exemplo a seguir ilustra essa
expressão regular:

C#

string pattern = @"\ban?\b";


string input = "An amiable animal with a large snout and an animated nose.";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'An' found at position 0.
// 'a' found at position 23.
// 'an' found at position 42.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

an? Corresponde a um a seguido por zero ou um caractere n .

\b Terminar em um limite de palavra.

Corresponder exatamente a n vezes: {n}


O quantificador { n } corresponde ao elemento anterior exatamente n vezes, em que n
é qualquer inteiro. { n } é um quantificador Greedy cujo equivalente lento é { n }? .

Por exemplo, a expressão regular \b\d+\,\d{3}\b tenta corresponder a um limite de


palavra seguido por um ou mais dígitos decimais seguidos por três dígitos decimais
seguidos por um limite de palavra. O exemplo a seguir ilustra essa expressão regular:

C#
string pattern = @"\b\d+\,\d{3}\b";
string input = "Sales totaled 103,524 million in January, " +
"106,971 million in February, but only " +
"943 million in March.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// '103,524' found at position 14.
// '106,971' found at position 45.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

\d+ Corresponde a um ou mais dígitos decimais.

\, Corresponde a um caractere de vírgula.

\d{3} Corresponde a três dígitos decimais.

\b Terminar em um limite de palavra.

Corresponder a pelo menos n vezes: {n,}


O quantificador { n ,} corresponde ao elemento anterior pelo menos n vezes, em que n
é qualquer inteiro. { n ,} é um quantificador Greedy cujo equivalente lento é { n ,}? .

Por exemplo, a expressão regular \b\d{2,}\b\D+ tenta corresponder a um limite de


palavra seguido por pelo menos dois dígitos seguidos por um limite de palavra e um
caractere não dígito. O exemplo a seguir ilustra essa expressão regular. A expressão
regular não corresponde à frase "7 days" porque contém apenas um dígito decimal,
mas corresponde com êxito às frases "10 weeks" e "300 years" .

C#

string pattern = @"\b\d{2,}\b\D+";


string input = "7 days, 10 weeks, 300 years";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// '10 weeks, ' found at position 8.
// '300 years' found at position 18.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

\d{2,} Corresponde a pelo menos dois dígitos decimais.

\b Corresponde a um limite de palavra.

\D+ Corresponde a pelo menos uma casa não decimal.

Corresponder entre n e m vezes: {n,m}


O quantificador { n , m } corresponde ao elemento anterior pelo menos n vezes, mas
não mais de m vezes, em que n e m são inteiros. { n , m } é um quantificador Greedy
cujo equivalente lento é { n , m }? .

No exemplo a seguir, a expressão regular (00\s){2,4} tenta corresponder a entre duas


e quatro ocorrências de dois dígitos zero seguidos por um espaço. A parte final da
cadeia de caracteres de entrada inclui esse padrão de cinco vezes em vez de no máximo
quatro. No entanto, apenas a parte inicial dessa subcadeia de caracteres (até o espaço e
o quinto par de zeros) corresponde ao padrão de expressão regular.

C#

string pattern = @"(00\s){2,4}";


string input = "0x00 FF 00 00 18 17 FF 00 00 00 21 00 00 00 00 00";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// '00 00 ' found at position 8.
// '00 00 00 ' found at position 23.
// '00 00 00 00 ' found at position 35.

Corresponder a zero ou mais vezes (correspondência


lenta): *?
O quantificador *? corresponde ao elemento anterior zero ou mais vezes, mas o menor
número de vezes possível. É a contraparte lenta do quantificador greedy * .

No exemplo a seguir, a expressão regular \b\w*?oo\w*?\b corresponde a todas as


palavras que contêm a cadeia de caracteres oo .

C#

string pattern = @"\b\w*?oo\w*?\b";


string input = "woof root root rob oof woo woe";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'woof' found at position 0.
// 'root' found at position 5.
// 'root' found at position 10.
// 'oof' found at position 19.
// 'woo' found at position 23.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

\w*? Corresponde a zero ou mais caracteres de palavra, mas o menor número de caracteres
possível.

oo Corresponde à cadeia de caracteres oo .

\w*? Corresponde a zero ou mais caracteres de palavra, mas o menor número de caracteres
possível.

\b Terminar em um limite de palavra.

Corresponder a uma ou mais vezes (correspondência


lenta): +?
O quantificador +? corresponde ao elemento anterior uma ou mais vezes, mas o menor
número de vezes possível. É a contraparte lenta do quantificador greedy + .

Por exemplo, a expressão regular \b\w+?\b corresponde a um ou mais caracteres


separados por limites de palavra. O exemplo a seguir ilustra essa expressão regular:
C#

string pattern = @"\b\w+?\b";


string input = "Aa Bb Cc Dd Ee Ff";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'Aa' found at position 0.
// 'Bb' found at position 3.
// 'Cc' found at position 6.
// 'Dd' found at position 9.
// 'Ee' found at position 12.
// 'Ff' found at position 15.

Corresponder a zero ou uma vez (correspondência lenta):


??
O quantificador ?? corresponde ao elemento anterior zero ou uma vez, mas o menor
número de vezes possível. É a contraparte lenta do quantificador greedy ? .

Por exemplo, a expressão regular ^\s*(System.)??Console.Write(Line)??\(?? tenta


corresponder às cadeias de caracteres Console.Write ou Console.WriteLine . A cadeia
de caracteres também pode incluir System. antes de Console , e pode ser seguida por
um parêntese de abertura. A cadeia de caracteres deve estar no início de uma linha,
embora possa ser antecedida por espaço em branco. O exemplo a seguir ilustra essa
expressão regular:

C#

string pattern = @"^\s*(System.)??Console.Write(Line)??\(??";


string input = "System.Console.WriteLine(\"Hello!\")\n" +
"Console.Write(\"Hello!\")\n" +
"Console.WriteLine(\"Hello!\")\n" +
"Console.ReadLine()\n" +
" Console.WriteLine";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnorePatternWhitespace |
RegexOptions.IgnoreCase |
RegexOptions.Multiline))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'System.Console.Write' found at position 0.
// 'Console.Write' found at position 36.
// 'Console.Write' found at position 61.
// ' Console.Write' found at position 110.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

^ Corresponde ao início do fluxo de entrada.

\s* Corresponde a zero ou mais caracteres de espaço em branco.

(System.)?? Corresponde a zero ou uma ocorrência da cadeia de caracteres System. .

Console.Write Corresponde à cadeia de caracteres Console.Write .

(Line)?? Corresponde a zero ou uma ocorrência da cadeia de caracteres Line .

\(?? Corresponde a zero ou uma ocorrência do parêntese de abertura.

Corresponder exatamente a n vezes (correspondência


lenta): {n}?
O quantificador { n }? corresponde ao elemento anterior exatamente n vezes, em que
n é qualquer inteiro. É a contraparte lenta do quantificador greedy { n } .

No exemplo a seguir, a expressão regular \b(\w{3,}?\.){2}?\w{3,}?\b é usada para


identificar um endereço web. A expressão corresponde a www.microsoft.com e
msdn.microsoft.com , mas não corresponde a mywebsite ou mycompany.com .

C#

string pattern = @"\b(\w{3,}?\.){2}?\w{3,}?\b";


string input = "www.microsoft.com msdn.microsoft.com mywebsite
mycompany.com";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'www.microsoft.com' found at position 0.
// 'msdn.microsoft.com' found at position 18.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição
Padrão Descrição

\b Iniciar em um limite de palavra.

(\w{3,}? Corresponde a pelo menos três caracteres de palavra, mas o menor número de
\.) caracteres possível, seguido por um caractere de ponto. Esse padrão é o primeiro
grupo de captura.

(\w{3,}? Corresponde ao padrão no primeiro grupo duas vezes, mas o menor número de vezes
\.){2}? possível.

\b Termina a correspondência em um limite de palavra.

Corresponder a pelo menos n vezes (correspondência


lenta): {n,}?
O quantificador { n ,}? corresponde ao elemento anterior pelo menos n vezes, em que
n é qualquer inteiro, mas o menor número de vezes possível. É o equivalente lento do
quantificador greedy { n ,} .

Veja o exemplo do quantificador { n }? na seção anterior para obter uma ilustração. A


expressão regular nesse exemplo usa o quantificador { n ,} para corresponder a uma
cadeia de caracteres que tem, pelo menos, três caracteres seguidos por um ponto final.

Corresponder entre n e m vezes (correspondência lenta):


{n,m}?
O quantificador { n , m }? corresponde ao elemento anterior entre n e m vezes, em que
n e m são inteiros, mas o menor número de vezes possível. É o equivalente lento do
quantificador greedy { n , m } .

No exemplo a seguir, a expressão regular \b[A-Z](\w*?\s*?){1,10}[.!?] corresponde a


frases que contêm entre uma e dez palavras. Corresponde a todas as frases na cadeia de
caracteres de entrada, exceto por uma frase que contém 18 palavras.

C#

string pattern = @"\b[A-Z](\w*?\s*?){1,10}[.!?]";


string input = "Hi. I am writing a short note. Its purpose is " +
"to test a regular expression that attempts to find "
+
"sentences with ten or fewer words. Most sentences " +
"in this note are short.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);

// The example displays the following output:


// 'Hi.' found at position 0.
// 'I am writing a short note.' found at position 4.
// 'Most sentences in this note are short.' found at position 132.

O padrão de expressão regular é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

[A-Z] Corresponde a um caractere maiúscula de A a Z.

(\w*? Corresponde a zero ou mais caracteres de palavra, seguidos por um ou mais caracteres
\s*?) de espaço em branco, mas o menor número de vezes possível. Esse padrão é o primeiro
grupo de captura.

{1,10} Corresponde ao padrão anterior entre 1 e 10 vezes.

[.!?] Corresponde a qualquer um dos caracteres de pontuação . , ! ou ? .

Quantificadores Greedy e lentos


Alguns quantificadores têm duas versões:

Uma versão Greedy.

Um quantificador Greedy tenta corresponder a um elemento tantas vezes quanto


possível.

Uma versão não Greedy (ou lenta).

Um quantificador não Greedy tenta corresponder a um elemento o menor número


de vezes possível. Você pode transformar um quantificador greedy em um
quantificador lento adicionando um ? .

Considere uma expressão regular que se destina a extrair os últimos quatro dígitos de
uma cadeia de caracteres de números, como um número de cartão de crédito. A versão
da expressão regular que usa o quantificador Greedy * é \b.*([0-9]{4})\b . No
entanto, se uma cadeia de caracteres contiver dois números, essa expressão regular
corresponde aos últimos quatro dígitos do segundo número, como mostra o exemplo a
seguir:

C#
string greedyPattern = @"\b.*([0-9]{4})\b";
string input1 = "1112223333 3992991999";
foreach (Match match in Regex.Matches(input1, greedyPattern))
Console.WriteLine("Account ending in ******{0}.", match.Groups[1].Value);

// The example displays the following output:


// Account ending in ******1999.

A expressão regular não corresponde ao primeiro número porque o quantificador *


tenta corresponder ao elemento anterior tantas vezes quanto possível em toda a cadeia
de caracteres e encontra sua correspondência no final da cadeia de caracteres.

Esse comportamento não é o desejado. Em vez disso, é possível usar o quantificador


lento *? para extrair dígitos de ambos os números, como mostra o exemplo a seguir:

C#

string lazyPattern = @"\b.*?([0-9]{4})\b";


string input2 = "1112223333 3992991999";
foreach (Match match in Regex.Matches(input2, lazyPattern))
Console.WriteLine("Account ending in ******{0}.", match.Groups[1].Value);

// The example displays the following output:


// Account ending in ******3333.
// Account ending in ******1999.

Na maioria dos casos, expressões regulares com quantificadores Greedy e lentos


retornam as mesmas correspondências. Geralmente retornam resultados diferentes
quando são usadas com o metacaractere curinga ( . ), que corresponde a qualquer
caractere.

Quantificadores e correspondências vazias


Os quantificadores * , + e { n , m } e seus equivalentes lentos nunca se repetem depois
de uma correspondência vazia quando o número mínimo de capturas foi encontrado.
Essa regra impede que quantificadores entrem em loops infinitos em correspondências
vazias de subexpressão quando o número máximo de capturas de grupo possíveis é
infinito ou quase infinito.

Por exemplo, o código a seguir mostra o resultado de uma chamada para o método
Regex.Match com o padrão de expressão regular (a?)* que corresponde a zero ou a
um caractere a zero ou mais vezes. Observe que o grupo de captura único captura cada
a bem como String.Empty, mas que não há uma segunda correspondência vazia,
porque a primeira correspondência vazia faz com que o quantificador pare de se repetir.
C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "(a?)*";
string input = "aaabbb";
Match match = Regex.Match(input, pattern);
Console.WriteLine("Match: '{0}' at index {1}",
match.Value, match.Index);
if (match.Groups.Count > 1) {
GroupCollection groups = match.Groups;
for (int grpCtr = 1; grpCtr <= groups.Count - 1; grpCtr++) {
Console.WriteLine(" Group {0}: '{1}' at index {2}",
grpCtr,
groups[grpCtr].Value,
groups[grpCtr].Index);
int captureCtr = 0;
foreach (Capture capture in groups[grpCtr].Captures) {
captureCtr++;
Console.WriteLine(" Capture {0}: '{1}' at index {2}",
captureCtr, capture.Value, capture.Index);
}
}
}
}
}
// The example displays the following output:
// Match: 'aaa' at index 0
// Group 1: '' at index 3
// Capture 1: 'a' at index 0
// Capture 2: 'a' at index 1
// Capture 3: 'a' at index 2
// Capture 4: '' at index 3

Para ver a diferença prática entre um grupo de captura que define um número mínimo e
máximo de captura e um que define um número fixo de capturas, considere os padrões
de expressão regular (a\1|(?(1)\1)){0,2} e (a\1|(?(1)\1)){2} . Ambas as expressões
regulares consistem em um único grupo de captura, que é definido na tabela a seguir:

Padrão Descrição

(a\1 Faça qualquer correspondência a a juntamente com o valor do primeiro grupo de


captura …

|(?(1) … ou teste se o primeiro grupo de captura foi definido. O constructo (?(1) não define
um grupo de captura.
Padrão Descrição

\1)) Se o primeiro grupo capturado existir, faça uma correspondência ao valor. Se o grupo
não existir, será correspondente a String.Empty.

A primeira expressão regular tenta corresponder a esse padrão entre zero e duas vezes;
a segunda, exatamente duas vezes. Como o primeiro padrão atinge seu número mínimo
de capturas com sua primeira captura de String.Empty, ele nunca se repete para tentar
corresponder a a\1 . O quantificador {0,2} permite apenas correspondências vazias na
última iteração. Por outro lado, a segunda expressão regular corresponde a a porque
avalia a\1 uma segunda vez. O número mínimo de iterações, 2, força o mecanismo a
repetir após uma correspondência vazia.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern, input;

pattern = @"(a\1|(?(1)\1)){0,2}";
input = "aaabbb";

Console.WriteLine("Regex pattern: {0}", pattern);


Match match = Regex.Match(input, pattern);
Console.WriteLine("Match: '{0}' at position {1}.",
match.Value, match.Index);
if (match.Groups.Count > 1) {
for (int groupCtr = 1; groupCtr <= match.Groups.Count - 1;
groupCtr++)
{
Group group = match.Groups[groupCtr];
Console.WriteLine(" Group: {0}: '{1}' at position {2}.",
groupCtr, group.Value, group.Index);
int captureCtr = 0;
foreach (Capture capture in group.Captures) {
captureCtr++;
Console.WriteLine(" Capture: {0}: '{1}' at position
{2}.",
captureCtr, capture.Value, capture.Index);
}
}
}
Console.WriteLine();

pattern = @"(a\1|(?(1)\1)){2}";
Console.WriteLine("Regex pattern: {0}", pattern);
match = Regex.Match(input, pattern);
Console.WriteLine("Matched '{0}' at position {1}.",
match.Value, match.Index);
if (match.Groups.Count > 1) {
for (int groupCtr = 1; groupCtr <= match.Groups.Count - 1;
groupCtr++)
{
Group group = match.Groups[groupCtr];
Console.WriteLine(" Group: {0}: '{1}' at position {2}.",
groupCtr, group.Value, group.Index);
int captureCtr = 0;
foreach (Capture capture in group.Captures) {
captureCtr++;
Console.WriteLine(" Capture: {0}: '{1}' at position
{2}.",
captureCtr, capture.Value, capture.Index);
}
}
}
}
}
// The example displays the following output:
// Regex pattern: (a\1|(?(1)\1)){0,2}
// Match: '' at position 0.
// Group: 1: '' at position 0.
// Capture: 1: '' at position 0.
//
// Regex pattern: (a\1|(?(1)\1)){2}
// Matched 'a' at position 0.
// Group: 1: 'a' at position 0.
// Capture: 1: '' at position 0.
// Capture: 2: 'a' at position 0.

Confira também
Linguagem de expressões regulares – referência rápida
Retrocesso
Construtores de referência inversa em
expressões regulares
Artigo • 10/05/2023

As referências inversas fornecem uma maneira conveniente de identificar um caractere


ou subcadeia de caracteres repetida em uma cadeia de caracteres. Por exemplo, se a
cadeia de caracteres de entrada contiver várias ocorrências de uma subcadeia de
caracteres arbitrária, você poderá corresponder a primeira ocorrência a um grupo de
captura e, em seguida, usar uma referência inversa para corresponder às ocorrências
subsequentes da subcadeia de caracteres.

7 Observação

Uma sintaxe separada é usada para se referir a grupos de captura nomeados e


numerados em cadeias de caracteres de substituição. Para saber mais, confira
Substituições.

O .NET define elementos de linguagem separados para se referir a grupos de captura


nomeados e numerados. Para saber mais sobre captura de grupos, confira Constructos
de agrupamento.

Referências inversas numeradas


Uma referência inversa numerada usa a seguinte sintaxe:

\ number

em que number é a posição ordinal do grupo de captura na expressão regular. Por


exemplo, \4 corresponde ao conteúdo do quarto grupo de captura. Se number não for
definido no padrão da expressão regular, ocorrerá um erro de análise e o mecanismo de
expressões regulares gerará um ArgumentException. Por exemplo, a expressão regular
\b(\w+)\s\1 é válida porque (\w+) é o primeiro e único grupo de captura na expressão.
Por outro lado, \b(\w+)\s\2 é inválida e gera uma exceção de argumento porque não
há nenhum grupo de captura com o número \2 . Além disso, quando number identifica
um grupo de captura em uma determinada posição ordinal, mas um nome numérico
diferente da posição ordinal desse grupo de captura é atribuído a ele, o analisador de
expressões regulares também gera um ArgumentException.
Observe a ambiguidade entre códigos de escape octais (como \16 ) e as referências
inversas \ number que usam a mesma notação. Essa ambiguidade é resolvida da
seguinte maneira:

As expressões \1 a \9 sempre são interpretadas como referências inversas e não


como códigos octais.

Se o primeiro dígito de uma expressão de diversos for 8 ou 9 (como \80 ou \91 ),


a expressão será interpretada como uma literal.

Expressões de \10 e maiores serão consideradas referências inversas se houver


uma referência inversa correspondente àquele número, caso contrário, elas serão
interpretadas como códigos octais.

Se uma expressão regular contiver uma referência inversa para um número de


grupo indefinido, ocorrerá um erro de análise e o mecanismo de expressões
regulares gerará um ArgumentException.

Se a ambiguidade for um problema, você poderá usar a notação \k< name > , que não é
ambígua e não pode ser confundida com códigos de caracteres octais. Da mesma
forma, os códigos hexadecimais como \xdd são não ambíguos e não podem ser
confundidos com referências inversas.

O exemplo a seguir localiza caracteres de palavra duplicados em uma cadeia de


caracteres. Ele define uma expressão regular, (\w)\1 , que consiste nos elementos a
seguir.

Elemento Descrição

(\w) Corresponde a um caractere de palavra e o atribui ao primeiro grupo de captura.

\1 Corresponde ao próximo caractere que é o mesmo que o valor do primeiro grupo de


captura.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(\w)\1";
string input = "trellis llama webbing dresser swagger";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("Found '{0}' at position {1}.",
match.Value, match.Index);
}
}
// The example displays the following output:
// Found 'll' at position 3.
// Found 'll' at position 8.
// Found 'bb' at position 16.
// Found 'ss' at position 25.
// Found 'gg' at position 33.

Referências inversas nomeadas


Uma referência inversa nomeada é definida usando a sintaxe a seguir:

\k< name >

ou:

\k' name '

em que name é o nome de um grupo de captura definido no padrão da expressão


regular. Se name não for definido no padrão da expressão regular, ocorrerá um erro de
análise e o mecanismo de expressões regulares gerará um ArgumentException.

O exemplo a seguir localiza caracteres de palavra duplicados em uma cadeia de


caracteres. Ele define uma expressão regular, (?<char>\w)\k<char> , que consiste nos
elementos a seguir.

Elemento Descrição

(? Corresponde a um caractere de palavra e o atribui a um grupo de captura chamado


<char>\w) char .

\k<char> Corresponde ao próximo caractere que é o mesmo que o valor do grupo de captura
char .

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(?<char>\w)\k<char>";
string input = "trellis llama webbing dresser swagger";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("Found '{0}' at position {1}.",
match.Value, match.Index);
}
}
// The example displays the following output:
// Found 'll' at position 3.
// Found 'll' at position 8.
// Found 'bb' at position 16.
// Found 'ss' at position 25.
// Found 'gg' at position 33.

Referências inversas numéricas nomeadas


Em uma referência inversa nomeada com \k , name também pode ser a representação
da cadeia de caracteres de um número. Por exemplo, o exemplo a seguir usa a
expressão regular (?<2>\w)\k<2> para localizar caracteres de palavra duplicados em
uma cadeia de caracteres. Nesse caso, o exemplo define um grupo de captura
explicitamente nomeado como "2", e a referência inversa é correspondentemente
denominada "2".

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(?<2>\w)\k<2>";
string input = "trellis llama webbing dresser swagger";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("Found '{0}' at position {1}.",
match.Value, match.Index);
}
}
// The example displays the following output:
// Found 'll' at position 3.
// Found 'll' at position 8.
// Found 'bb' at position 16.
// Found 'ss' at position 25.
// Found 'gg' at position 33.

Se name é a representação de cadeia de caracteres de um número e nenhum grupo de


captura tem esse nome, \k< name > é o mesmo que o \ number da referência inversa,
em que number é a posição ordinal da captura. No exemplo a seguir, há um único
grupo de captura nomeado char . O constructo de referência inversa se refere a ele
como \k<1> . Conforme demonstrado pela saída do exemplo, a chamada para o
Regex.IsMatch é bem-sucedida porque char é o primeiro grupo de captura.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
Console.WriteLine(Regex.IsMatch("aa", @"(?<char>\w)\k<1>"));
// Displays "True".
}
}

No entanto, se name é a representação de cadeia de caracteres de um número e um


nome numérico foi explicitamente atribuído a um grupo de captura nessa posição, o
analisador de expressão regular não pode identificar o grupo de captura por sua
posição ordinal. Em vez disso, ele gera um ArgumentException. O único grupo de
captura no exemplo a seguir é denominado "2". Já que o constructo \k é usado para
definir uma referência inversa denominada "1", o analisador de expressão regular não
pode identificar o primeiro grupo de captura e gera uma exceção.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
Console.WriteLine(Regex.IsMatch("aa", @"(?<2>\w)\k<1>"));
// Throws an ArgumentException.
}
}

A que as referências inversas correspondem


Uma referência inversa refere-se à definição mais recente de um grupo (a definição mais
imediatamente à esquerda, ao fazer a correspondência da esquerda para a direita).
Quando um grupo faz várias capturas, uma referência inversa refere-se à captura mais
recente.

O exemplo a seguir inclui um padrão de expressão regular, (?<1>a)(?<1>\1b)* , que


redefine o grupo nomeado \1. A tabela a seguir descreve cada padrão na expressão
regular.

Padrão Descrição

(?<1>a) Corresponde ao caractere "a" e atribui o resultado ao grupo de captura chamado 1 .

(? Corresponda zero ou mais ocorrências ao grupo chamado 1 junto com "b" e atribua o
<1>\1b)* resultado ao grupo de captura chamado 1 .

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"(?<1>a)(?<1>\1b)*";
string input = "aababb";
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine("Match: " + match.Value);
foreach (Group group in match.Groups)
Console.WriteLine(" Group: " + group.Value);
}
}
}
// The example displays the following output:
// Group: aababb
// Group: abb

Comparando a expressão regular com a cadeia de caracteres de entrada ("aababb"), o


mecanismo de expressões regulares realiza as seguintes operações:

1. Ele começa no início da cadeia de caracteres e corresponde com êxito o “a” com a
expressão (?<1>a) . O valor do grupo 1 agora é "a".

2. Ele avança para o segundo caractere e corresponde com êxito a cadeia de


caracteres "ab" com a expressão \1b , ou "ab". Em seguida, atribui o resultado,
"ab", a \1 .
3. Ele avança para o quarto caractere. A expressão (?<1>\1b)* deve ser
correspondida zero ou mais vezes, de forma que corresponda com êxito a cadeia
de caracteres “abb” à expressão \1b . Ela atribui o resultado, “abb”, a \1 .

Neste exemplo, * é um quantificador looping – ele é avaliado repetidamente até que o


mecanismo de expressões regulares não corresponda ao padrão definido. Os
quantificadores de looping não limpam definições de grupo.

Se um grupo não capturou nenhuma subcadeia de caracteres, uma referência inversa a


esse grupo é indefinida e nunca corresponde. Isso é ilustrado pelo padrão de expressão
regular \b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b que é definido da seguinte maneira:

Padrão Descrição

\b Começa a correspondência em um limite de palavra.

(\p{Lu} Corresponde a duas letras maiúsculas. Este é o primeiro grupo de captura.


{2})

(\d{2})? Corresponde a zero ou uma ocorrência de dois dígitos decimais. Este é o segundo
grupo de captura.

(\p{Lu} Corresponde a duas letras maiúsculas. Este é o terceiro grupo de captura.


{2})

\b Termina a correspondência em um limite de palavra.

Uma cadeia de caracteres de entrada pode corresponder a essa expressão regular,


mesmo se os dois dígitos decimais que são definidos pelo segundo grupo de captura
não estiverem presentes. O exemplo a seguir mostra que, embora a correspondência
seja bem-sucedida, um grupo de captura vazio foi encontrado entre dois grupos de
captura com êxito.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\p{Lu}{2})(\d{2})?(\p{Lu}{2})\b";
string[] inputs = { "AA22ZZ", "AABB" };
foreach (string input in inputs)
{
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine("Match in {0}: {1}", input, match.Value);
if (match.Groups.Count > 1)
{
for (int ctr = 1; ctr <= match.Groups.Count - 1; ctr++)
{
if (match.Groups[ctr].Success)
Console.WriteLine("Group {0}: {1}",
ctr, match.Groups[ctr].Value);
else
Console.WriteLine("Group {0}: <no match>", ctr);
}
}
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match in AA22ZZ: AA22ZZ
// Group 1: AA
// Group 2: 22
// Group 3: ZZ
//
// Match in AABB: AABB
// Group 1: AA
// Group 2: <no match>
// Group 3: BB

Confira também
Linguagem de expressões regulares – referência rápida
Construtores de alternância em
expressões regulares
Artigo • 10/05/2023

Os constructos de alternância modificam uma expressão regular para permitir uma


correspondência condicional ou do tipo um/ou outro. O .NET dá suporte a três
constructos de alternância:

Correspondência de padrão com |


Correspondência condicional com (?(expression)yes|no)
Correspondência condicional com base em um grupo capturado válido

Correspondência de padrão com |


Você pode usar o caractere de barra vertical ( | ) para corresponder a qualquer um de
uma série de padrões, no qual o caractere | separa cada padrão.

Como a classe de caracteres positivos, o caractere | pode ser usado para corresponder
a qualquer um de vários caracteres únicos. O exemplo a seguir usa uma classe de
caracteres positivos e um padrão do tipo um/ou outro correspondendo ao caractere |
para localizar ocorrências das palavras “gray” ou “grey” em uma cadeia de caracteres.
Nesse caso, o caractere | produz uma expressão regular que é mais detalhada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
// Regular expression using character class.
string pattern1 = @"\bgr[ae]y\b";
// Regular expression using either/or.
string pattern2 = @"\bgr(a|e)y\b";

string input = "The gray wolf blended in among the grey rocks.";
foreach (Match match in Regex.Matches(input, pattern1))
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index);
Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern2))
Console.WriteLine("'{0}' found at position {1}",
match.Value, match.Index);
}
}
// The example displays the following output:
// 'gray' found at position 4
// 'grey' found at position 35
//
// 'gray' found at position 4
// 'grey' found at position 35

A expressão regular que usa o caractere | , \bgr(a|e)y\b é interpretada conforme


mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

gr Corresponder aos caracteres "gr".

(a|e) Corresponder a um "a" ou "e".

y\b Corresponder a um “y” em um limite de palavra.

O caractere | também pode ser usado para executar uma correspondência do tipo
um/ou outro com vários caracteres ou subexpressões, que podem incluir qualquer
combinação de literais de caracteres e elementos de linguagem de expressão regular. (A
classe de caractere não oferece essa funcionalidade.) O exemplo a seguir usa o caractere
| para extrair um SSN (Número de Segurança Social) dos EUA, que é um número de 9

dígitos com o formato ddd-dd-dddd, ou um EIN (Número de Identificação do


Empregador) dos EUA, que é um número de 9 dígitos com o formato dd-ddddddd.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b";
string input = "01-9999999 020-333333 777-88-9999";
Console.WriteLine("Matches for {0}:", pattern);
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(" {0} at position {1}", match.Value,
match.Index);
}
}
// The example displays the following output:
// Matches for \b(\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b:
// 01-9999999 at position 0
// 777-88-9999 at position 22

A expressão regular \b(\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b é interpretada conforme


mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

(\d{2}- Corresponde a uma das seguintes opções: dois dígitos decimais seguidos por um
\d{7}|\d{3}- hífen seguido por sete dígitos decimais ou três dígitos decimais, um hífen, dois
\d{2}- dígitos decimais, outro hífen e quatro dígitos decimais.
\d{4})

\b Termina a correspondência em um limite de palavra.

Correspondência condicional com uma


expressão
Este elemento de linguagem tenta corresponder a um dos dois padrões dependendo de
se ele pode corresponder a um padrão inicial. Sua sintaxe é:

(?( expressão ) sim )

ou

(?( expressão ) sim | não )

em que expression é o padrão inicial para correspondência, yes é o padrão para


correspondência se expression for correspondida e no é o padrão opcional para
correspondência se expression não for correspondida (se um padrão no não for
fornecido, ele é equivalente a um no vazio). O mecanismo de expressões regulares trata
a expressão como uma asserção de largura zero, isto é, o mecanismo de expressões
regulares não avança no fluxo de entrada após avaliar a expressão. Portanto, esse
constructo é equivalente ao seguinte:

(?(?= expression ) yes | no )

em que (?= expression ) é um constructo de asserção de largura zero. (Para mais


informações, consulte Constructo de agrupamento.) Como o mecanismo de expressões
regulares interpreta a expressão como uma âncora (uma asserção de largura zero), a
expressão precisa ser uma asserção de largura zero (para obter mais informações,
confira Âncoras) ou uma subexpressão que também está contida em sim. Caso
contrário, o padrão sim não pode ser correspondido.

7 Observação

Se expression for um grupo de captura nomeado ou numerado, o constructo de


alternância é interpretado como um teste de captura. Para obter mais informações,
confira Correspondência condicional com base em um grupo capturado válido.
Em outras palavras, o mecanismo de expressões regulares não tenta corresponder
a subcadeia de caracteres capturada, mas em vez disso, testa a presença ou
ausência do grupo.

O exemplo a seguir é uma variação do exemplo que aparece na seção E/Ou


Correspondência de Padrões com |. Ele usa a correspondência condicional para
determinar se os três primeiros caracteres após um limite de palavra são dois dígitos
seguidos por um hífen. Se forem, ele tentará corresponder a um EIN (Número de
Identificação do Empregador) dos EUA. Caso contrário, ele tenta corresponder a um SSN
(Número de Segurança Social) dos EUA.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(?(\d{2}-)\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b";
string input = "01-9999999 020-333333 777-88-9999";
Console.WriteLine("Matches for {0}:", pattern);
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(" {0} at position {1}", match.Value,
match.Index);
}
}
// The example displays the following output:
// Matches for \b(\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b:
// 01-9999999 at position 0
// 777-88-9999 at position 22

O padrão de expressão regular \b(?(\d{2}-)\d{2}-\d{7}|\d{3}-\d{2}-\d{4})\b é


interpretado conforme mostrado na tabela a seguir:

Padrão Descrição
Padrão Descrição

\b Iniciar em um limite de palavra.

(? Determinar se os próximos três caracteres são compostos por dois dígitos seguidos
(\d{2}-) por um hífen.

\d{2}- Se o padrão anterior corresponder, corresponder a dois dígitos seguidos por um


\d{7} hífen seguido por sete dígitos.

\d{3}- Se o padrão anterior não corresponder, corresponder a três dígitos decimais, um


\d{2}- hífen, dois dígitos decimais, outro hífen e quatro dígitos decimais.
\d{4}

\b Corresponder a um limite de palavra.

Correspondência condicional com base em um


grupo capturado válido
Este elemento de linguagem tenta corresponder a um dos dois padrões dependendo de
se ele correspondeu a um grupo de captura especificado. Sua sintaxe é:

(?( name ) yes )

ou

(?( name ) yes | no )

ou

(?( number ) yes )

ou

(?( number ) yes | no )

em que name é o nome e number é o número de um grupo de captura, yes é a


expressão para correspondência se name ou number tiver uma correspondência e no for
a expressão opcional para correspondência se não houver uma (se um padrão no não
for fornecido, ele é equivalente a um no vazio).

Se name não corresponder ao nome de um grupo de captura que é usado no padrão de


expressão regular, o constructo de alternância será interpretado como teste de
expressão, conforme explicado na seção anterior. Normalmente, isso significa que
expression é avaliada como false . Se number não corresponder a um grupo de captura
numerado que é usado no padrão de expressão regular, o mecanismo de expressões
regulares gerará um ArgumentException.

O exemplo a seguir é uma variação do exemplo que aparece na seção E/Ou


Correspondência de Padrões com |. Ele usa um grupo de captura chamado n2 que
consiste em dois dígitos seguidos por um hífen. O constructo de alternância testa se
este grupo de captura foi correspondido na cadeia de caracteres de entrada. Em caso
afirmativo, o constructo de alternância tenta corresponder aos sete últimos dígitos de
um EIN (Número de Identificação do Empregador) dos EUA. Caso contrário, ele tenta
corresponder a um SSN (Número de Segurança Social) dos EUA de 9 dígitos.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(?<n2>\d{2}-)?(?(n2)\d{7}|\d{3}-\d{2}-\d{4})\b";
string input = "01-9999999 020-333333 777-88-9999";
Console.WriteLine("Matches for {0}:", pattern);
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(" {0} at position {1}", match.Value,
match.Index);
}
}
// The example displays the following output:
// Matches for \b(?<n2>\d{2}-)?(?(n2)\d{7}|\d{3}-\d{2}-\d{4})\b:
// 01-9999999 at position 0
// 777-88-9999 at position 22

O padrão de expressão regular \b(?<n2>\d{2}-)?(?(n2)\d{7}|\d{3}-\d{2}-\d{4})\b é


interpretado conforme mostrado na tabela a seguir:

Padrão Descrição

\b Iniciar em um limite de palavra.

(? Corresponder a zero ou uma ocorrência de dois dígitos seguidos por um hífen.


<n2>\d{2}-)? Atribua um nome ao grupo de captura n2 .

(?(n2) Testar se n2 foi correspondido na cadeia de caracteres de entrada.

\d{7} Se n2 tiver sido correspondido, corresponder a sete dígitos decimais.


Padrão Descrição

|\d{3}- Se n2 não tiver sido correspondido, corresponder a três dígitos decimais, um


\d{2}-\d{4} hífen, dois dígitos decimais, outro hífen e quatro dígitos decimais.

\b Corresponder a um limite de palavra.

Uma variação desse exemplo que usa um grupo numerado em vez de um grupo
nomeado é mostrada no exemplo a seguir. O padrão da expressão regular é
\b(\d{2}-)?(?(1)\d{7}|\d{3}-\d{2}-\d{4})\b .

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\d{2}-)?(?(1)\d{7}|\d{3}-\d{2}-\d{4})\b";
string input = "01-9999999 020-333333 777-88-9999";
Console.WriteLine("Matches for {0}:", pattern);
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(" {0} at position {1}", match.Value,
match.Index);
}
}
// The example display the following output:
// Matches for \b(\d{2}-)?(?(1)\d{7}|\d{3}-\d{2}-\d{4})\b:
// 01-9999999 at position 0
// 777-88-9999 at position 22

Confira também
Linguagem de expressões regulares – referência rápida
Substituições em expressões regulares
Artigo • 10/05/2023

As substituições são elementos de linguagem que são reconhecidos apenas em padrões


de substituição. Eles usam um padrão de expressão regular para definir o todo ou parte do
texto que substitui o texto correspondente na cadeia de caracteres de entrada. O padrão
de substituição pode consistir em uma ou mais substituições junto com caracteres literais.
Padrões de substituição são fornecidos para sobrecargas do método Regex.Replace que
têm um parâmetro replacement e para o método Match.Result. Os métodos substituem o
padrão correspondente pelo padrão que é definido pelo parâmetro replacement .

O .NET define os elementos de substituição listados na tabela a seguir.

Substituição Descrição

$ number Inclui a última subcadeia de caracteres correspondida pelo grupo de captura que é
identificado por number, no qual number é um valor decimal na cadeia de caracteres
de substituição. Para obter mais informações, consulte Substituindo um grupo
numerado.

${ name } Inclui a última subcadeia de caracteres correspondida pelo grupo nomeado que é
designado por (?< name > ) na cadeia de caracteres de substituição. Para obter mais
informações, consulte Substituindo um grupo nomeado.

$$ Inclui um único literal “$” na cadeia de caracteres de substituição. Para obter mais
informações, consulte Substituindo um símbolo "$".

$& Inclui uma cópia da correspondência inteira na cadeia de caracteres de substituição.


Para obter mais informações, consulte Substituindo a correspondência inteira.

$` Inclui todo o texto da cadeia de caracteres de entrada antes da correspondência na


cadeia de caracteres de substituição. Para obter mais informações, consulte
Substituindo o texto antes da correspondência.

$' Inclui todo o texto da cadeia de caracteres de entrada após a correspondência na


cadeia de caracteres de substituição. Para obter mais informações, consulte
Substituindo o texto após a correspondência.

$+ O inclui o último grupo capturado na cadeia de caracteres de substituição. Para


obter mais informações, consulte Substituindo o último grupo capturado.

$_ Inclui a cadeia de entrada inteira na cadeia de caracteres de substituição. Para obter


mais informações, consulte Substituindo a cadeia de caracteres de entrada inteira.

Elementos de substituição e padrões de


substituição
As substituições são as únicas construções especiais reconhecidas em um padrão de
substituição. Nenhum dos outros elementos de linguagem de expressões regulares,
incluindo caracteres de escape e o ponto final ( . ), que corresponde a qualquer caractere,
são aceitos. Da mesma forma, os elementos de linguagem de substituição são
reconhecidos apenas nos padrões de substituição e nunca são válidos em padrões de
expressões regulares.

O único caractere que pode aparecer em um padrão de expressão regular ou em uma


substituição é o caractere $ , embora ele tenha um significado diferente em cada contexto.
Em um padrão de expressão regular, $ é uma âncora que corresponde ao final da cadeia
de caracteres. Em um padrão de substituição, $ indica o início de uma substituição.

7 Observação

Para uma funcionalidade semelhante a um padrão de substituição dentro de uma


expressão regular, use um backreference. Para obter mais informações sobre
referências inversas, consulte Construtores de referência inversa.

Substituindo um grupo numerado


O elemento de linguagem $ number inclui a última subcadeia de caracteres correspondida
pelo grupo de captura number na cadeia de caracteres de substituição, no qual number é
o índice do grupo de captura. Por exemplo, o padrão de substituição $1 indica que a
subcadeia de caracteres correspondente deve ser substituída pelo primeiro grupo
capturado. Para saber mais sobre os grupos de captura numerados, consulte Constructos
de agrupamento.

Todos os dígitos após $ são interpretados como pertencentes ao grupo number. Se essa
não for sua intenção, você poderá substituir um grupo nomeado. Por exemplo, você pode
usar a cadeia de caracteres de substituição ${1}1 em vez de $11 para definir a cadeia de
caracteres de substituição como o valor do primeiro grupo capturado junto com o número
“1 ". Para obter mais informações, consulte Substituindo um grupo nomeado.

Grupos de captura, aos quais não são atribuídos nomes explicitamente usando a sintaxe
(?< name >) , são numerados da esquerda para a direita, começando em um. Os grupos

nomeados também são numerados da esquerda para a direita, começando em um maior


que o índice do último grupo não nomeado. Por exemplo, na expressão regular (\w)(?
<digit>\d) , o índice do grupo nomeado digit é 2.

Se number não especificar um grupo de captura válido definido no padrão de expressão


regular, $ number será interpretado como uma sequência de caracteres literal que será
usada para substituir cada correspondência.

O exemplo a seguir usa a substituição $ número para remover o símbolo de moeda de um


valor decimal. Ele remove os símbolos de moeda localizados no início ou término de um
valor monetário e reconhece os dois separadores decimais mais comuns (“.” e “, ").

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\p{Sc}*(\s?\d+[.,]?\d*)\p{Sc}*";
string replacement = "$1";
string input = "$16.32 12.19 £16.29 €18.29 €18,29";
string result = Regex.Replace(input, pattern, replacement);
Console.WriteLine(result);
}
}
// The example displays the following output:
// 16.32 12.19 16.29 18.29 18,29

O padrão de expressão regular \p{Sc}*(\s?\d+[.,]?\d*)\p{Sc}* é definido conforme


mostrado na tabela a seguir.

Padrão Descrição

\p{Sc}* Corresponde a zero ou mais caracteres de símbolos de moedas.

\s? Corresponder a zero ou a um caractere de espaço em branco.

\d+ Corresponde a um ou mais dígitos decimais.

[.,]? Corresponde a zero ou um ponto ou vírgula.

\d* Corresponde a zero ou mais dígitos decimais.

(\s?\d+ Corresponde a um espaço em branco seguido por um ou mais dígitos decimais, seguidos
[.,]? por zero ou um ponto ou uma vírgula, seguidos por zero ou mais dígitos decimais. Este é
\d*) o primeiro grupo de captura. Como o padrão de substituição é $1 , a chamada ao método
Regex.Replace substitui a subcadeia de caracteres inteira correspondente a esse grupo
capturado.

Substituindo um grupo nomeado


O elemento de linguagem ${ name } substitui a última subcadeia de caracteres
correspondida pelo grupo de captura name, no qual name é o nome de um grupo de
captura definido pelo elemento de linguagem (?< name >) . Para saber mais sobre os
grupos de captura nomeados, consulte Constructos de agrupamento.

Se name não especificar um grupo de captura nomeado válido, definido no padrão da


expressão regular, mas consistir em dígitos, ${ name } será interpretado como um grupo
numerado.

Se name não especificar um grupo de captura nomeado válido nem um grupo de captura
numerado válido, definido no padrão da expressão regular, ${ name } será interpretado
como uma sequência de caracteres literal que será usada para substituir cada
correspondência.

O exemplo a seguir usa a substituição ${ name } para remover o símbolo de moeda de


um valor decimal. Ele remove os símbolos de moeda localizados no início ou término de
um valor monetário e reconhece os dois separadores decimais mais comuns (“.” e “, ").

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\p{Sc}*(?<amount>\s?\d+[.,]?\d*)\p{Sc}*";
string replacement = "${amount}";
string input = "$16.32 12.19 £16.29 €18.29 €18,29";
string result = Regex.Replace(input, pattern, replacement);
Console.WriteLine(result);
}
}
// The example displays the following output:
// 16.32 12.19 16.29 18.29 18,29

O padrão de expressão regular \p{Sc}*(?<amount>\s?\d[.,]?\d*)\p{Sc}* é definido


conforme mostrado na tabela a seguir.

Padrão Descrição

\p{Sc}* Corresponde a zero ou mais caracteres de símbolos de moedas.

\s? Corresponder a zero ou a um caractere de espaço em branco.

\d+ Corresponde a um ou mais dígitos decimais.


Padrão Descrição

[.,]? Corresponde a zero ou um ponto ou vírgula.

\d* Corresponde a zero ou mais dígitos decimais.

(? Corresponde a um espaço em branco seguido por um ou mais dígitos decimais,


<amount>\s? seguidos por zero ou um ponto ou uma vírgula, seguidos por zero ou mais dígitos
\d[.,]? decimais. Este é o grupo de captura chamado amount . Como o padrão de substituição
\d*) é ${amount} , a chamada ao método Regex.Replace substitui a subcadeia de caracteres
inteira correspondente a esse grupo capturado.

Substituindo um caractere “$”


A substituição de $$ insere um caractere literal “$” na cadeia de caracteres substituída.

O exemplo a seguir usa o objeto NumberFormatInfo para determinar o símbolo de moeda


atual da cultura e seu posicionamento em uma cadeia de caracteres de moeda. Ele então
cria um padrão de expressão regular e um padrão de substituição dinamicamente. Se o
exemplo é executado em um computador cuja cultura atual é en-US, ele gera o padrão de
expressão regular \b(\d+)(\.(\d+))? e o padrão de substituição $$ $1$2 . O padrão de
substituição substitui o texto correspondente por um símbolo de moeda e um espaço
seguido pelo primeiro e segundo grupos capturados.

C#

using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
// Define array of decimal values.
string[] values= { "16.35", "19.72", "1234", "0.99"};
// Determine whether currency precedes (True) or follows (False) number.
bool precedes = NumberFormatInfo.CurrentInfo.CurrencyPositivePattern % 2
== 0;
// Get decimal separator.
string cSeparator =
NumberFormatInfo.CurrentInfo.CurrencyDecimalSeparator;
// Get currency symbol.
string symbol = NumberFormatInfo.CurrentInfo.CurrencySymbol;
// If symbol is a "$", add an extra "$".
if (symbol == "$") symbol = "$$";

// Define regular expression pattern and replacement string.


string pattern = @"\b(\d+)(" + cSeparator + @"(\d+))?";
string replacement = "$1$2";
replacement = precedes ? symbol + " " + replacement : replacement + " "
+ symbol;
foreach (string value in values)
Console.WriteLine("{0} --> {1}", value, Regex.Replace(value, pattern,
replacement));
}
}
// The example displays the following output:
// 16.35 --> $ 16.35
// 19.72 --> $ 19.72
// 1234 --> $ 1234
// 0.99 --> $ 0.99

O padrão de expressão regular \b(\d+)(\.(\d+))? é definido conforme mostrado na


tabela a seguir.

Padrão Descrição

\b Inicia a correspondência no começo de um limite de palavra.

(\d+) Corresponde a um ou mais dígitos decimais. Este é o primeiro grupo de captura.

\. Faz a correspondência a um período (o separador decimal).

(\d+) Corresponde a um ou mais dígitos decimais. Este é o terceiro grupo de captura.

(\. Faz a correspondência de zero ou uma ocorrência de um período seguido por um ou mais
(\d+))? dígitos decimais. Este é o segundo grupo de captura.

Substituindo a correspondência inteira


A substituição $& inclui a correspondência inteira na cadeia de caracteres de substituição.
Muitas vezes, ela é usada para adicionar uma subcadeia de caracteres ao início ou fim da
cadeia de caracteres correspondente. Por exemplo, o padrão de substituição ($&) adiciona
parênteses no início e no final de cada correspondência. Se não houver correspondência, a
substituição $& não terá efeito.

O exemplo a seguir usa a substituição $& para adicionar aspas no início e no final de
títulos de livros armazenados em uma matriz de cadeia de caracteres.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"^(\w+\s?)+$";
string[] titles = { "A Tale of Two Cities",
"The Hound of the Baskervilles",
"The Protestant Ethic and the Spirit of Capitalism",
"The Origin of Species" };
string replacement = "\"$&\"";
foreach (string title in titles)
Console.WriteLine(Regex.Replace(title, pattern, replacement));
}
}
// The example displays the following output:
// "A Tale of Two Cities"
// "The Hound of the Baskervilles"
// "The Protestant Ethic and the Spirit of Capitalism"
// "The Origin of Species"

O padrão de expressão regular ^(\w+\s?)+$ é definido conforme mostrado na tabela a


seguir.

Padrão Descrição

^ Começa a correspondência no início da cadeia de caracteres de entrada.

(\w+\s?)+ Corresponde ao padrão de um ou mais caracteres de palavra seguidos por zero ou um


espaço em branco uma ou mais vezes.

$ Corresponder ao final da cadeia de caracteres de entrada.

O padrão de substituição "$&" adiciona aspas literais no início e no final de cada


correspondência.

Substituindo texto antes da correspondência


A substituição $` substitui a cadeia de caracteres correspondida pela cadeia de caracteres
de entrada inteira antes da correspondência. Ou seja, ela duplica a cadeia de caracteres de
entrada até a correspondência e remove o texto correspondido. Qualquer texto após o
texto correspondido permanece inalterado na cadeia de caracteres de resultado. Se houver
várias correspondências em uma cadeia de caracteres de entrada, o texto de substituição
será derivado da cadeia de caracteres de entrada original, em vez da cadeia de caracteres
em que o texto foi substituído por correspondências anteriores. (O exemplo fornece uma
ilustração.) Se não houver correspondência, a substituição de $` não terá efeito.

O exemplo a seguir usa o padrão de expressão regular \d+ para corresponder a uma
sequência de um ou mais dígitos decimais na cadeia de caracteres de entrada. A cadeia de
caracteres de substituição $` substitui esses dígitos pelo texto que precede a
correspondência.
C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "aa1bb2cc3dd4ee5";
string pattern = @"\d+";
string substitution = "$`";
Console.WriteLine("Matches:");
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(" {0} at position {1}", match.Value,
match.Index);

Console.WriteLine("Input string: {0}", input);


Console.WriteLine("Output string: " +
Regex.Replace(input, pattern, substitution));
}
}
// The example displays the following output:
// Matches:
// 1 at position 2
// 2 at position 5
// 3 at position 8
// 4 at position 11
// 5 at position 14
// Input string: aa1bb2cc3dd4ee5
// Output string: aaaabbaa1bbccaa1bb2ccddaa1bb2cc3ddeeaa1bb2cc3dd4ee

Neste exemplo, a cadeia de caracteres de entrada "aa1bb2cc3dd4ee5" contém cinco


correspondências. A tabela a seguir ilustra como a substituição $` faz com que o
mecanismo de expressão regular substitua cada correspondência na cadeia de caracteres
de entrada. O texto inserido é mostrado em negrito na coluna de resultados.

Corresponder Posição Cadeia de Cadeia de caracteres de resultado


a caracteres antes
da
correspondência

1 2 aa aaaabb2cc3dd4ee5

2 5 aa1bb aaaabbaa1bbcc3dd4ee5

3 8 aa1bb2cc aaaabbaa1bbccaa1bb2ccdd4ee5

4 11 aa1bb2cc3dd aaaabbaa1bbccaa1bb2ccddaa1bb2cc3ddee5

5 14 aa1bb2cc3dd4ee aaaabbaa1bbccaa1bb2ccddaa1bb2cc3ddeeaa1bb2cc3dd4ee
Substituindo texto após a correspondência
A substituição $' substitui a cadeia de caracteres correspondida pela cadeia de caracteres
de entrada inteira após a correspondência. Ou seja, ela duplica a cadeia de caracteres de
entrada após a correspondência e remove o texto correspondido. Qualquer texto antes do
texto correspondido permanece inalterado na cadeia de caracteres de resultado. Se não
houver correspondência, a substituição $' não terá efeito.

O exemplo a seguir usa o padrão de expressão regular \d+ para corresponder a uma
sequência de um ou mais dígitos decimais na cadeia de caracteres de entrada. A cadeia de
caracteres de substituição $' substitui esses dígitos pelo texto após a correspondência.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "aa1bb2cc3dd4ee5";
string pattern = @"\d+";
string substitution = "$'";
Console.WriteLine("Matches:");
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(" {0} at position {1}", match.Value,
match.Index);
Console.WriteLine("Input string: {0}", input);
Console.WriteLine("Output string: " +
Regex.Replace(input, pattern, substitution));
}
}
// The example displays the following output:
// Matches:
// 1 at position 2
// 2 at position 5
// 3 at position 8
// 4 at position 11
// 5 at position 14
// Input string: aa1bb2cc3dd4ee5
// Output string: aabb2cc3dd4ee5bbcc3dd4ee5ccdd4ee5ddee5ee

Neste exemplo, a cadeia de caracteres de entrada "aa1bb2cc3dd4ee5" contém cinco


correspondências. A tabela a seguir ilustra como a substituição $' faz com que o
mecanismo de expressão regular substitua cada correspondência na cadeia de caracteres
de entrada. O texto inserido é mostrado em negrito na coluna de resultados.
Corresponder Posição Cadeia de Cadeia de caracteres de resultado
a caracteres após a
correspondência

1 2 bb2cc3dd4ee5 aabb2cc3dd4ee5bb2cc3dd4ee5

2 5 cc3dd4ee5 aabb2cc3dd4ee5bbcc3dd4ee5cc3dd4ee5

3 8 dd4ee5 aabb2cc3dd4ee5bbcc3dd4ee5ccdd4ee5dd4ee5

4 11 ee5 aabb2cc3dd4ee5bbcc3dd4ee5ccdd4ee5ddee5ee5

5 14 String.Empty aabb2cc3dd4ee5bbcc3dd4ee5ccdd4ee5ddee5ee

Substituindo o último grupo capturado


A substituição $+ substitui a cadeia de caracteres correspondida pelo último grupo
capturado. Se não houver nenhum grupo capturado ou se o valor do grupo capturado por
último for String.Empty, a substituição $+ não terá efeito.

O exemplo a seguir identifica palavras duplicadas em uma cadeia de caracteres e usa a


substituição $+ para substituí-los por uma única ocorrência da palavra. A opção
RegexOptions.IgnoreCase é usada para garantir que as palavras que diferem apenas em
termos de letras maiúsculas e minúsculas, mas que de outra forma são idênticas, sejam
consideradas duplicadas.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\w+)\s\1\b";
string substitution = "$+";
string input = "The the dog jumped over the fence fence.";
Console.WriteLine(Regex.Replace(input, pattern, substitution,
RegexOptions.IgnoreCase));
}
}
// The example displays the following output:
// The dog jumped over the fence.

O padrão de expressão regular \b(\w+)\s\1\b é definido conforme mostrado na tabela a


seguir.
Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Este é o primeiro grupo de


captura.

\s Corresponde a um caractere de espaço em branco.

\1 Corresponde ao primeiro grupo capturado.

\b Termina a correspondência em um limite de palavra.

Substituindo a cadeia de caracteres de entrada


inteira
A substituição $_ substitui a cadeia de caracteres correspondida pela cadeia de caracteres
de entrada inteira. Ou seja, ela remove o texto correspondido e o substitui pela cadeia de
caracteres inteira, inclusive o texto correspondido.

O exemplo a seguir corresponde a um ou mais dígitos decimais na cadeia de caracteres de


entrada. Ele usa a substituição $_ para substituí-los pela cadeia de caracteres de entrada
inteira.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "ABC123DEF456";
string pattern = @"\d+";
string substitution = "$_";
Console.WriteLine("Original string: {0}", input);
Console.WriteLine("String with substitution: {0}",
Regex.Replace(input, pattern, substitution));
}
}
// The example displays the following output:
// Original string: ABC123DEF456
// String with substitution: ABCABC123DEF456DEFABC123DEF456

Neste exemplo, a cadeia de caracteres de entrada "ABC123DEF456" contém duas


correspondências. A tabela a seguir ilustra como a substituição $_ faz com que o
mecanismo de expressão regular substitua cada correspondência na cadeia de caracteres
de entrada. O texto inserido é mostrado em negrito na coluna de resultados.

Corresponder a Posição Corresponder a Cadeia de caracteres de resultado

1 3 123 ABCABC123DEF456DEF456

2 5 456 ABCABC123DEF456DEFABC123DEF456

Confira também
Linguagem de expressões regulares – referência rápida
Opções de expressões regulares
Artigo • 29/06/2023

Por padrão, a comparação de uma cadeia de caracteres de entrada com quaisquer


caracteres literais em um padrão de expressão regular diferencia maiúsculas e
minúsculas; o espaço em branco em um padrão de expressão regular é interpretado
como caracteres de espaço em branco literais e os grupos de captura em uma
expressão regular são nomeados implícita e explicitamente. É possível modificar esses e
vários outros aspectos do comportamento de expressão regular especificando opções
de expressões regulares. Algumas dessas opções, que estão listadas na tabela a seguir,
podem ser incluídas embutidas como parte do padrão de expressão regular, ou podem
ser fornecidas a um construtor de classe System.Text.RegularExpressions.Regex ou
método de correspondência padrão estático como um valor de enumeração
System.Text.RegularExpressions.RegexOptions.

Membro do Caractere Efeito Mais


RegexOptions embutido informações

None Não Use o comportamento padrão. Opções padrão


disponível

IgnoreCase i Use correspondência sem Correspondência


diferenciação de maiúsculas e sem
minúsculas. diferenciação
entre maiúsculas
e minúsculas

Multiline m Use o modo multilinha, em que ^ e Modo multilinha


$ indicam o início e o fim de cada
linha (em vez do início e o fim da
cadeia de caracteres de entrada).

Singleline s Use o modo de linha única, em que o Modo de linha


ponto (.) corresponde com todos os única
caracteres (em vez de todos os
caracteres, exceto \n ).

ExplicitCapture n Não capture grupos sem nome. As Apenas capturas


únicas capturas válidas são grupos explícitas
explicitamente nomeados ou
numerados na forma (?
< name > subexpression ) .
Membro do Caractere Efeito Mais
RegexOptions embutido informações

Compiled Não Compile a expressão regular para um Expressões


disponível assembly. regulares
compiladas

IgnorePatternWhitespace x Exclua um espaço em branco sem Ignorar espaço


escape do padrão e habilite em branco
comentários após uma tecla de
cerquilha ( # ).

RightToLeft Não Altera a direção da pesquisa. A Modo da direita


disponível pesquisa se move da direita para a para a esquerda
esquerda, em vez de da esquerda
para a direita.

ECMAScript Não Habilite o comportamento Comportamento


disponível compatível com ECMAScript para a de
expressão. correspondência
de ECMAScript

CultureInvariant Não Ignorar diferenças culturais no Comparação


disponível idioma. usando a cultura
invariável

NonBacktracking Não Faça a correspondência usando uma Modo sem


disponível abordagem que evita o rastreamento rastreamento
inverso e garante o processamento inverso
de tempo linear no comprimento da
entrada. (Disponível no .NET 7 e
versões posteriores.)

Especificar opções
É possível especificar opções para expressões regulares de uma destas três maneiras:

No parâmetro options de um construtor de classe


System.Text.RegularExpressions.Regex ou método de correspondência padrão
( Shared no Visual Basic) estático, como Regex(String, RegexOptions) ou
Regex.Match(String, String, RegexOptions). O parâmetro options é uma
combinação OR bit a bit de valores enumerados
System.Text.RegularExpressions.RegexOptions.

Quando as opções são fornecidas a uma instância Regex mediante uso do


parâmetro options de um construtor de classe, elas são atribuídas à propriedade
System.Text.RegularExpressions.RegexOptions. No entanto, a propriedade
System.Text.RegularExpressions.RegexOptions não reflete opções embutidas no
próprio padrão de expressão regular.

O exemplo a seguir ilustra esse cenário. Ele usa o parâmetro options do método
Regex.Match(String, String, RegexOptions) para habilitar correspondência sem
diferenciação entre maiúsculas e minúsculas e ignorar espaço em branco do
parâmetro ao identificar palavras que começam com a letra "d".

C#

string pattern = @"d \w+ \s";


string input = "Dogs are decidedly good pets.";
RegexOptions options = RegexOptions.IgnoreCase |
RegexOptions.IgnorePatternWhitespace;

foreach (Match match in Regex.Matches(input, pattern, options))


Console.WriteLine("'{0}// found at index {1}.", match.Value,
match.Index);
// The example displays the following output:
// 'Dogs // found at index 0.
// 'decidedly // found at index 9.

Aplicando opções embutidas em um padrão de expressão regular com a sintaxe


(?imnsx-imnsx) . A opção se aplica ao padrão do ponto em que a opção é definida
até o fim do padrão ou o ponto em que a opção tem é indefinida por outra opção
embutida. Observe que a propriedade
System.Text.RegularExpressions.RegexOptions de uma instância Regex não reflete
essas opções embutidas. Para saber mais, confira o tópico Constructos diversos.

O exemplo a seguir ilustra esse cenário. Ele usa opções embutidas para habilitar a
correspondência sem diferenciação entre maiúsculas e minúsculas e ignorar o
espaço em branco do padrão ao identificar palavras que começam com a letra “d”.

C#

string pattern = @"(?ix) d \w+ \s";


string input = "Dogs are decidedly good pets.";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine("'{0}// found at index {1}.", match.Value,
match.Index);
// The example displays the following output:
// 'Dogs // found at index 0.
// 'decidedly // found at index 9.
Aplicando opções embutidas em um constructo de agrupamento em particular em
um padrão de expressão regular com a sintaxe (?imnsx-imnsx: subexpressão ) .
Nenhum sinal antes de um conjunto de opções ativa o conjunto; um sinal de
subtração antes de um conjunto de opções desativa o conjunto. ( ? é uma parte
fixa da sintaxe do constructo do idioma exigida com as opções habilitadas ou
desabilitadas). A opção se aplica somente a esse grupo. Para saber mais, confira
Constructos de agrupamento.

O exemplo a seguir ilustra esse cenário. Ele usa opções embutidas em um


constructo de agrupamento para habilitar a correspondência sem diferenciação
entre maiúsculas e minúsculas e ignorar espaço em branco do padrão ao
identificar palavras que começam com a letra “d”.

C#

string pattern = @"\b(?ix: d \w+)\s";


string input = "Dogs are decidedly good pets.";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine("'{0}// found at index {1}.", match.Value,
match.Index);
// The example displays the following output:
// 'Dogs // found at index 0.
// 'decidedly // found at index 9.

Se as opções forem embutidas especificadas, um sinal de menos ( - ) antes de uma


opção ou conjunto de opções desativa essas opções. Por exemplo, a construção
embutida (?ix-ms) ativa as opções RegexOptions.IgnoreCase e
RegexOptions.IgnorePatternWhitespace e desativa as opções RegexOptions.Multiline e
RegexOptions.Singleline. Todas as opções de expressões regulares são desativadas por
padrão.

7 Observação

Se as opções de expressão regular especificadas no parâmetro options de uma


chamada de construtor ou método entrar em conflito com as opções especificadas
embutidas em um padrão de expressão regular, serão usadas as opções embutidas.

As cinco opções de expressão regular a seguir podem ser definidas com parâmetro de
opções e embutidas:

RegexOptions.IgnoreCase
RegexOptions.Multiline

RegexOptions.Singleline

RegexOptions.ExplicitCapture

RegexOptions.IgnorePatternWhitespace

As cinco opções de expressão regular a seguir podem ser definidas usando o parâmetro
options , mas não podem ser definidas embutidas:

RegexOptions.None

RegexOptions.Compiled

RegexOptions.RightToLeft

RegexOptions.CultureInvariant

RegexOptions.ECMAScript

Determinar opções
É possível determinar que opções foram fornecidas a um objeto Regex quando ele tiver
sido instanciado recuperando o valor da propriedade Regex.Options somente leitura.
Essa propriedade é particularmente útil para determinar as opções definidas para uma
expressão regular compilada criada pelo método Regex.CompileToAssembly.

Para testar a presença de qualquer opção, exceto RegexOptions.None, realize uma


operação AND com o valor da propriedade Regex.Options e o valor RegexOptions no
qual você está interessado. Em seguida, teste se o resultado é igual ao valor de
RegexOptions. O exemplo a seguir testa se a opção RegexOptions.IgnoreCase foi
definida.

C#

if ((rgx.Options & RegexOptions.IgnoreCase) == RegexOptions.IgnoreCase)


Console.WriteLine("Case-insensitive pattern comparison.");
else
Console.WriteLine("Case-sensitive pattern comparison.");

Para testar RegexOptions.None, determine se o valor da propriedade Regex.Options é


igual a RegexOptions.None, como ilustra o exemplo a seguir.

C#
if (rgx.Options == RegexOptions.None)
Console.WriteLine("No options have been set.");

As seções a seguir listam as opções com suporte na expressão regular no .NET.

Opções padrão
A opção RegexOptions.None indica que nenhuma opção foi especificada, e o
mecanismo de expressão regular usa seu comportamento padrão. Isso inclui o seguinte:

O padrão é interpretado como canônico e não como uma expressão regular


ECMAScript.

O padrão da expressão regular é combinado na cadeia de caracteres de entrada da


esquerda para a direita.

As comparações diferenciam maiúsculas de minúsculas.

Os elementos de linguagem ^ e $ indicam o início e o fim da cadeia de caracteres


de entrada. O final da cadeia de caracteres de entrada pode ser um caractere de
nova linha \n à direita.

O elemento de linguagem . corresponde com todos os caracteres, exceto \n .

Qualquer espaço em branco em um padrão de expressão regular é interpretado


como caractere de espaço literal.

As convenções da cultura atual são usadas ao comparar o padrão com a cadeia de


caracteres de entrada.

Os grupos de capturas no padrão de expressão regular são implícitos e explícitos.

7 Observação

A opção RegexOptions.None não tem equivalente embutido. Quando as opções


de expressões regulares são embutidas aplicadas, o comportamento padrão é
restaurado de modo opção a opção, desativando uma opção em particular. Por
exemplo, (?i) ativa a comparação sem diferenciar maiúsculas de minúsculas e (?-
i) restaura a comparação que diferencia maiúsculas de minúsculas.

Como a opção RegexOptions.None representa o comportamento padrão do mecanismo


de expressão regular, raramente, ela é explicitamente especificada em uma chamada de
método. Em vez disso, é chamado um método de construtor ou de correspondência
padrão estático sem um parâmetro options .

Correspondência sem diferenciação entre


maiúsculas e minúsculas
A opção IgnoreCase ou a opção embutida i fornece correspondência sem
diferenciação entre maiúsculas e minúsculas. Por padrão, são usadas as convenções de
diferenciação entre maiúsculas e minúsculas da cultura atual.

O exemplo a seguir define um padrão de expressão regular, \bthe\w*\b , que


corresponde a todas as palavras que começam com “the”. Como a primeira chamada
para o método Match usa a comparação que diferencia maiúsculas de minúsculas
padrão, a saída indica que a cadeia de caracteres "The", que inicia a frase, não é
combinada. Ela é combinada quando o método Match é chamado com opções
definidas para IgnoreCase.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\bthe\w*\b";
string input = "The man then told them about that event.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("Found {0} at index {1}.", match.Value,
match.Index);

Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("Found {0} at index {1}.", match.Value,
match.Index);
}
}
// The example displays the following output:
// Found then at index 8.
// Found them at index 18.
//
// Found The at index 0.
// Found then at index 8.
// Found them at index 18.
O exemplo a seguir modifica o padrão da expressão regular do exemplo anterior para
usar opções embutidas, em vez do parâmetro options para fornecer comparação de
diferenciação entre maiúsculas e minúsculas. O primeiro padrão define a opção que não
diferencia maiúsculas de minúsculas em um constructo de agrupamento que se aplica
apenas à letra “t” na cadeia de caracteres “the”. Como o constructo da opção ocorre no
início do padrão, o segundo padrão aplica a opção que não diferencia maiúsculas de
minúsculas a toda a expressão regular.

C#

using System;
using System.Text.RegularExpressions;

public class CaseExample


{
public static void Main()
{
string pattern = @"\b(?i:t)he\w*\b";
string input = "The man then told them about that event.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("Found {0} at index {1}.", match.Value,
match.Index);

Console.WriteLine();
pattern = @"(?i)\bthe\w*\b";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine("Found {0} at index {1}.", match.Value,
match.Index);
}
}
// The example displays the following output:
// Found The at index 0.
// Found then at index 8.
// Found them at index 18.
//
// Found The at index 0.
// Found then at index 8.
// Found them at index 18.

Modo multilinha
A opção RegexOptions.Multiline ou a opção embutida m habilita o mecanismo de
expressão regular para processar uma cadeia de caracteres de entrada que consiste em
várias linhas. Ele altera a interpretação dos elementos de linguagem ^ e $ para que
indiquem o início e o fim de uma linha, em vez de o início e o fim da cadeia de
caracteres de entrada.
Por padrão, $ será atendido somente no final da cadeia de caracteres de entrada. Se
você especificar a opção RegexOptions.Multiline, ela será atendida pelo caractere de
nova linha ( \n ) ou com o fim da cadeia de caracteres de entrada.

Em nenhum dos casos, $ reconhece a combinação de caracteres de retorno de


carro/feed de linha ( \r\n ). $ sempre ignora qualquer retorno de carro ( \r ). Para
encerrar a correspondência com \r\n ou \n , use a subexpressão \r?$ em vez de
apenas $ . Observe que isso fará a parte \r da correspondência.

O exemplo a seguir extrai nomes e pontuações de jogadores de boliche e os adiciona a


uma coleção SortedList<TKey,TValue>, que os classifica em ordem decrescente. O
método Matches é chamado duas vezes. Na primeira chamada do método, a expressão
regular é ^(\w+)\s(\d+)$ e nenhuma opção é definida. Como a saída mostra, uma vez
que o mecanismo de expressões regulares não pode corresponder ao padrão de
entrada junto com o início e o fim da cadeia de caracteres de entrada, nenhuma
correspondência é encontrada. Na segunda chamada do método, a expressão regular é
alterada para ^(\w+)\s(\d+)\r?$ e as opções são definidas para RegexOptions.Multiline.
Como a saída mostra, os nomes e pontuações são combinados com sucesso e as
pontuações são exibidas em ordem decrescente.

C#

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class Multiline1Example


{
public static void Main()
{
SortedList<int, string> scores = new SortedList<int, string>(new
DescendingComparer1<int>());

string input = "Joe 164\n" +


"Sam 208\n" +
"Allison 211\n" +
"Gwen 171\n";
string pattern = @"^(\w+)\s(\d+)$";
bool matched = false;

Console.WriteLine("Without Multiline option:");


foreach (Match match in Regex.Matches(input, pattern))
{
scores.Add(Int32.Parse(match.Groups[2].Value),
(string)match.Groups[1].Value);
matched = true;
}
if (!matched)
Console.WriteLine(" No matches.");
Console.WriteLine();

// Redefine pattern to handle multiple lines.


pattern = @"^(\w+)\s(\d+)\r*$";
Console.WriteLine("With multiline option:");
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.Multiline))
scores.Add(Int32.Parse(match.Groups[2].Value),
(string)match.Groups[1].Value);

// List scores in descending order.


foreach (KeyValuePair<int, string> score in scores)
Console.WriteLine("{0}: {1}", score.Value, score.Key);
}
}

public class DescendingComparer1<T> : IComparer<T>


{
public int Compare(T x, T y)
{
return Comparer<T>.Default.Compare(x, y) * -1;
}
}
// The example displays the following output:
// Without Multiline option:
// No matches.
//
// With multiline option:
// Allison: 211
// Sam: 208
// Gwen: 171
// Joe: 164

O padrão de expressão regular ^(\w+)\s(\d+)\r*$ é definido conforme mostrado na


tabela a seguir.

Padrão Descrição

^ Começar no início da linha.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Este é o primeiro grupo de


captura.

\s Corresponde a um caractere de espaço em branco.

(\d+) Corresponde a um ou mais dígitos decimais. Este é o segundo grupo de captura.

\r? Corresponder a zero ou um caractere de retorno de carro.

$ Terminar no fim da linha.


O exemplo a seguir é equivalente ao anterior, exceto que ele usa a opção embutida (?
m) para definir a opção de multilinhas.

C#

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

public class Multiline2Example


{
public static void Main()
{
SortedList<int, string> scores = new SortedList<int, string>(new
DescendingComparer<int>());

string input = "Joe 164\n" +


"Sam 208\n" +
"Allison 211\n" +
"Gwen 171\n";
string pattern = @"(?m)^(\w+)\s(\d+)\r*$";

foreach (Match match in Regex.Matches(input, pattern,


RegexOptions.Multiline))
scores.Add(Convert.ToInt32(match.Groups[2].Value),
match.Groups[1].Value);

// List scores in descending order.


foreach (KeyValuePair<int, string> score in scores)
Console.WriteLine("{0}: {1}", score.Value, score.Key);
}
}

public class DescendingComparer<T> : IComparer<T>


{
public int Compare(T x, T y)
{
return Comparer<T>.Default.Compare(x, y) * -1;
}
}
// The example displays the following output:
// Allison: 211
// Sam: 208
// Gwen: 171
// Joe: 164

Modo de linha única


A opção RegexOptions.Singleline, ou a opção embutida s , faz o mecanismo de
expressão regular tratar a cadeia de caracteres de entrada como se consistisse em uma
única linha. Ele faz isso mudando o comportamento do elemento de linguagem de
ponto ( . ) para que corresponda a todos os caracteres, em vez de corresponder a todo
caractere exceto pelo caractere de nova linha \n .

O exemplo a seguir ilustra como o comportamento do elemento de linguagem . muda


quando se usa a opção RegexOptions.Singleline. A expressão regular ^.+ começa no
início da cadeia de caracteres e corresponde a todos os caracteres. Por padrão, a
correspondência termina no final da primeira linha; o padrão de expressão regular
corresponde ao caractere de retorno de carro \r , mas não corresponde a \n . Como a
opção RegexOptions.Singleline interpreta toda a cadeia de caracteres de entrada como
uma única linha, ela corresponde a cada caractere na cadeia de caracteres de entrada,
incluindo \n .

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "^.+";
string input = "This is one line and" + Environment.NewLine + "this is
the second.";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(Regex.Escape(match.Value));

Console.WriteLine();
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.Singleline))
Console.WriteLine(Regex.Escape(match.Value));
}
}
// The example displays the following output:
// This\ is\ one\ line\ and\r
//
// This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.

O exemplo a seguir é equivalente ao anterior, exceto que ele usa a opção embutida (?
s) para habilitar o modo de linha única.

C#

using System;
using System.Text.RegularExpressions;

public class SingleLineExample


{
public static void Main()
{
string pattern = "(?s)^.+";
string input = "This is one line and" + Environment.NewLine + "this
is the second.";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine(Regex.Escape(match.Value));
}
}
// The example displays the following output:
// This\ is\ one\ line\ and\r\nthis\ is\ the\ second\.

Apenas capturas explícitas


Por padrão, grupos de capturas são definidos pelo uso de parênteses no padrão de
expressão regular. Grupos nomeados recebem um nome ou número pela opção de
linguagem (?< nome > subexpressão ) , enquanto grupos não nomeados são acessíveis
pelo índice. No objeto GroupCollection, grupos não nomeados precedem grupos
nomeados.

Constructos de agrupamento costumam ser usados apenas para aplicar quantificadores


a vários elementos de linguagem; as subcadeias de caracteres capturadas não são de
interesse. Por exemplo, se a seguinte expressão regular:

\b\(?((\w+),?\s?)+[\.!?]\)?

for feita somente para extrair frases que terminem com um ponto, ponto de exclamação
ou ponto de interrogação de um documento, apenas a frase resultante (representada
pelo objeto Match) é de interesse. As palavras individuais na coleção não são.

Capturar grupos que não serão usados posteriormente pode ser caro, pois o mecanismo
de expressão regular precisa preencher os objetos de coleção GroupCollection e
CaptureCollection. Como alternativa, você pode usar a opção
RegexOptions.ExplicitCapture ou a opção embutida n para especificar que apenas as
capturas válidas são explicitamente nomeadas ou grupos numerados que são
projetados pelo constructo (?< nome > subexpressão ) .

O exemplo a seguir exibe informações sobre as correspondências retornadas pelo


padrão de expressão regular \b\(?((\w+),?\s?)+[\.!?]\)? quando o método Match é
chamado com e sem a opção RegexOptions.ExplicitCapture. Como mostra a saída da
chamada do primeiro método, o mecanismo de expressão regular preenche totalmente
os objetos da coleção GroupCollection e CaptureCollection com informações sobre
subcadeias de caracteres capturadas. Como o segundo método é chamado com
options definido para RegexOptions.ExplicitCapture, ele não captura informações sobre
grupos.

C#

using System;
using System.Text.RegularExpressions;

public class Explicit1Example


{
public static void Main()
{
string input = "This is the first sentence. Is it the beginning " +
"of a literary masterpiece? I think not. Instead, " +
"it is a nonsensical paragraph.";
string pattern = @"\b\(?((?>\w+),?\s?)+[\.!?]\)?";
Console.WriteLine("With implicit captures:");
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine("The match: {0}", match.Value);
int groupCtr = 0;
foreach (Group group in match.Groups)
{
Console.WriteLine(" Group {0}: {1}", groupCtr,
group.Value);
groupCtr++;
int captureCtr = 0;
foreach (Capture capture in group.Captures)
{
Console.WriteLine(" Capture {0}: {1}", captureCtr,
capture.Value);
captureCtr++;
}
}
}
Console.WriteLine();
Console.WriteLine("With explicit captures only:");
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.ExplicitCapture))
{
Console.WriteLine("The match: {0}", match.Value);
int groupCtr = 0;
foreach (Group group in match.Groups)
{
Console.WriteLine(" Group {0}: {1}", groupCtr,
group.Value);
groupCtr++;
int captureCtr = 0;
foreach (Capture capture in group.Captures)
{
Console.WriteLine(" Capture {0}: {1}", captureCtr,
capture.Value);
captureCtr++;
}
}
}
}
}
// The example displays the following output:
// With implicit captures:
// The match: This is the first sentence.
// Group 0: This is the first sentence.
// Capture 0: This is the first sentence.
// Group 1: sentence
// Capture 0: This
// Capture 1: is
// Capture 2: the
// Capture 3: first
// Capture 4: sentence
// Group 2: sentence
// Capture 0: This
// Capture 1: is
// Capture 2: the
// Capture 3: first
// Capture 4: sentence
// The match: Is it the beginning of a literary masterpiece?
// Group 0: Is it the beginning of a literary masterpiece?
// Capture 0: Is it the beginning of a literary masterpiece?
// Group 1: masterpiece
// Capture 0: Is
// Capture 1: it
// Capture 2: the
// Capture 3: beginning
// Capture 4: of
// Capture 5: a
// Capture 6: literary
// Capture 7: masterpiece
// Group 2: masterpiece
// Capture 0: Is
// Capture 1: it
// Capture 2: the
// Capture 3: beginning
// Capture 4: of
// Capture 5: a
// Capture 6: literary
// Capture 7: masterpiece
// The match: I think not.
// Group 0: I think not.
// Capture 0: I think not.
// Group 1: not
// Capture 0: I
// Capture 1: think
// Capture 2: not
// Group 2: not
// Capture 0: I
// Capture 1: think
// Capture 2: not
// The match: Instead, it is a nonsensical paragraph.
// Group 0: Instead, it is a nonsensical paragraph.
// Capture 0: Instead, it is a nonsensical paragraph.
// Group 1: paragraph
// Capture 0: Instead,
// Capture 1: it
// Capture 2: is
// Capture 3: a
// Capture 4: nonsensical
// Capture 5: paragraph
// Group 2: paragraph
// Capture 0: Instead
// Capture 1: it
// Capture 2: is
// Capture 3: a
// Capture 4: nonsensical
// Capture 5: paragraph
//
// With explicit captures only:
// The match: This is the first sentence.
// Group 0: This is the first sentence.
// Capture 0: This is the first sentence.
// The match: Is it the beginning of a literary masterpiece?
// Group 0: Is it the beginning of a literary masterpiece?
// Capture 0: Is it the beginning of a literary masterpiece?
// The match: I think not.
// Group 0: I think not.
// Capture 0: I think not.
// The match: Instead, it is a nonsensical paragraph.
// Group 0: Instead, it is a nonsensical paragraph.
// Capture 0: Instead, it is a nonsensical paragraph.

O padrão de expressão regular \b\(?((?>\w+),?\s?)+[\.!?]\)? é definido como mostra


a tabela a seguir.

Padrão Descrição

\b Começar em um limite de palavra.

\(? Corresponder zero ou uma ocorrência do parêntese de abertura (“(“).

(?>\w+),? Corresponder um ou mais caracteres de palavra seguidos por zero ou uma vírgula.
Não retroceda ao corresponder caracteres de palavra.

\s? Corresponder a zero ou a um caractere de espaço em branco.

((\w+),? Corresponder a combinação de um ou mais caracteres de palavra, zero ou mais


\s?)+ vírgulas e zero ou um caractere de espaço em branco uma ou mais vezes.

[\.!?]\)? Corresponder qualquer um dos três símbolos de pontuação seguidos por zero ou
um parêntese de fechamento (“)”).
Você também pode usar o elemento embutido (?n) para suprimir capturas
automáticas. O exemplo a seguir modifica o padrão de expressão regular anterior para
usar o elemento embutido (?n) em vez da opção RegexOptions.ExplicitCapture.

C#

using System;
using System.Text.RegularExpressions;

public class Explicit2Example


{
public static void Main()
{
string input = "This is the first sentence. Is it the beginning " +
"of a literary masterpiece? I think not. Instead, " +
"it is a nonsensical paragraph.";
string pattern = @"(?n)\b\(?((?>\w+),?\s?)+[\.!?]\)?";

foreach (Match match in Regex.Matches(input, pattern))


{
Console.WriteLine("The match: {0}", match.Value);
int groupCtr = 0;
foreach (Group group in match.Groups)
{
Console.WriteLine(" Group {0}: {1}", groupCtr,
group.Value);
groupCtr++;
int captureCtr = 0;
foreach (Capture capture in group.Captures)
{
Console.WriteLine(" Capture {0}: {1}", captureCtr,
capture.Value);
captureCtr++;
}
}
}
}
}
// The example displays the following output:
// The match: This is the first sentence.
// Group 0: This is the first sentence.
// Capture 0: This is the first sentence.
// The match: Is it the beginning of a literary masterpiece?
// Group 0: Is it the beginning of a literary masterpiece?
// Capture 0: Is it the beginning of a literary masterpiece?
// The match: I think not.
// Group 0: I think not.
// Capture 0: I think not.
// The match: Instead, it is a nonsensical paragraph.
// Group 0: Instead, it is a nonsensical paragraph.
// Capture 0: Instead, it is a nonsensical paragraph.
Por fim, é possível usar o elemento do grupo embutido (?n:) para suprimir capturas
automáticas grupo a grupo. O exemplo a seguir modifica o padrão anterior para
suprimir capturas sem nome no grupo externo, ((?>\w+),?\s?) . Observe que isso
também suprime capturas sem nome no grupo interno.

C#

using System;
using System.Text.RegularExpressions;

public class Explicit3Example


{
public static void Main()
{
string input = "This is the first sentence. Is it the beginning " +
"of a literary masterpiece? I think not. Instead, " +
"it is a nonsensical paragraph.";
string pattern = @"\b\(?(?n:(?>\w+),?\s?)+[\.!?]\)?";

foreach (Match match in Regex.Matches(input, pattern))


{
Console.WriteLine("The match: {0}", match.Value);
int groupCtr = 0;
foreach (Group group in match.Groups)
{
Console.WriteLine(" Group {0}: {1}", groupCtr,
group.Value);
groupCtr++;
int captureCtr = 0;
foreach (Capture capture in group.Captures)
{
Console.WriteLine(" Capture {0}: {1}", captureCtr,
capture.Value);
captureCtr++;
}
}
}
}
}
// The example displays the following output:
// The match: This is the first sentence.
// Group 0: This is the first sentence.
// Capture 0: This is the first sentence.
// The match: Is it the beginning of a literary masterpiece?
// Group 0: Is it the beginning of a literary masterpiece?
// Capture 0: Is it the beginning of a literary masterpiece?
// The match: I think not.
// Group 0: I think not.
// Capture 0: I think not.
// The match: Instead, it is a nonsensical paragraph.
// Group 0: Instead, it is a nonsensical paragraph.
// Capture 0: Instead, it is a nonsensical paragraph.
Expressões regulares compiladas

7 Observação

Sempre que possível, use expressões regulares geradas pela origem em vez de
compilar expressões regulares usando a opção RegexOptions.Compiled. A geração
de origem pode ajudar seu aplicativo a iniciar com mais rapidez, ser executado com
mais agilidade e ser mais completo. Para saber quando a geração de origem é
possível, confira Quando usá-la.

Por padrão, as expressões regulares no .NET são interpretadas. Quando um objeto


Regex é instanciado ou um método Regex estático é chamado, o padrão de expressão
regular é analisado em um conjunto de opcodes personalizados, e um interpretador usa
esses opcodes para executar a expressão regular. Isso envolve uma troca: o custo de
inicializar o mecanismo de expressões regulares é minimizado com prejuízo do
desempenho do tempo de execução.

Você pode usar expressões regulares compiladas, em vez de interpretadas, usando a


opção RegexOptions.Compiled. Neste caso, quando um padrão é enviado ao
mecanismo de expressões regulares, ele é analisado em um subconjunto de opcodes e
convertido para a MSIL (linguagem intermediária da Microsoft), que pode ser enviada
diretamente ao Common Language Runtime. Expressões regulares compiladas
maximizam o desempenho do tempo de execução às custas do tempo de inicialização.

7 Observação

Uma expressão regular só pode ser compilada fornecendo o valor


RegexOptions.Compiled ao parâmetro options de um construtor de classe Regex
ou um método de correspondência padrão estático. Não está disponível como uma
opção embutida.

É possível usar expressões regulares compiladas em chamadas para expressões


regulares estáticas e de instância. Em expressões regulares estáticas, a opção
RegexOptions.Compiled é enviada ao parâmetro options do método de
correspondência padrão de expressão regular. Em expressões regulares de instância, é
enviado ao parâmetro options do construtor de classe Regex. Em ambos os casos,
resulta em melhoria de desempenho.

Porém, essa melhoria de desempenho ocorre apenas sob as seguintes condições:


É usado um objeto Regex que representa uma expressão regular em particular, em
várias chamadas para métodos de correspondência padrão de expressão regular.

O objeto Regex não pode sair do escopo, então pode ser reutilizado.

Uma expressão regular estática é usada em várias chamadas para métodos de


correspondência padrão de expressão regular. (A melhoria de desempenho é
possível porque as expressões regulares usadas em chamadas de método estático
são armazenadas em cache pelo mecanismo de expressões regulares.)

7 Observação

A opção RegexOptions.Compiled não está relacionada ao método


Regex.CompileToAssembly, que cria um assembly de uso especial contendo
expressões regulares compiladas predefinidas.

Ignorar espaço em branco


Por padrão, o espaço em branco em um padrão de expressão regular é significativo; ele
força o mecanismo de expressões regulares para combinar um caractere de espaço em
branco na cadeia de caracteres de entrada. Devido a isso, a expressão regular " \b\w+\s "
e " \b\w+ " são, de um modo geral, equivalentes. Além disso, quando a tecla de cerquilha
(#) é encontrada em um padrão de expressão regular, ela é interpretada como um
caractere literal a ser combinado.

A opção RegexOptions.IgnorePatternWhitespace ou a opção embutida x muda seu


comportamento padrão da seguinte maneira:

É ignorado o espaço em branco sem escape no padrão de expressão regular. Para


fazer parte de um padrão de expressão regular, os caracteres de espaço em branco
devem ser escapados (por exemplo, \s ou " \ ").

A tecla de cerquilha (#) é interpretada como o início de um comentário, em vez de


um caractere literal. Todo o texto no padrão de expressão regular do caractere #
até o próximo caractere \n ou até o fim da cadeia de caracteres é interpretado
como comentário.

No entanto, nos casos a seguir, os caracteres de espaço em branco em uma expressão


regular não serão ignorados, mesmo se você usar a opção
RegexOptions.IgnorePatternWhitespace:
O espaço em branco dentro de uma classe de caractere sempre é interpretado
literalmente. Por exemplo, o padrão da expressão regular [ .,;:] corresponde a
qualquer caractere de espaço em branco, ponto, vírgula, dois pontos ou ponto e
vírgula único.

O espaço em branco não é permitido dentro de um quantificador entre colchetes,


como { n } , { n ,} e { n , m } . Por exemplo, o padrão de expressão regular \d{1,
3} falha ao corresponder quaisquer sequências de dígitos de um a três dígitos
porque contém um caractere de espaço em branco.

Não é permitido espaço em branco dentro da sequência de caracteres que


introduz um elemento de linguagem. Por exemplo:

O elemento de linguagem (?: subexpressão ) representa um grupo sem


captura; a parte (?: do elemento não pode ter espaços inseridos. O padrão (?
: subexpressão ) lança uma ArgumentException no tempo de execução porque

o mecanismo de expressão regular não consegue analisar o padrão, e o padrão


( ?: subexpressão ) falha em corresponder a subexpressão.

O elemento de linguagem \p{ name } , que representa uma categoria Unicode


ou um bloco nomeado, não pode incluir espaços inseridos na parte \p{ do
elemento. Se você incluir um espaço em branco, o elemento lança uma
ArgumentException no tempo de execução.

Habilitar essa opção ajuda a simplificar expressões regulares que costumam ser difíceis
de analisar e entender. Melhora a legibilidade e torna possível documentar uma
expressão regular.

O exemplo a seguir define o padrão de expressão regular a seguir:

\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? # Matches an entire sentence.

Esse padrão é similar ao padrão definido na seção Apenas capturas explícitas, exceto
por usar a opção RegexOptions.IgnorePatternWhitespace para ignorar espaço em
branco de padrão.

C#

using System;
using System.Text.RegularExpressions;

public class Whitespace1Example


{
public static void Main()
{
string input = "This is the first sentence. Is it the beginning " +
"of a literary masterpiece? I think not. Instead, " +
"it is a nonsensical paragraph.";
string pattern = @"\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? # Matches an
entire sentence.";

foreach (Match match in Regex.Matches(input, pattern,


RegexOptions.IgnorePatternWhitespace))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// This is the first sentence.
// Is it the beginning of a literary masterpiece?
// I think not.
// Instead, it is a nonsensical paragraph.

O exemplo a seguir usa a opção embutida (?x) para ignorar o espaço em branco
padrão.

C#

using System;
using System.Text.RegularExpressions;

public class Whitespace2Example


{
public static void Main()
{
string input = "This is the first sentence. Is it the beginning " +
"of a literary masterpiece? I think not. Instead, " +
"it is a nonsensical paragraph.";
string pattern = @"(?x)\b \(? ( (?>\w+) ,?\s? )+ [\.!?] \)? #
Matches an entire sentence.";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// This is the first sentence.
// Is it the beginning of a literary masterpiece?
// I think not.
// Instead, it is a nonsensical paragraph.

Modo da direita para a esquerda


Por padrão, o mecanismo de expressões regulares pesquisa da esquerda para a direita. É
possível reverter a direção de pesquisa usando a opção RegexOptions.RightToLeft. A
pesquisa da direita para esquerda inicia automaticamente na última posição de
caractere da cadeia de caracteres. Para métodos de correspondência padrão que
incluem um parâmetro de posição inicial, como Regex.Match(String, Int32), a posição
inicial especificada é o índice da posição do caractere mais à direita em que a pesquisa
deve iniciar.

7 Observação

O modo de padrão da direita para a esquerda está disponível apenas fornecendo o


valor RegexOptions.RightToLeft para o parâmetro options de um construtor de
classe Regex ou o método de correspondência padrão estático. Não está
disponível como uma opção embutida.

Exemplo
A expressão regular \bb\w+\s corresponde palavras com dois ou mais caracteres que
começam com a letra “b” e são seguidas por um caractere de espaço em branco. No
exemplo a seguir, a cadeia de caracteres de entrada consiste em três palavras que
incluem um ou mais caracteres “b”. A primeira e a segunda palavras começam com "b",
e a terceira palavra termina com "b". Como mostra a saída do exemplo de pesquisa da
direita para a esquerda, apenas a primeira e a segunda palavras correspondem ao
padrão de expressão regular, com a segunda palavra sendo correspondida primeiro.

C#

using System;
using System.Text.RegularExpressions;

public class RTL1Example


{
public static void Main()
{
string pattern = @"\bb\w+\s";
string input = "build band tab";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.RightToLeft))
Console.WriteLine("'{0}' found at position {1}.", match.Value,
match.Index);
}
}
// The example displays the following output:
// 'band ' found at position 6.
// 'build ' found at position 0.
Ordem de avaliação
A opção RegexOptions.RightToLeft altera a direção da pesquisa e também inverte a
ordem na qual o padrão de expressão regular é avaliado. Em uma pesquisa da direita
para a esquerda, o padrão de pesquisa é lido da direita para a esquerda. Essa distinção é
importante porque pode afetar coisas como grupos de captura e referências inversas.
Por exemplo, a expressão Regex.Match("abcabc", @"\1(abc)",
RegexOptions.RightToLeft) encontra uma correspondência abcabc , mas em uma
pesquisa da esquerda para a direita ( Regex.Match("abcabc", @"\1(abc)",
RegexOptions.None) ), nenhuma correspondência é encontrada. Isso ocorre porque o

elemento (abc) deve ser avaliado antes do elemento de grupo de captura numerado
( \1 ) para que uma correspondência seja encontrada.

Asserções lookbehind e lookahead


O local de uma correspondência para uma asserção lookahead ( (?=subexpression) ) ou
lookbehind ( (?<=subexpression) ) não muda em uma pesquisa da direita para a
esquerda. As asserções lookahead olham para a direita do local da correspondência
atual; as asserções lookbehind olham para a esquerda do local da correspondência
atual.

 Dica

Se uma pesquisa é da direita para a esquerda ou não, os lookbehinds são


implementados usando uma pesquisa da direita para a esquerda começando no
local da correspondência atual.

Por exemplo, a expressão regular (?<=\d{1,2}\s)\w+,\s\d{4} usa a asserção lookbehind


para testar uma data que antecede um nome de mês. A expressão regular corresponde
ao mês e o ano. Saiba mais sobre as asserções lookahead e lookbehind em Constructos
de agrupamento.

C#

using System;
using System.Text.RegularExpressions;

public class RTL2Example


{
public static void Main()
{
string[] inputs = { "1 May, 1917", "June 16, 2003" };
string pattern = @"(?<=\d{1,2}\s)\w+,\s\d{4}";

foreach (string input in inputs)


{
Match match = Regex.Match(input, pattern,
RegexOptions.RightToLeft);
if (match.Success)
Console.WriteLine("The date occurs in {0}.", match.Value);
else
Console.WriteLine("{0} does not match.", input);
}
}
}

// The example displays the following output:


// The date occurs in May, 1917.
// June 16, 2003 does not match.

O padrão de expressão regular é definido como mostra a tabela a seguir.

Padrão Descrição

(? O início da correspondência deve ser antecedido por um ou dois dígitos decimais


<=\d{1,2}\s) seguidos por um espaço.

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

, Corresponda um caractere de vírgula.

\s Corresponde a um caractere de espaço em branco.

\d{4} Corresponder a quatro dígitos decimais.

Comportamento de correspondência de
ECMAScript
Por padrão, o mecanismo de expressões regulares usa comportamento canônico ao
corresponder um padrão de expressão regular a um texto de entrada. Porém, é possível
instruir o mecanismo de expressão regular a usar o comportamento de correspondência
ECMAScript especificando a opção RegexOptions.ECMAScript.

7 Observação

O comportamento compatível com ECMAScript está disponível apenas fornecendo


o valor RegexOptions.ECMAScript para o parâmetro options de um construtor de
classe Regex ou método de correspondência padrão estático. Não está disponível
como uma opção embutida.

A opção RegexOptions.ECMAScript só pode ser combinada com as opções


RegexOptions.IgnoreCase e RegexOptions.Multiline. O uso de qualquer outra opção em
uma expressão regular resulta em ArgumentOutOfRangeException.

O comportamento das expressões regulares ECMAScript e canônicas difere em três


áreas: sintaxe da classe de caractere, grupos de captura autorreferidos e interpretação
octal versus de referência inversa.

Sintaxe da classe de caractere. Como as expressões regulares canônicas dão


suporte a Unicode e o ECMAScrip não, as classes de caractere no ECMAScrip têm
uma sintaxe mais limitada e alguns elementos de linguagem da classe de caractere
têm um significado diferente. Por exemplo, o ECMAScript não oferece suporte a
elementos de linguagem como a categoria Unicode ou elementos de bloco \p e
\P . De modo similar, o elemento \w , que corresponde um caractere de palavra, é
equivalente à classe de caractere [a-zA-Z_0-9] ao usar ECMAScript e
[\p{Ll}\p{Lu}\p{Lt}\p{Lo}\p{Nd}\p{Pc}\p{Lm}] ao usar comportamento canônico.

Para saber mais, confira Classes de caracteres.

O exemplo a seguir ilustra a diferença entre correspondência padrão ECMAScript e


canônica. Define uma expressão regular, \b(\w+\s*)+ , que combina palavras
seguidas por caracteres de espaço em branco. A entrada consiste em duas cadeias
de caracteres, uma que usa o conjunto de caracteres latinos e outra que usa o
conjunto de caracteres cirílicos. Como a saída mostra, a chamada para o método
Regex.IsMatch(String, String, RegexOptions) que usa correspondência ECMAScript
falha ao combinar as palavras cirílicas, enquanto a chamada de método que usa
correspondência canônica não corresponde essas palavras.

C#

using System;
using System.Text.RegularExpressions;

public class EcmaScriptExample


{
public static void Main()
{
string[] values = { "целый мир", "the whole world" };
string pattern = @"\b(\w+\s*)+";
foreach (var value in values)
{
Console.Write("Canonical matching: ");
if (Regex.IsMatch(value, pattern))
Console.WriteLine("'{0}' matches the pattern.", value);
else
Console.WriteLine("{0} does not match the pattern.",
value);

Console.Write("ECMAScript matching: ");


if (Regex.IsMatch(value, pattern, RegexOptions.ECMAScript))
Console.WriteLine("'{0}' matches the pattern.", value);
else
Console.WriteLine("{0} does not match the pattern.",
value);
Console.WriteLine();
}
}
}
// The example displays the following output:
// Canonical matching: 'целый мир' matches the pattern.
// ECMAScript matching: целый мир does not match the pattern.
//
// Canonical matching: 'the whole world' matches the pattern.
// ECMAScript matching: 'the whole world' matches the pattern.

Grupos de capturas com autorreferência. Uma classe de captura de expressão


regular com uma referência inversa para si mesma deve ser atualizada com cada
iteração de captura. Como o exemplo a seguir mostra, esse recurso habilita a
expressão regular ((a+)(\1) ?)+ para corresponder a cadeia de caracteres de
entrada “ aa aaaa aaaaaa ” ao usar ECMAScript, mas não ao usar correspondência
canônica.

C#

using System;
using System.Text.RegularExpressions;

public class EcmaScript2Example


{
static string pattern;

public static void Main()


{
string input = "aa aaaa aaaaaa ";
pattern = @"((a+)(\1) ?)+";

// Match input using canonical matching.


AnalyzeMatch(Regex.Match(input, pattern));

// Match input using ECMAScript.


AnalyzeMatch(Regex.Match(input, pattern,
RegexOptions.ECMAScript));
}
private static void AnalyzeMatch(Match m)
{
if (m.Success)
{
Console.WriteLine("'{0}' matches {1} at position {2}.",
pattern, m.Value, m.Index);
int grpCtr = 0;
foreach (Group grp in m.Groups)
{
Console.WriteLine(" {0}: '{1}'", grpCtr, grp.Value);
grpCtr++;
int capCtr = 0;
foreach (Capture cap in grp.Captures)
{
Console.WriteLine(" {0}: '{1}'", capCtr,
cap.Value);
capCtr++;
}
}
}
else
{
Console.WriteLine("No match found.");
}
Console.WriteLine();
}
}
// The example displays the following output:
// No match found.
//
// '((a+)(\1) ?)+' matches aa aaaa aaaaaa at position 0.
// 0: 'aa aaaa aaaaaa '
// 0: 'aa aaaa aaaaaa '
// 1: 'aaaaaa '
// 0: 'aa '
// 1: 'aaaa '
// 2: 'aaaaaa '
// 2: 'aa'
// 0: 'aa'
// 1: 'aa'
// 2: 'aa'
// 3: 'aaaa '
// 0: ''
// 1: 'aa '
// 2: 'aaaa '

A expressão regular é definida como mostrado na tabela a seguir.

Padrão Descrição

(a+) Corresponda a letra "a" uma ou mais vezes. Este é o segundo grupo de captura.
Padrão Descrição

(\1) Corresponda a subcadeia de caracteres capturada pelo primeiro grupo de


captura. Este é o terceiro grupo de captura.

? Corresponda zero ou um caractere de espaço.

((a+) Corresponda o padrão de um ou mais caracteres "a" seguidos por uma cadeia de
(\1) ?)+ caracteres que corresponda o primeiro grupo de capturas seguido por zero ou
um caractere de espaço uma ou mais vezes. Este é o primeiro grupo de captura.

Resolução de ambiguidades entre escapes octais e referências inversas. A tabela a


seguir resume as diferenças na interpretação octal versus de referência inversa por
expressões regulares canônicas e ECMAScript.

Expressão Comportamento canônico Comportamento de ECMAScript


regular

\0 Interprete como um octal. Por exemplo, Mesmo comportamento.


seguido \044 é sempre interpretado como um
por 0 - 2 valor octal e significa "$".
dígitos
octais

\ seguido Interprete como referência inversa. Por Se existir um grupo de capturas de


por um exemplo, \9 sempre significa referência um único dígito decimal, faça
dígito de inversa 9, mesmo que um nono grupo referência inversa a esse dígito.
1 a 9, de capturas não exista. Se o grupo de Caso contrário, interprete o valor
seguido captura não existir, o analisador de como literal.
por expressão regular lança uma
nenhum ArgumentException.
dígito
decimal
adicional,
Expressão Comportamento canônico Comportamento de ECMAScript
regular

\ seguido Interprete os dígitos como um valor Interprete como uma referência


por um decimal. Se o grupo de capturas existir, inversa convertendo todos os
dígito de interprete a expressão como referência dígitos possíveis para um valor
1 a 9, inversa. decimal que pode fazer referência
seguido a uma captura. Se nenhum dígito
por Caso contrário, interprete os dígitos puder ser convertido, interprete
dígitos octais iniciais até o octal 377; ou seja, como um octal usando os dígitos
decimais considere apenas 8 bits inferiores do octais iniciais até o octal 377;
adicionais valor. Interprete os dígitos restantes interprete os dígitos restantes
como literais. Por exemplo, na expressão como literais.
\3000 , se o grupo de capturas 300
existir, interprete como referência inversa
300; se o grupo de capturas 300 não
existir, interprete como um octal 300
seguido por 0.

Comparar usando a cultura invariável


Por padrão, quando o mecanismo de expressão regular realiza comparações sem
diferenciar maiúsculas de minúsculas, ele usa as convenções de maiúsculas de
minúsculas da cultura atual para determinar caracteres equivalentes em maiúsculas e
minúsculas.

Porém, esse comportamento é indesejável para alguns tipos de comparações,


especialmente ao comparar a entrada do usuário com os nomes de recursos do sistema,
como senhas, arquivos ou URLs. O exemplo a seguir ilustra esse cenário. O código tem
como objetivo bloquear o acesso a qualquer recurso cuja URL seja prefixada com
FILE://. A expressão regular tenta uma correspondência sem diferenciar maiúsculas e
minúsculas com a cadeia de caracteres usando a expressão regular $FILE:// . No
entanto, quando a cultura do sistema atual é tr-TR (Turco – Turquia ), "I" não é o
equivalente maiúsculo de "i". Como resultado, a chamada para o método Regex.IsMatch
retorna false e o acesso ao arquivo é permitido.

C#

CultureInfo defaultCulture = Thread.CurrentThread.CurrentCulture;


Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");

string input = "file://c:/Documents.MyReport.doc";


string pattern = "FILE://";

Console.WriteLine("Culture-sensitive matching ({0} culture)...",


Thread.CurrentThread.CurrentCulture.Name);
if (Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
Console.WriteLine("URLs that access files are not allowed.");
else
Console.WriteLine("Access to {0} is allowed.", input);

Thread.CurrentThread.CurrentCulture = defaultCulture;
// The example displays the following output:
// Culture-sensitive matching (tr-TR culture)...
// Access to file://c:/Documents.MyReport.doc is allowed.

7 Observação

Para obter mais informações sobre comparações de cadeias de caracteres que


diferenciam maiúsculas de minúsculas e usam cultura invariável, consulte Práticas
recomendadas para o uso de cadeias de caracteres.

Em vez de usar comparações que não diferenciam maiúsculas de minúsculas da cultura


atual, é possível especificar a opção RegexOptions.CultureInvariant para ignorar
diferenças culturais no idioma e usar as convenções da cultura invariável.

7 Observação

A comparação usando cultura invariável está disponível apenas fornecendo o valor


RegexOptions.CultureInvariant para o parâmetro options de um construtor de
classe Regex ou método de correspondência padrão estático. Não está disponível
como uma opção embutida.

O exemplo a seguir é idêntico ao anterior, exceto que o método estático


Regex.IsMatch(String, String, RegexOptions) é chamado com opções que incluem
RegexOptions.CultureInvariant. Mesmo quando a cultura atual está definida como turco
(Turquia), o mecanismo de expressão regular é capaz de corresponder com sucesso
"FILE" e "file" e bloquear o acesso ao recurso do arquivo.

C#

CultureInfo defaultCulture = Thread.CurrentThread.CurrentCulture;


Thread.CurrentThread.CurrentCulture = new CultureInfo("tr-TR");

string input = "file://c:/Documents.MyReport.doc";


string pattern = "FILE://";

Console.WriteLine("Culture-insensitive matching...");
if (Regex.IsMatch(input, pattern,
RegexOptions.IgnoreCase | RegexOptions.CultureInvariant))
Console.WriteLine("URLs that access files are not allowed.");
else
Console.WriteLine("Access to {0} is allowed.", input);

Thread.CurrentThread.CurrentCulture = defaultCulture;
// The example displays the following output:
// Culture-insensitive matching...
// URLs that access files are not allowed.

Modo sem rastreamento inverso


Por padrão, o mecanismo regex do .NET usa o rastreamento inverso para tentar
encontrar correspondências de padrão. Um mecanismo de rastreamento inverso é
aquele que tenta fazer a correspondência com um padrão e, se isso falha, volta e tenta
fazer a correspondência com um padrão alternativo, e assim por diante. O mecanismo
de rastreamento inverso é muito rápido para casos típicos, mas desacelera à medida
que o número de alternâncias de padrão aumenta, o que pode levar a rastreamentos
inversos catastróficos. A opção RegexOptions.NonBacktracking não usa o rastreamento
inverso e evita esse pior cenário. A meta dela é proporcionar um comportamento
consistentemente bom, qualquer que seja a entrada pesquisada.

A opção RegexOptions.NonBacktracking não dá suporte a tudo o que os outros


mecanismos internos dão. Em particular, ela não pode ser usada em conjunto com
RegexOptions.RightToLeft ou RegexOptions.ECMAScript. Também não permite os
seguintes constructos no padrão:

Grupos atômicos
Referências inversas
Grupos de balanceamento
Condicionais
Soluções alternativas
Âncoras de início ( \G )

RegexOptions.NonBacktracking também tem uma diferença sutil em relação à execução.


Se um grupo de captura estiver em um loop, a maioria dos mecanismos regex (não
.NET) fornecerá apenas o último valor correspondente para essa captura. No entanto, o
mecanismo regex do .NET rastreia todos os valores capturados dentro de um loop e
fornece acesso a eles. A opção RegexOptions.NonBacktracking é como a maioria das
outras implementações regex e só dá suporte ao fornecimento da captura final.

Confira também
Linguagem de expressões regulares – referência rápida
Construtores diversos em expressões
regulares
Artigo • 09/05/2023

As expressões regulares em .NET incluem três constructos diversos de linguagem. Um


deles permite habilitar ou desabilitar opções específicas de correspondência no meio de
um padrão de expressão regular. Os dois restantes permitem incluir comentários em
uma expressão regular.

Opções embutidas
É possível definir ou desabilitar opções específicas de correspondência de padrão para
parte de uma expressão regular mediante uso da sintaxe

(?imnsx-imnsx)

Você lista as opções que deseja habilitar após o ponto de interrogação e as opções que
deseja desabilitar após o sinal de subtração. A tabela a seguir descreve cada opção. Para
obter mais informações sobre cada opção, consulte Opções de expressões regulares.

Opção Descrição

i Correspondência sem diferenciação entre maiúsculas e minúsculas.

m Modo multilinha.

n Apenas capturas explícitas. (Parênteses não funcionam como grupos de captura.)

s Modo de linha única.

x Ignorar espaço em branco sem escape e permitir comentários no modo x.

Qualquer alteração nas opções de expressões regulares definida pelo constructo (?


imnsx-imnsx) permanece em vigor até o fim do grupo delimitador.

7 Observação

O constructo de agrupamento (?imnsx-imnsx: subexpression ) oferece uma


funcionalidade idêntica para uma subexpressão. Para saber mais, confira
Constructos de agrupamento.
O exemplo a seguir usa as opções i , n e x para desabilitar a diferenciação entre
maiúsculas e minúsculas e habilitar e capturas explícitas, bem como ignorar o espaço
em branco no padrão de expressão regular no meio de uma expressão regular.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern;
string input = "double dare double Double a Drooling dog The Dreaded
Deep";

pattern = @"\b(D\w+)\s(d\w+)\b";
// Match pattern using default options.
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine(match.Value);
if (match.Groups.Count > 1)
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
Console.WriteLine(" Group {0}: {1}", ctr,
match.Groups[ctr].Value);
}
Console.WriteLine();

// Change regular expression pattern to include options.


pattern = @"\b(D\w+)(?ixn) \s (d\w+) \b";
// Match new pattern with options.
foreach (Match match in Regex.Matches(input, pattern))
{
Console.WriteLine(match.Value);
if (match.Groups.Count > 1)
for (int ctr = 1; ctr < match.Groups.Count; ctr++)
Console.WriteLine(" Group {0}: '{1}'", ctr,
match.Groups[ctr].Value);
}
}
}
// The example displays the following output:
// Drooling dog
// Group 1: Drooling
// Group 2: dog
//
// Drooling dog
// Group 1: 'Drooling'
// Dreaded Deep
// Group 1: 'Dreaded'
O exemplo define duas expressões regulares. A primeira, \b(D\w+)\s(d\w+)\b ,
corresponde a duas palavras consecutivas que começam com um “D” maiúsculo e um
“d” minúsculo. A segunda expressão regular, \b(D\w+)(?ixn) \s (d\w+) \b , usa opções
embutidas para modificar esse padrão, conforme descrito na tabela a seguir. Uma
comparação dos resultados confirma o efeito do constructo (?ixn) .

Padrão Descrição

\b Iniciar em um limite de palavra.

(D\w+) Corresponder a um “D” maiúsculo seguido por um ou mais caracteres de palavra. Este é
o primeiro grupo de captura.

(?ixn) Desse ponto em diante, faça comparações sem distinção de maiúsculas e minúsculas,
faça apenas capturas explícitas e ignore o espaço em branco no padrão de expressão
regular.

\s Corresponde a um caractere de espaço em branco.

(d\w+) Corresponder a um “d” maiúsculo ou minúsculo seguido por um ou mais caracteres de


palavra. Este grupo não foi capturado porque a opção n (captura explícita) estava
habilitada.

\b Corresponder a um limite de palavra.

Comentário embutido
O constructo (?# comment ) permite incluir um comentário embutido em uma
expressão regular. O mecanismo de expressões regulares não usa nenhuma parte do
comentário na correspondência de padrão, apesar de o comentário estar incluído na
cadeia de caracteres que é retornada pelo método Regex.ToString. O comentário é
encerrado no primeiro caractere de fechar parênteses.

O exemplo a seguir repete o primeiro padrão de expressão regular do exemplo na


seção anterior. Ele adiciona dois comentários embutidos na expressão regular para
indicar se a comparação diferencia maiúsculas de minúsculas. O padrão de expressão
regular, \b((?# case-sensitive comparison)D\w+)\s(?ixn)((?#case-insensitive
comparison)d\w+)\b , é definido da seguinte forma.

Padrão Descrição

\b Iniciar em um limite de palavra.


Padrão Descrição

(?# case- Um comentário. Não afeta o comportamento de correspondência.


sensitive
comparison)

(D\w+) Corresponder a um “D” maiúsculo seguido por um ou mais caracteres de palavra.


Este é o primeiro grupo de captura.

\s Corresponde a um caractere de espaço em branco.

(?ixn) Desse ponto em diante, faça comparações sem distinção de maiúsculas e


minúsculas, faça apenas capturas explícitas e ignore o espaço em branco no
padrão de expressão regular.

(?#case- Um comentário. Não afeta o comportamento de correspondência.


insensitive
comparison)

(d\w+) Corresponder a um “d” maiúsculo ou minúsculo seguido por um ou mais


caracteres de palavra. Este é o segundo grupo de captura.

\b Corresponder a um limite de palavra.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b((?# case-sensitive comparison)D\w+)\s(?ixn)((?
#case-insensitive comparison)d\w+)\b";
Regex rgx = new Regex(pattern);
string input = "double dare double Double a Drooling dog The Dreaded
Deep";

Console.WriteLine("Pattern: " + pattern.ToString());


// Match pattern using default options.
foreach (Match match in rgx.Matches(input))
{
Console.WriteLine(match.Value);
if (match.Groups.Count > 1)
{
for (int ctr = 1; ctr <match.Groups.Count; ctr++)
Console.WriteLine(" Group {0}: {1}", ctr,
match.Groups[ctr].Value);
}
}
}
}
// The example displays the following output:
// Pattern: \b((?# case-sensitive comparison)D\w+)\s(?ixn)((?#case-
insensitive comp
// arison)d\w+)\b
// Drooling dog
// Group 1: Drooling
// Dreaded Deep
// Group 1: Dreaded

Comentário de final de linha


Um sinal numérico ( # ) marca um comentário do modo x, que começa com o caractere
# sem escape no final do padrão de expressão regular e continua até o final da linha.
Para usar este constructo, você deve habilitar a opção x (por meio de opções
embutidas) ou fornecer o valor RegexOptions.IgnorePatternWhitespace ao parâmetro
option ao instanciar o objeto Regex ou chamar um método estático Regex.

O exemplo a seguir ilustra o constructo do comentário de final de linha. Ele determina


se uma cadeia de caracteres é uma cadeia de caracteres de formato de composição que
inclui pelo menos um item de formato. A tabela a seguir descreve os constructos no
padrão de expressão regular:

\{\d+(,-*\d+)*(\:\w{1,4}?)*\}(?x) # Looks for a composite format item.

Padrão Descrição

\{ Corresponder a uma chave de abertura.

\d+ Corresponde a um ou mais dígitos decimais.

(,-*\d+)* Corresponder a zero ou a uma ocorrência de vírgula, seguida por um sinal


de subtração opcional, seguido por um ou mais dígitos decimais.

(\:\w{1,4}?)* Corresponder a zero ou a uma ocorrência de dois-pontos, seguido de um a


quatro caracteres em branco, mas o mínimo possível.

\} Corresponder a uma chave de fechamento.

(?x) Habilitar a opção de ignorar espaço em branco no padrão para o


comentário de final de linha ser reconhecido.

# Looks for a Um comentário de final de linha.


composite format
item.

C#
using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\{\d+(,-*\d+)*(\:\w{1,4}?)*\}(?x) # Looks for a
composite format item.";
string input = "{0,-3:F}";
Console.WriteLine("'{0}':", input);
if (Regex.IsMatch(input, pattern))
Console.WriteLine(" contains a composite format item.");
else
Console.WriteLine(" does not contain a composite format item.");
}
}
// The example displays the following output:
// '{0,-3:F}':
// contains a composite format item.

Observe que, em vez de fornecer o constructo (?x) na expressão regular, o comentário


pode também poderia ter sido reconhecido chamando o método Regex.IsMatch(String,
String, RegexOptions) e passando-o para o valor de enumeração
RegexOptions.IgnorePatternWhitespace.

Confira também
Linguagem de expressões regulares – referência rápida
Práticas recomendadas para expressões
regulares no .NET
Artigo • 07/10/2023

O mecanismo de expressões regulares no .NET é uma ferramenta poderosa e repleta de


recursos que processa o texto com base em correspondências de padrões em vez de em
comparar e corresponder o texto literal. Na maioria dos casos, ele realiza a
correspondência de padrões de forma rápida e eficiente. No entanto, em alguns casos,
o mecanismo de expressões regulares pode parecer ser muito lento. Em casos extremos,
pode até mesmo parecer parar de responder enquanto processa uma entrada
relativamente pequena em um período de horas ou até mesmo dias.

Este artigo descreve algumas das práticas recomendadas que os desenvolvedores


podem adotar para garantir que as expressões regulares obtenham o máximo de
desempenho.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

Considere a fonte de entrada


Em geral, as expressões regulares podem aceitar dois tipos de entrada: restrita e não
restrita. Uma entrada restrita é o texto proveniente de uma fonte conhecida ou confiável
e segue um formato predefinido. Uma entrada irrestrita é um texto proveniente de uma
fonte não confiável como um usuário web e que não pode seguir um formato
predefinido ou esperado.

Os padrões de expressões regulares geralmente são escritos para corresponder as


entradas válidas. Ou seja, os desenvolvedores examinam o texto que desejam
corresponder e escrevem um padrão de expressão regular que corresponde a ele. Os
desenvolvedores então determinam se esse padrão requer correção ou uma elaboração
adicional testando-o com vários itens de entrada válidos. Quando o padrão
corresponde a todas as entradas válidas previstas, é declarado como pronto para
produção e pode ser incluído em um aplicativo final. Esse método torna um padrão de
expressão regular adequado para corresponder uma entrada restrita. No entanto, não é
adequado para corresponder à entrada irrestrita.

Para corresponder a uma entrada irrestrita, uma expressão regular deve conseguir
manipular três tipos de texto de forma eficiente:

Texto que corresponda ao padrão da expressão regular.


Texto que não corresponda ao padrão da expressão regular.
Texto que quase corresponda ao padrão da expressão regular.

O último tipo de texto é particularmente problemático para uma expressão regular que
foi escrita para lidar com entradas restritas. Se essa expressão regular também depender
de retrocesso abrangente, o mecanismo de expressões regulares poderá gastar um
período fora do normal (em alguns casos, muitas horas ou dias) processando texto
aparentemente inócuo.

2 Aviso

O exemplo a seguir usa uma expressão regular que é sujeita a rastreamento inverso
excessivo e que pode rejeitar endereços de email válidos. Você não deve usá-la em
uma rotina de validação de email. Se você desejar uma expressão regular que
valida endereços de email, confira Como verificar se cadeias de caracteres estão
em um formato de email válido.

Por exemplo, pense em uma expressão regular usada, mas problemática, para validar o
alias de um endereço de email. A expressão regular ^[0-9A-Z]([-.\w]*[0-9A-Z])*$ é
gravada para processar o que é considerado um endereço de email válido. Um
endereço de email válido consiste em um caractere alfanumérico seguido por zero ou
mais caracteres que podem ser alfanuméricos, pontos ou hifens. A expressão regular
deve terminar com um caractere alfanumérico. No entanto, como mostra o exemplo a
seguir, embora esta expressão regular identifique entradas válidas com facilidade, seu
desempenho é muito ineficiente ao processar uma entrada quase válida:

C#

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
Stopwatch sw;
string[] addresses = { "AAAAAAAAAAA@contoso.com",
"AAAAAAAAAAaaaaaaaaaa!@contoso.com" };
// The following regular expression should not actually be used to
// validate an email address.
string pattern = @"^[0-9A-Z]([-.\w]*[0-9A-Z])*$";
string input;

foreach (var address in addresses) {


string mailBox = address.Substring(0, address.IndexOf("@"));
int index = 0;
for (int ctr = mailBox.Length - 1; ctr >= 0; ctr--) {
index++;

input = mailBox.Substring(ctr, index);


sw = Stopwatch.StartNew();
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
sw.Stop();
if (m.Success)
Console.WriteLine("{0,2}. Matched '{1,25}' in {2}",
index, m.Value, sw.Elapsed);
else
Console.WriteLine("{0,2}. Failed '{1,25}' in {2}",
index, input, sw.Elapsed);
}
Console.WriteLine();
}
}
}

// The example displays output similar to the following:


// 1. Matched ' A' in 00:00:00.0007122
// 2. Matched ' AA' in 00:00:00.0000282
// 3. Matched ' AAA' in 00:00:00.0000042
// 4. Matched ' AAAA' in 00:00:00.0000038
// 5. Matched ' AAAAA' in 00:00:00.0000042
// 6. Matched ' AAAAAA' in 00:00:00.0000042
// 7. Matched ' AAAAAAA' in 00:00:00.0000042
// 8. Matched ' AAAAAAAA' in 00:00:00.0000087
// 9. Matched ' AAAAAAAAA' in 00:00:00.0000045
// 10. Matched ' AAAAAAAAAA' in 00:00:00.0000045
// 11. Matched ' AAAAAAAAAAA' in 00:00:00.0000045
//
// 1. Failed ' !' in 00:00:00.0000447
// 2. Failed ' a!' in 00:00:00.0000071
// 3. Failed ' aa!' in 00:00:00.0000071
// 4. Failed ' aaa!' in 00:00:00.0000061
// 5. Failed ' aaaa!' in 00:00:00.0000081
// 6. Failed ' aaaaa!' in 00:00:00.0000126
// 7. Failed ' aaaaaa!' in 00:00:00.0000359
// 8. Failed ' aaaaaaa!' in 00:00:00.0000414
// 9. Failed ' aaaaaaaa!' in 00:00:00.0000758
// 10. Failed ' aaaaaaaaa!' in 00:00:00.0001462
// 11. Failed ' aaaaaaaaaa!' in 00:00:00.0002885
// 12. Failed ' Aaaaaaaaaaa!' in 00:00:00.0005780
// 13. Failed ' AAaaaaaaaaaa!' in 00:00:00.0011628
// 14. Failed ' AAAaaaaaaaaaa!' in 00:00:00.0022851
// 15. Failed ' AAAAaaaaaaaaaa!' in 00:00:00.0045864
// 16. Failed ' AAAAAaaaaaaaaaa!' in 00:00:00.0093168
// 17. Failed ' AAAAAAaaaaaaaaaa!' in 00:00:00.0185993
// 18. Failed ' AAAAAAAaaaaaaaaaa!' in 00:00:00.0366723
// 19. Failed ' AAAAAAAAaaaaaaaaaa!' in 00:00:00.1370108
// 20. Failed ' AAAAAAAAAaaaaaaaaaa!' in 00:00:00.1553966
// 21. Failed ' AAAAAAAAAAaaaaaaaaaa!' in 00:00:00.3223372

Conforme mostrado na saída do exemplo anterior, o mecanismo de expressões


regulares processa o alias de email válido quase ao mesmo tempo, independentemente
do seu comprimento. Por outro lado, quando o endereço de email quase válido tem
mais de cinco caracteres, o tempo de processamento chega a dobrar para cada
caractere adicional na cadeia de caracteres. Portanto, uma cadeia de 28 caracteres quase
válidos levaria mais de uma hora para ser processada e uma cadeia de 33 caracteres
quase válidos demoraria quase um dia.

Como essa expressão regular foi desenvolvida considerando apenas a correspondência


com o formato da entrada, ela não leva em consideração entradas que não
correspondem ao padrão. Isso, por sua vez, pode permitir que entradas irrestritas quase
correspondentes ao padrão da expressão regular prejudiquem significativamente o
desempenho.

Para resolver este problema, você pode fazer o seguinte:

Ao desenvolver um padrão, você deve considerar como o retrocesso pode afetar o


desempenho do mecanismo de expressões regulares, especialmente se a
expressão regular for criada para processar entradas sem restrição. Para obter mais
informações, consulte a seção Tome conta do retrocesso.

Teste rigorosamente sua expressão regular usando entradas inválidas, quase


válidas e válidas. Você pode usar Rex para gerar entrada aleatoriamente para
uma expressão regular específica. Rex é uma ferramenta de exploração de
expressão regular da Microsoft Research.

Trate a instanciação de objetos adequadamente


No meio do modelo de objeto de expressões regulares do .NET está a classe
System.Text.RegularExpressions.Regex, a qual representa o mecanismo de expressões
regulares. Muitas vezes, o maior fator individual que afeta o desempenho das
expressões regulares é a forma como o mecanismo Regex é usado. Definir uma
expressão regular envolve o acoplamento vigoroso do mecanismo de expressões
regulares com um padrão de expressão regular. Esse processo de acoplamento, seja
envolvendo a instanciação de um objeto Regex ao passar para seu constructo um
padrão de expressão regular ou chamar um método estático ao passar o padrão de
expressão regular e a cadeia de caracteres a ser analisada, é necessariamente caro.

7 Observação

Para uma análise detalhada das implicações de desempenho do uso de expressões


regulares interpretadas e compiladas, confira Otimizando o desempenho de
expressões regulares - Parte II: controle o rastreamento inverso no blog da
equipe BCL.

É possível acoplar o mecanismo de expressões regulares com um padrão específico de


expressão regular e usar o mecanismo para fazer a correspondência com o texto de
várias maneiras:

Você pode chamar um método estático de correspondência de padrões,


Regex.Match(String, String). Este método não requer a instanciação de um objeto
de expressão regular.

Você pode criar uma instância de um objeto Regex e chamar um método de


correspondência de padrões de instância de uma expressão regular interpretada,
que é o método padrão para associar o mecanismo de expressão regular a um
padrão de expressão regular. Ele ocorre quando um objeto Regex é instanciado
sem um argumento options que inclua o sinalizador Compiled.

Você pode criar uma instância de um objeto Regex e chamar um método


instanciado de correspondência de padrões de uma expressão regular compilada.
Os objetos de expressão regular representam padrões compilados quando um
objeto Regex é instanciado com um argumento options que inclui o sinalizador
Compiled.

Você pode criar um objeto Regex com propósito especial, que é acoplado a um
padrão de expressão regular específico, compilá-lo e salvá-lo em um assembly
autônomo. Você pode chamar o método Regex.CompileToAssembly para compilá-
lo e salvá-lo.

A forma específica como você chama métodos de correspondência de expressões


regulares pode ter impacto significativo em seu aplicativo. As seções a seguir abordam
quando usar chamadas de métodos estáticos, expressões regulares interpretadas e
expressões regulares compiladas para melhorar o desempenho do seu aplicativo.

) Importante
A forma da chamada de método (estático, interpretada, compilada) afeta o
desempenho se a mesma expressão regular é usada repetidamente em chamadas
de método ou se um aplicativo faz uso extensivo de objetos de expressão regular.

Expressões regulares estáticas


Os métodos de expressões regulares estáticas são recomendados como uma alternativa
a criar repetidamente instâncias de um objeto de expressão regular com a mesma
expressão regular. Ao contrário de padrões de expressões regulares usados por objetos
de expressões regulares, os códigos de operação ou a linguagem intermediária
compilada da Microsoft (MSIL) de padrões usados em chamadas de métodos estáticos
são armazenados em cache internamente pelo mecanismo de expressões regulares.

Por exemplo, um manipulador de eventos chama frequentemente outro método para


validar a entrada do usuário. Este exemplo é mostrado no código a seguir, no qual o
evento Button de um controle Click é usado para chamar um método IsValidCurrency ,
o qual verifica se o usuário inseriu um símbolo de moeda seguido por pelo menos um
dígito decimal.

C#

public void OKButton_Click(object sender, EventArgs e)


{
if (! String.IsNullOrEmpty(sourceCurrency.Text))
if (RegexLib.IsValidCurrency(sourceCurrency.Text))
PerformConversion();
else
status.Text = "The source currency value is invalid.";
}

Uma implementação ineficiente do método IsValidCurrency é mostrada no exemplo a


seguir:

7 Observação

Observe que cada chamada de método cria uma nova instância de um objeto
Regex com o mesmo padrão. Isso, por sua vez, significa que o padrão de expressão
regular deve ser recompilado toda vez que o método é chamado.

C#

using System;
using System.Text.RegularExpressions;
public class RegexLib
{
public static bool IsValidCurrency(string currencyValue)
{
string pattern = @"\p{Sc}+\s*\d+";
Regex currencyRegex = new Regex(pattern);
return currencyRegex.IsMatch(currencyValue);
}
}

Você deve substituir esse código ineficiente por uma chamada ao método estático
Regex.IsMatch(String, String). Esse método elimina a necessidade de criar uma instância
de um objeto Regex toda vez que você deseja chamar um método de correspondência
de padrões e permite que o mecanismo de expressões regulares recupere uma versão
compilada da expressão regular do cache.

C#

using System;
using System.Text.RegularExpressions;

public class RegexLib


{
public static bool IsValidCurrency(string currencyValue)
{
string pattern = @"\p{Sc}+\s*\d+";
return Regex.IsMatch(currencyValue, pattern);
}
}

Por padrão, os 15 padrões de expressões regulares estáticas usados mais recentemente


são armazenados no cache. Para aplicativos que requerem um número maior de
expressões regulares estáticas armazenadas no cache, o tamanho do cache pode ser
ajustado com a definição da propriedade Regex.CacheSize.

A expressão regular \p{Sc}+\s*\d+ que é usada neste exemplo verifica que a cadeia de
caracteres de entrada tem um símbolo de moeda e pelo menos um dígito decimal. O
padrão é definido conforme mostrado na tabela a seguir:

Padrão Descrição

\p{Sc}+ Corresponde a um ou mais caracteres no símbolo Unicode, categoria de moeda.

\s* Corresponde a zero ou mais caracteres de espaço em branco.

\d+ Corresponde a um ou mais dígitos decimais.


Expressões regulares compiladas vs. interpretadas
Os padrões de expressões regulares que não são associados ao mecanismo de
expressões regulares com a especificação da opção Compiled são interpretados.
Quando um objeto de expressão regular é instanciado, o mecanismo de expressões
regulares converte a expressão regular em um conjunto de códigos de operação.
Quando um método de instância é chamado, os códigos de operação são convertidos
para MSIL e executados pelo compilador JIT. Da mesma forma, quando um método
estático de expressão regular é chamado e a expressão regular não pode ser encontrada
no cache, o mecanismo de expressão regular converte a expressão regular em um
conjunto de códigos operacionais e os armazena no cache. Em seguida, converte esses
códigos de operação para MSIL de modo que o compilador JIT possa executá-los. As
expressões regulares interpretadas reduzem o tempo de inicialização ao custo de um
tempo de execução mais lento. Por conta desse processo, elas são melhor utilizadas
quando a expressão regular é usada em um pequeno número de chamadas de método
ou se o número exato de chamadas para métodos de expressão regular é desconhecido,
mas com a expectativa de ser pequeno. À medida que número de chamadas de método
aumenta, o ganho de desempenho do tempo de inicialização reduzido é superado pela
velocidade de execução mais lenta.

Os padrões de expressões regulares associados ao mecanismo de expressões regulares


com a especificação da opção Compiled são compilados. Portanto, quando um objeto
de expressão regular criar uma instância ou quando um método de expressão regular
estática é chamado e a expressão regular não pode ser encontrada no cache, o
mecanismo de expressões regulares converte a expressão regular para um conjunto
intermediário de códigos de operação. Esses códigos são convertidos em MSIL. Quando
um método é chamado, o compilador JIT executa o MSIL. Em contraste com as
expressões regulares interpretadas, as expressões regulares compiladas aumentam o
tempo de inicialização, mas executam os métodos individuais de correspondência
padrão de forma mais rápida. Como resultado, o benefício de desempenho que resulta
da compilação da expressão regular aumenta em proporção ao número de métodos de
expressões regulares chamados.

Para resumir, recomendamos que você use expressões regulares interpretadas ao


chamar métodos de expressões regulares com uma expressão regular específica com
relativa pouca frequência. Você deve usar expressões regulares compiladas ao chamar
os métodos de expressões regulares com uma expressão regular específica com uma
relativa frequência. É difícil determinar o limite exato em que as velocidades de
execução mais lentas das expressões regulares interpretadas ultrapassam os ganhos
proporcionados pelo tempo de inicialização reduzido ou o limite em que os tempos de
inicialização mais lentos das expressões regulares compiladas ultrapassam os ganhos
gerados por suas velocidades de execução mais rápida. O limite depende de vários
fatores, incluindo a complexidade da expressão regular e dos dados específicos que são
processados. Para determinar se expressões regulares interpretadas ou compiladas
oferecem o melhor desempenho para seu cenário de aplicativo específico, você pode
usar a classe Stopwatch para comparar seu tempo de execução.

O exemplo a seguir compara o desempenho de expressões regulares compiladas e


interpretadas ao ler as 10 primeiras frases e ao ler todas as frases no texto The Financier
de Theodore Dreiser. Conforme mostrado pela saída do exemplo, quando apenas dez
chamadas são feitas para os métodos correspondentes de expressão regular, uma
expressão regular interpretada oferece um desempenho melhor do que uma expressão
regular compilada. No entanto, uma expressão regular compilada oferece melhor
desempenho quando um grande número de chamadas (neste caso, mais 13.000) é feito.

C#

using System;
using System.Diagnostics;
using System.IO;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\w+((\r?\n)|,?\s))*\w+[.?:;!]";
Stopwatch sw;
Match match;
int ctr;

StreamReader inFile = new StreamReader(@".\Dreiser_TheFinancier.txt");


string input = inFile.ReadToEnd();
inFile.Close();

// Read first ten sentences with interpreted regex.


Console.WriteLine("10 Sentences with Interpreted Regex:");
sw = Stopwatch.StartNew();
Regex int10 = new Regex(pattern, RegexOptions.Singleline);
match = int10.Match(input);
for (ctr = 0; ctr <= 9; ctr++) {
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine(" {0} matches in {1}", ctr, sw.Elapsed);

// Read first ten sentences with compiled regex.


Console.WriteLine("10 Sentences with Compiled Regex:");
sw = Stopwatch.StartNew();
Regex comp10 = new Regex(pattern,
RegexOptions.Singleline | RegexOptions.Compiled);
match = comp10.Match(input);
for (ctr = 0; ctr <= 9; ctr++) {
if (match.Success)
// Do nothing with the match except get the next match.
match = match.NextMatch();
else
break;
}
sw.Stop();
Console.WriteLine(" {0} matches in {1}", ctr, sw.Elapsed);

// Read all sentences with interpreted regex.


Console.WriteLine("All Sentences with Interpreted Regex:");
sw = Stopwatch.StartNew();
Regex intAll = new Regex(pattern, RegexOptions.Singleline);
match = intAll.Match(input);
int matches = 0;
while (match.Success) {
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine(" {0:N0} matches in {1}", matches, sw.Elapsed);

// Read all sentences with compiled regex.


Console.WriteLine("All Sentences with Compiled Regex:");
sw = Stopwatch.StartNew();
Regex compAll = new Regex(pattern,
RegexOptions.Singleline | RegexOptions.Compiled);
match = compAll.Match(input);
matches = 0;
while (match.Success) {
matches++;
// Do nothing with the match except get the next match.
match = match.NextMatch();
}
sw.Stop();
Console.WriteLine(" {0:N0} matches in {1}", matches, sw.Elapsed);
}
}
// The example displays the following output:
// 10 Sentences with Interpreted Regex:
// 10 matches in 00:00:00.0047491
// 10 Sentences with Compiled Regex:
// 10 matches in 00:00:00.0141872
// All Sentences with Interpreted Regex:
// 13,443 matches in 00:00:01.1929928
// All Sentences with Compiled Regex:
// 13,443 matches in 00:00:00.7635869
//
// >compare1
// 10 Sentences with Interpreted Regex:
// 10 matches in 00:00:00.0046914
// 10 Sentences with Compiled Regex:
// 10 matches in 00:00:00.0143727
// All Sentences with Interpreted Regex:
// 13,443 matches in 00:00:01.1514100
// All Sentences with Compiled Regex:
// 13,443 matches in 00:00:00.7432921

O padrão de expressão regular usado neste exemplo, \b(\w+((\r?\n)|,?\s))*\w+


[.?:;!] , é definido como mostrado na tabela a seguir:

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\w+ Corresponde a um ou mais caracteres de palavra.

(\r?\n)|,? Corresponde a um zero ou um retorno de carro seguido por um caractere de nova


\s) linha, ou zero ou uma vírgula seguida por um caractere de espaço em branco.

(\w+((\r? Corresponde a zero ou mais ocorrências de um ou mais caracteres de palavra que


\n)|,?\s))* são seguidos por zero ou por retornos de carro e por um caractere de nova linha
ou por zero ou uma vírgula seguida por um caractere de espaço em branco.

\w+ Corresponde a um ou mais caracteres de palavra.

[.?:;!] Corresponde a um ponto, um ponto de interrogação, dois-pontos, ponto e vírgula


ou ponto de exclamação.

Expressões regulares: compiladas em um assembly


O .NET também permite criar um assembly que contém expressões regulares
compiladas. Esse recurso move a ocorrência de desempenho da compilação de
expressões regulares do tempo de execução para o tempo de design. No entanto,
também envolve algum trabalho extra. Você deve definir as expressões regulares com
antecedência e compilá-las para um assembly. O compilador pode então fazer
referência a esse assembly ao compilar código-fonte que usa expressões regulares do
assembly. Cada expressão regular compilada no assembly é representada por uma
classe que deriva de Regex.

Para compilar expressões regulares para um assembly, você chama o método


Regex.CompileToAssembly(RegexCompilationInfo[], AssemblyName) e passa por uma
matriz de objetos RegexCompilationInfo e um objeto AssemblyName. Os objetos
RegexCompilationInfo representam as expressões regulares a serem compiladas, e o
objeto AssemblyName que contém informações sobre a assembly a ser criada.

Recomendamos que você compile as expressões regulares em um assembly nestas


situações:

Se você é um desenvolvedor de componentes que deseja criar uma biblioteca de


expressões regulares reutilizáveis.
Se você quer que seus métodos de correspondência de padrões das expressões
regulares sejam chamados de forma indefinida, de uma ou duas vezes a milhares
ou dezenas de milhares de vezes. Ao contrário de expressões regulares compiladas
ou interpretadas, as expressões regulares que são compiladas para separar
assemblies oferecem desempenho consistente, independente do número de
chamadas de método.

Se estiver usando expressões regulares compiladas para otimizar o desempenho, não


use reflexão para criar o assembly, carregar o mecanismo de expressões regulares e
executar os métodos de correspondência de padrões. Para evitar a reflexão, você não
deve criar padrões de expressões regulares dinamicamente, e especificar as opções de
correspondência de padrões (como correspondência de padrões sem diferenciação de
maiúsculas e minúsculas) no momento em que o assembly é criado. Exige também que
você separe o código que cria o assembly do código que usa a expressão regular.

O exemplo a seguir mostra como criar um assembly que contém uma expressão regular
compilada. Ele cria um assembly chamado RegexLib.dll com uma única classe de
expressão regular SentencePattern . Essa classe contém o padrão de expressão regular
correspondente à sentença usado na seção Expressões Regulares Compiladas x
Interpretadas.

C#

using System;
using System.Reflection;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
RegexCompilationInfo SentencePattern =
new RegexCompilationInfo(@"\b(\w+((\r?\n)|,?
\s))*\w+[.?:;!]",
RegexOptions.Multiline,
"SentencePattern",

"Utilities.RegularExpressions",
true);
RegexCompilationInfo[] regexes = { SentencePattern };
AssemblyName assemName = new AssemblyName("RegexLib,
Version=1.0.0.1001, Culture=neutral, PublicKeyToken=null");
Regex.CompileToAssembly(regexes, assemName);
}
}

Quando o exemplo é compilado em um executável e executado, ele cria um assembly


denominado RegexLib.dll . Uma classe Utilities.RegularExpressions.SentencePattern
derivada de Regex representa a expressão regular. O exemplo a seguir usa a expressão
regular compilada para extrair as sentenças do texto The Financier de Theodore Dreiser:

C#

using System;
using System.IO;
using System.Text.RegularExpressions;
using Utilities.RegularExpressions;

public class Example


{
public static void Main()
{
SentencePattern pattern = new SentencePattern();
StreamReader inFile = new StreamReader(@".\Dreiser_TheFinancier.txt");
string input = inFile.ReadToEnd();
inFile.Close();

MatchCollection matches = pattern.Matches(input);


Console.WriteLine("Found {0:N0} sentences.", matches.Count);
}
}
// The example displays the following output:
// Found 13,443 sentences.

Tome conta do retrocesso


Normalmente, o mecanismo de expressões regulares usa progressão linear para
percorrer uma cadeia de caracteres de entrada e compará-la a uma expressão regular
padrão. No entanto, quando quantificadores indefinidos, como * , + e ? são usados em
um padrão de expressão regular, o mecanismo de expressões regulares pode ignorar
uma parte das correspondências parciais com êxito e retornar ao estado salvo
anteriormente para pesquisar uma correspondência com êxito para o padrão inteiro.
Esse processo é conhecido como retrocesso.
 Dica

Para obter mais informações sobre o retrocesso, consulte Detalhes do


comportamento de expressões regulares e Retrocesso. Para obter discussões
detalhadas sobre retrocesso, consulte as Melhorias de Expressão Regular no .NET
7 e Otimizando o Desempenho de Expressões Regulares postagens no blog.

O suporte ao retrocesso proporciona poder e flexibilidade às expressões regulares. Ele


também coloca a responsabilidade por controlar o funcionamento do mecanismo de
expressões regulares nas mãos dos desenvolvedores de expressões regulares. Como os
desenvolvedores geralmente não estão cientes dessa responsabilidade, o uso indevido
do retrocesso ou a confiança no retrocesso excessivo geralmente exerce o papel mais
significativo na degradação do desempenho da expressão regular. Em um cenário de
pior caso, o tempo de execução pode dobrar para cada caractere adicional na cadeia de
caracteres de entrada. Na verdade, com o uso de rastreamento inverso excessivo, é fácil
criar o equivalente programático de um loop infinito se a entrada quase corresponder
ao padrão de expressão regular. O mecanismo de expressão regular pode levar horas ou
até dias para processar uma cadeia de caracteres de entrada relativamente curta.

Normalmente, os aplicativos pagam uma multa de desempenho por usar o


rastreamento inverso, mesmo não sendo essencial para uma correspondência. Por
exemplo, a expressão regular \b\p{Lu}\w*\b corresponde a todas as palavras que
começam com um caractere maiúsculo, como mostra a tabela a seguir:

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\p{Lu} Corresponde a um caractere minúsculo.

\w* Corresponde a zero ou mais caracteres de palavra.

\b Termina a correspondência em um limite de palavra.

Como um limite de palavra não é o mesmo que ou um subconjunto de, um caractere de


palavra, não há nenhuma possibilidade de o mecanismo de expressões regulares cruzar
um limite de palavra ao corresponder caracteres de palavra. Portanto, para essa
expressão regular, o rastreamento inverso nunca pode contribuir para o sucesso geral
de qualquer correspondência. Ele só pode degradar o desempenho porque o
mecanismo de expressão regular é forçado a salvar seu estado para cada
correspondência preliminar bem-sucedida de um caractere de palavra.
Se você determinar que o retrocesso não é necessário, você pode desabilitá-lo de
algumas maneiras:

Definindo a opção RegexOptions.NonBacktracking (introduzida no .NET 7). Para


obter mais informações, confira Modo sem retrocesso.

Usando o elemento de linguagem (?>subexpression) , conhecido como um grupo


atômico. O exemplo a seguir analisa uma cadeia de caracteres de entrada usando
duas expressões regulares. A primeira, \b\p{Lu}\w*\b , depende do retrocesso. A
segunda, \b\p{Lu}(?>\w*)\b , desabilita o retrocesso. Conforme mostrado pela
saída do exemplo, ambas produzem o mesmo resultado:

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "This this word Sentence name Capital";
string pattern = @"\b\p{Lu}\w*\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);

Console.WriteLine();

pattern = @"\b\p{Lu}(?>\w*)\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// This
// Sentence
// Capital
//
// This
// Sentence
// Capital

Em muitos casos, o retrocesso é essencial para corresponder um padrão de expressão


regular ao texto de entrada. No entanto, o retrocesso excessivo pode prejudicar
severamente o desempenho e criar a impressão de que um aplicativo parou de
responder. Em particular, esse problema ocorre quando quantificadores são aninhados e
o texto que corresponde à subexpressão externa é um subconjunto do texto que
corresponde à subexpressão interna.
2 Aviso

Além de evitar rastreamentos inversos excessivos, você deve usar o recurso de


tempo limite para garantir que rastreamentos inversos excessivos não degradem
severamente o desempenho da expressão regular. Para obter mais informações,
confira a seção Use valores de tempo limite.

Por exemplo, o padrão de expressão regular ^[0-9A-Z]([-.\w]*[0-9A-Z])*\$$ destina-se


a corresponder a um número de peça que consiste em pelo menos um caractere
alfanumérico. Todos os demais caracteres podem consistir em um caractere
alfanumérico, um hífen, um sublinhado ou um ponto, embora o último caractere deva
ser alfanumérico. Um cifrão termina o número da peça. Em alguns casos, esse padrão de
expressão regular pode exibir um desempenho ruim porque os quantificadores estão
aninhados e porque a subexpressão [0-9A-Z] é um subconjunto da subexpressão
[-.\w]* .

Nesses casos, você pode otimizar o desempenho da expressão regular ao remover os


quantificadores aninhados e substituir a subexpressão externa por uma declaração de
lookahead ou lookbehind de largura zero. Asserções lookbehind e lookahead são
âncoras. Elas não movem o ponteiro na cadeia de caracteres de entrada, mas fazem
uma verificação para checar se uma condição especificada foi atendida. Por exemplo, a
expressão regular do número de peça pode ser reescrita como ^[0-9A-Z][-.\w]*(?<=[0-
9A-Z])\$$ . Esse padrão de expressão regular é definido conforme mostrado na tabela a

seguir:

Padrão Descrição

^ Começar a correspondência no início da cadeia de caracteres de entrada.

[0-9A-Z] Corresponde a um caractere alfanumérico. O número de peça deve consistir em


pelo menos este caractere.

[-.\w]* Corresponde a zero ou mais ocorrências de qualquer caractere de palavra, hífen


ou ponto.

\$ Corresponde a um cifrão.

(?<=[0-9A- Avalie atrás do sinal de cifrão final para garantir que o caractere anterior seja
Z]) alfanumérico.

$ Finalizar a correspondência no final da cadeia de caracteres de entrada.

O exemplo a seguir mostra o uso dessa expressão regular para corresponder a uma
matriz que contém possíveis números de blocos:
C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"^[0-9A-Z][-.\w]*(?<=[0-9A-Z])\$$";
string[] partNos = { "A1C$", "A4", "A4$", "A1603D$", "A1603D#" };

foreach (var input in partNos) {


Match match = Regex.Match(input, pattern);
if (match.Success)
Console.WriteLine(match.Value);
else
Console.WriteLine("Match not found.");
}
}
}
// The example displays the following output:
// A1C$
// Match not found.
// A4$
// A1603D$
// Match not found.

A linguagem de expressões regulares no .NET inclui os seguintes elementos de


linguagem que você pode usar para eliminar quantificadores aninhados. Para obter mais
informações, consulte Constructos de agrupamento.

Elemento de Descrição
linguagem

(?= subexpression Lookahead positivo de largura zero. Avalia à direita da posição atual para
) determinar se subexpression corresponde à cadeia de caracteres de
entrada.

(?! subexpression Lookahead negativo de largura zero. Avalia à direita da posição atual para
) determinar se subexpression não corresponde à cadeia de caracteres de
entrada.

(?<= Lookbehind positivo de largura zero. Avalia à esquerda da posição atual


subexpression ) para determinar se subexpression corresponde à cadeia de caracteres de
entrada.

(?<! Lookbehind negativo de largura zero. Avalia à esquerda da posição atual


subexpression ) para determinar se subexpression não corresponde à cadeia de caracteres
de entrada.
Use valores de tempo limite
Se suas expressões regulares processarem entradas quase correspondentes ao padrão
da expressão regular, elas poderão frequentemente confiar no retrocesso excessivo, o
que afeta significativamente o desempenho. Além de considerar cuidadosamente o uso
de rastreamento inverso e testar a expressão regular contra entradas quase
correspondentes, você deve sempre definir um valor de tempo limite para garantir que
o impacto do rastreamento inverso excessivo, caso ocorra, seja minimizado.

O intervalo de tempo limite de expressão regular define o período que o mecanismo de


expressão regular procurará por uma única correspondência antes de atingir o tempo
limite. Dependendo do padrão de expressão regular e do texto de entrada, o tempo de
execução pode exceder o intervalo de tempo limite especificado, mas não passa mais
tempo no rastreamento inverso do que o intervalo de tempo limite especificado. O
intervalo de tempo limite padrão é Regex.InfiniteMatchTimeout, o que significa que a
expressão regular não terá tempo limite. Você pode substituir esse valor e definir um
intervalo de tempo limite da seguinte maneira:

Chame o constructo Regex(String, RegexOptions, TimeSpan) para fornecer um


valor de tempo limite ao criar uma instância de um objeto Regex.

Chame um método estático de padrões correspondentes, como


Regex.Match(String, String, RegexOptions, TimeSpan) ou Regex.Replace(String,
String, String, RegexOptions, TimeSpan), que inclui um parâmetro matchTimeout .

Chame o constructo que tem um parâmetro de tipo TimeSpan para expressões


regulares compiladas que são criadas chamando-se o método
Regex.CompileToAssembly.

Defina um valor em todo o processo ou AppDomain com código como


AppDomain.CurrentDomain.SetData("REGEX_DEFAULT_MATCH_TIMEOUT",
TimeSpan.FromMilliseconds(100)); .

Se você tiver definido um intervalo de tempo limite e uma correspondência não for
localizada no final do intervalo, o método de expressão regular gerará uma exceção
RegexMatchTimeoutException. No manipulador de exceção, você pode optar por tentar
fazer novamente a correspondência com um intervalo de tempo limite mais longo,
abandonar a tentativa de correspondência e assumir que não há nenhuma
correspondência ou abandonar a tentativa de correspondência e registrar as
informações de exceção para análise futura.

O exemplo a seguir define um método GetWordData que instancia uma expressão


regular com um intervalo de tempo limite de 350 milissegundos para calcular o número
de palavras e o número médio de caracteres em uma palavra em um documento de
texto. Se a operação de correspondência exceder o tempo limite, o intervalo de tempo
limite aumenta em 350 milissegundos e o objeto de Regex criará uma nova instância. Se
o novo intervalo de tempo limite exceder 1 segundo, o método gerará novamente a
exceção no chamador.

C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
RegexUtilities util = new RegexUtilities();
string title = "Doyle - The Hound of the Baskervilles.txt";
try {
var info = util.GetWordData(title);
Console.WriteLine("Words: {0:N0}", info.Item1);
Console.WriteLine("Average Word Length: {0:N2} characters",
info.Item2);
}
catch (IOException e) {
Console.WriteLine("IOException reading file '{0}'", title);
Console.WriteLine(e.Message);
}
catch (RegexMatchTimeoutException e) {
Console.WriteLine("The operation timed out after {0:N0}
milliseconds",
e.MatchTimeout.TotalMilliseconds);
}
}
}

public class RegexUtilities


{
public Tuple<int, double> GetWordData(string filename)
{
const int MAX_TIMEOUT = 1000; // Maximum timeout interval in
milliseconds.
const int INCREMENT = 350; // Milliseconds increment of timeout.

List<string> exclusions = new List<string>( new string[] { "a", "an",


"the" });
int[] wordLengths = new int[29]; // Allocate an array of more
than ample size.
string input = null;
StreamReader sr = null;
try {
sr = new StreamReader(filename);
input = sr.ReadToEnd();
}
catch (FileNotFoundException e) {
string msg = String.Format("Unable to find the file '{0}'",
filename);
throw new IOException(msg, e);
}
catch (IOException e) {
throw new IOException(e.Message, e);
}
finally {
if (sr != null) sr.Close();
}

int timeoutInterval = INCREMENT;


bool init = false;
Regex rgx = null;
Match m = null;
int indexPos = 0;
do {
try {
if (! init) {
rgx = new Regex(@"\b\w+\b", RegexOptions.None,
TimeSpan.FromMilliseconds(timeoutInterval));
m = rgx.Match(input, indexPos);
init = true;
}
else {
m = m.NextMatch();
}
if (m.Success) {
if ( !exclusions.Contains(m.Value.ToLower()))
wordLengths[m.Value.Length]++;

indexPos += m.Length + 1;
}
}
catch (RegexMatchTimeoutException e) {
if (e.MatchTimeout.TotalMilliseconds < MAX_TIMEOUT) {
timeoutInterval += INCREMENT;
init = false;
}
else {
// Rethrow the exception.
throw;
}
}
} while (m.Success);

// If regex completed successfully, calculate number of words and


average length.
int nWords = 0;
long totalLength = 0;

for (int ctr = wordLengths.GetLowerBound(0); ctr <=


wordLengths.GetUpperBound(0); ctr++) {
nWords += wordLengths[ctr];
totalLength += ctr * wordLengths[ctr];
}
return new Tuple<int, double>(nWords, totalLength/nWords);
}
}

Capture somente quando necessário


As expressões regulares no .NET oferecem suporte a constructos de agrupamento, que
permitem a você agrupar um padrão de expressão regular em uma ou mais
subexpressões. Os constructos de agrupamento mais usados na linguagem de
expressões regulares no .NET são ( subexpression ) , que define um grupo de captura
numerado, e (?< name > subexpression ) , que define um grupo de captura nomeado. Os
construtores de agrupamento são essenciais para criar referências reversas e definir uma
subexpressão à qual um quantificador é aplicado.

No entanto, o uso desses elementos de linguagem tem um custo. Eles fazem com que o
objeto GroupCollection retornado pela propriedade Match.Groups seja preenchido com
as capturas nomeadas ou sem nome mais recentes. Se um único constructo de
agrupamento capturar várias substrings de caracteres na cadeia de caracteres de
entrada, também preenchem o objeto CaptureCollection retornado pela propriedade
Group.Captures de um grupo de captura específico com vários objetos Capture.

Muitas vezes, os constructos de agrupamento são usados em uma expressão regular


apenas para que os quantificadores possam ser aplicados a eles. Os grupos capturados
por essas subexpressões não são usados posteriormente. Por exemplo, a expressão
regular \b(\w+[;,]?\s?)+[.?!] é criada para capturar uma frase inteira. A tabela a seguir
descreve os elementos de linguagem nesse padrão de expressão regular e seu efeito
nas coleções Match e Match.Groups do objeto Group.Captures:

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\w+ Corresponde a um ou mais caracteres de palavra.

[;,]? Corresponde a zero ou uma vírgula ou ponto e vírgula.

\s? Corresponde a zero ou a um caractere de espaço em branco.

(\w+ Corresponde a uma ou mais ocorrências de um ou mais caracteres de palavra


[;,]? seguidos por uma vírgula opcional ou por ponto e vírgula seguido por um caractere
\s?)+ de espaço em branco opcional. Este padrão define o primeiro grupo de captura, que é
Padrão necessário
Descrição para que a combinação de vários caracteres de palavra (ou seja, uma
palavra) seguido por um símbolo de pontuação opcional seja repetida até que o
mecanismo de expressões regulares atinja o final de uma sentença.

[.?!] Corresponde a um ponto, um ponto de interrogação ou um ponto de exclamação.

Como o exemplo a seguir mostra, quando uma correspondência é encontrada, os


objetos GroupCollection e de CaptureCollection são preenchidos com as capturas da
correspondência. Nesse caso, o grupo de captura (\w+[;,]?\s?) existe para que o
quantificador + possa ser aplicado a ele, o que permite que o padrão de expressão
regular corresponda a cada palavra em uma sentença. Caso contrário, ele
corresponderia à última palavra em uma sentença.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "This is one sentence. This is another.";
string pattern = @"\b(\w+[;,]?\s?)+[.?!]";

foreach (Match match in Regex.Matches(input, pattern)) {


Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index);
int grpCtr = 0;
foreach (Group grp in match.Groups) {
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index);
int capCtr = 0;
foreach (Capture cap in grp.Captures) {
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index);
capCtr++;
}
grpCtr++;
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: 'This is one sentence.' at index 0.
// Group 0: 'This is one sentence.' at index 0.
// Capture 0: 'This is one sentence.' at 0.
// Group 1: 'sentence' at index 12.
// Capture 0: 'This ' at 0.
// Capture 1: 'is ' at 5.
// Capture 2: 'one ' at 8.
// Capture 3: 'sentence' at 12.
//
// Match: 'This is another.' at index 22.
// Group 0: 'This is another.' at index 22.
// Capture 0: 'This is another.' at 22.
// Group 1: 'another' at index 30.
// Capture 0: 'This ' at 22.
// Capture 1: 'is ' at 27.
// Capture 2: 'another' at 30.

Quando você usa subexpressões apenas para aplicar quantificadores a elas e não está
interessado em texto capturado, desabilite as capturas de grupo. Por exemplo, o
elemento de linguagem (?:subexpression) evita que o grupo ao qual ele se aplica
capture subcadeias de caracteres correspondidas. No exemplo a seguir, o padrão de
expressão regular do exemplo anterior é alterado para \b(?:\w+[;,]?\s?)+[.?!] .
Conforme mostrado pela saída, ele impede que o mecanismo de expressões regulares
preencha as coleções GroupCollection e CaptureCollection:

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "This is one sentence. This is another.";
string pattern = @"\b(?:\w+[;,]?\s?)+[.?!]";

foreach (Match match in Regex.Matches(input, pattern)) {


Console.WriteLine("Match: '{0}' at index {1}.",
match.Value, match.Index);
int grpCtr = 0;
foreach (Group grp in match.Groups) {
Console.WriteLine(" Group {0}: '{1}' at index {2}.",
grpCtr, grp.Value, grp.Index);
int capCtr = 0;
foreach (Capture cap in grp.Captures) {
Console.WriteLine(" Capture {0}: '{1}' at {2}.",
capCtr, cap.Value, cap.Index);
capCtr++;
}
grpCtr++;
}
Console.WriteLine();
}
}
}
// The example displays the following output:
// Match: 'This is one sentence.' at index 0.
// Group 0: 'This is one sentence.' at index 0.
// Capture 0: 'This is one sentence.' at 0.
//
// Match: 'This is another.' at index 22.
// Group 0: 'This is another.' at index 22.
// Capture 0: 'This is another.' at 22.

É possível desabilitar as capturas de uma das seguintes formas:

Use o elemento de linguagem (?:subexpression) . Esse elemento impede a captura


de subcadeias de caracteres correspondidas no grupo ao qual se ele aplica. Ele não
desabilita capturas de substring de caracteres em grupos aninhados.

Use a opção ExplicitCapture. Ela desabilita todas as capturas sem nome ou


implícitas no padrão de expressão regular. Quando você usa essa opção, somente
as subcadeias de caracteres que correspondem aos grupos nomeados definidos
com o elemento de linguagem (?<name>subexpression) podem ser capturadas. O
sinalizador ExplicitCapture pode ser passado para o parâmetro options de um
construtor de classe Regex ou para o parâmetro options de um método de
correspondência estática Regex.

Use a opção n no elemento de linguagem (?imnsx) . Esta opção desabilita todas


as capturas sem nome ou implícitas a partir do ponto no padrão de expressão
regular em que o elemento aparece. As capturas são desabilitadas até o final do
padrão ou até a opção (-n) permitir capturas sem nome ou implícitas. Para saber
mais, confira Constructos diversos.

Use a opção n no elemento de linguagem (?imnsx:subexpression) . Esta opção


desativa todas as capturas sem nome ou implícitas em subexpression . As capturas
por grupos de capturas aninhadas sem nome ou implícitas também são
desabilitadas.

Artigos relacionados
Título Descrição

Detalhes do Examina a implementação do mecanismo de expressões regulares no


comportamento de .NET. O artigo trata da flexibilidade de expressões regulares e explica a
expressões regulares responsabilidade do desenvolvedor para garantir o funcionamento
eficiente e robusto do mecanismo de expressões regulares.

Retrocesso Explica o que é o retrocesso é como ele afeta o desempenho da


expressão regular e examina os elementos de linguagem que
Título Descrição

fornecem alternativas ao retrocesso.

Linguagem de Descreve os elementos de linguagem de expressões regulares do .NET


expressões regulares – e fornece links para a documentação detalhada de cada elemento da
referência rápida linguagem.

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
O modelo de objeto de expressão
regular
Artigo • 10/05/2023

Este tópico descreve o modelo do objeto usado ao trabalhar com expressões regulares
do .NET. Ele contém as seções a seguir:

O mecanismo de expressões regulares

Os objetos MatchCollection e Match

A coleta de Grupo

O grupo capturado

A coleta de captura

A captura individual

O mecanismo da expressão regular


O mecanismo de expressões regulares no .NET é representada pela classe Regex. O
mecanismo de expressões regulares é responsável por analisar e compilar uma
expressão regular e realizar operações que correspondem ao padrão da expressão
regular com uma cadeia de caracteres de entrada. O mecanismo é o componente
central no modelo do objeto da expressão regular do .NET.

Você pode usar o mecanismo de expressões regulares de duas maneiras:

Ao calcular os métodos estáticos da classe Regex. Os parâmetros do método


incluem a cadeia de caracteres de entrada e o padrão da expressão regular. O
mecanismo de expressões regulares armazena em cache expressões regulares que
são usadas em chamadas de método estático; por isso, chamadas repetidas a
métodos de expressão regular estáticos que usam a mesma expressão regular
oferecem um desempenho relativamente bom.

Ao instanciar um objeto Regex, transmitindo uma expressão regular ao construtor


da classe. Nesse caso, o objeto Regex é imutável (somente leitura) e representa um
mecanismo de expressão regular que está ligado rigorosamente a uma única
expressão regular. Como as expressões regulares usadas por instâncias Regex não
são armazenadas em cache, você não deve instanciar um objeto Regex várias vezes
com a mesma expressão regular.
Você pode chamar os métodos da classe Regex para realizar as seguintes operações:

Determinar se uma cadeia de caracteres corresponde a um padrão de expressão


regular.

Extrair uma única correspondência ou a primeira correspondência.

Extrair todas as correspondências.

Substituir uma subcadeia de caracteres correspondida.

Dividir uma cadeia única em uma matriz de cadeias de caracteres.

Essas operações são descritas nas seções a seguir.

Correspondendo a um padrão de expressão regular


O método Regex.IsMatch retorna true se a cadeia corresponder ao padrão, ou false se
não corresponder. Geralmente, o método IsMatch é usado para validar a entrada da
cadeia de caracteres. Por exemplo, o código a seguir assegura que uma cadeia de
caracteres corresponda a um número do cadastro de pessoas físicas válido nos Estados
Unidos.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] values = { "111-22-3333", "111-2-3333"};
string pattern = @"^\d{3}-\d{2}-\d{4}$";
foreach (string value in values) {
if (Regex.IsMatch(value, pattern))
Console.WriteLine("{0} is a valid SSN.", value);
else
Console.WriteLine("{0}: Invalid", value);
}
}
}
// The example displays the following output:
// 111-22-3333 is a valid SSN.
// 111-2-3333: Invalid

O padrão da expressão regular ^\d{3}-\d{2}-\d{4}$ é interpretado conforme mostrado


na tabela a seguir.
Padrão Descrição

^ Corresponder ao início da cadeia de caracteres de entrada.

\d{3} Corresponder a três dígitos decimais.

- Corresponder a um hífen.

\d{2} Corresponde a dois dígitos decimais.

- Corresponder a um hífen.

\d{4} Corresponder a quatro dígitos decimais.

$ Corresponder ao final da cadeia de caracteres de entrada.

Extraindo uma única correspondência ou a primeira


correspondência
O método Regex.Match retorna um objeto Match que contém informações sobre a
primeira subcadeia de caracteres que corresponde a um padrão de expressão regular.
Se a propriedade Match.Success retornar true , indicando que uma correspondência foi
localizada, você pode recuperar informações sobre correspondências subsequentes
chamando o método Match.NextMatch. Essas chamadas de método podem continuar
até que a propriedade Match.Success retorne false . Por exemplo, o código a seguir usa
o método Regex.Match(String, String) para localizar a primeira ocorrência de uma
palavra duplicada em uma cadeia de caracteres. Em seguida, ele chama o método
Match.NextMatch para encontrar ocorrências adicionais. O exemplo examina a
propriedade Match.Success após cada chamada de método para determinar se a
correspondência atual foi bem-sucedida e se uma chamada para o método
Match.NextMatch deve seguir.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "This is a a farm that that raises dairy cattle.";
string pattern = @"\b(\w+)\W+(\1)\b";
Match match = Regex.Match(input, pattern);
while (match.Success)
{
Console.WriteLine("Duplicate '{0}' found at position {1}.",
match.Groups[1].Value, match.Groups[2].Index);
match = match.NextMatch();
}
}
}
// The example displays the following output:
// Duplicate 'a' found at position 10.
// Duplicate 'that' found at position 22.

O padrão da expressão regular \b(\w+)\W+(\1)\b é interpretado conforme mostrado na


tabela a seguir.

Padrão Descrição

\b Começa a correspondência em um limite de palavra.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Este é o primeiro grupo de


captura.

\W+ Estabeleça a correspondência com um ou mais caracteres que não compõem palavras.

(\1) Corresponder à primeira cadeia capturada. Este é o segundo grupo de captura.

\b Termina a correspondência em um limite de palavra.

Extraindo todas as correspondências


O método Regex.Matches retorna um objeto MatchCollection que contém informações
sobre todas as correspondências que o mecanismo de expressão regular localizou na
cadeia de caracteres de entrada. Por exemplo, o exemplo anterior poderia ser regravado
para chamar o método Matches ao invés dos métodos Match e NextMatch.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "This is a a farm that that raises dairy cattle.";
string pattern = @"\b(\w+)\W+(\1)\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("Duplicate '{0}' found at position {1}.",
match.Groups[1].Value, match.Groups[2].Index);
}
}
// The example displays the following output:
// Duplicate 'a' found at position 10.
// Duplicate 'that' found at position 22.

Substituindo uma subcadeia de caracteres correspondida


O método Regex.Replace substitui cada subcadeia de caracteres que corresponde ao
padrão da expressão regular com uma cadeia de caracteres ou padrão de expressão
regular especificado, e retorna a cadeia de caracteres de entrada inteira com as
substituições. Por exemplo, o código a seguir adiciona um símbolo de moeda dos
Estados Unidos antes de um número decimal em uma cadeia de caracteres.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b\d+\.\d{2}\b";
string replacement = "$$$&";
string input = "Total Cost: 103.64";
Console.WriteLine(Regex.Replace(input, pattern, replacement));
}
}
// The example displays the following output:
// Total Cost: $103.64

O padrão da expressão regular \b\d+\.\d{2}\b é interpretado conforme mostrado na


tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\d+ Corresponde a um ou mais dígitos decimais.

\. Corresponde a um ponto final.

\d{2} Corresponde a dois dígitos decimais.

\b Termina a correspondência em um limite de palavra.

O padrão de substituição $$$& é interpretado conforme a tabela a seguir.

Padrão Cadeia de caracteres de substituição


Padrão Cadeia de caracteres de substituição

$$ O caractere de cifrão ($).

$& A subcadeia de caracteres correspondida inteira.

Dividindo uma cadeia de caracteres única em uma matriz


de cadeias de caracteres
O método Regex.Split divide a cadeia de caracteres de entrada nas posições definidas
por uma correspondência de expressão regular. Por exemplo, o código a seguir coloca
os itens em uma lista numerada em uma matriz de cadeia de caracteres.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "1. Eggs 2. Bread 3. Milk 4. Coffee 5. Tea";
string pattern = @"\b\d{1,2}\.\s";
foreach (string item in Regex.Split(input, pattern))
{
if (! String.IsNullOrEmpty(item))
Console.WriteLine(item);
}
}
}
// The example displays the following output:
// Eggs
// Bread
// Milk
// Coffee
// Tea

O padrão da expressão regular \b\d{1,2}\.\s é interpretado conforme mostrado na


tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\d{1,2} Corresponder a um ou dois dígitos decimais.

\. Corresponde a um ponto final.


Padrão Descrição

\s Corresponde a um caractere de espaço em branco.

A coleção de correspondência e os objetos de


correspondência
Métodos regex retornam dois objetos que fazem parte do modelos de objeto de
expressão regular: os objetos MatchCollection e Match.

A coleção de correspondência
O método Regex.Matches retorna um objeto MatchCollection que contém objetos
Match que representam todas as correspondências localizadas pelo mecanismo de
expressão regular na ordem em que elas ocorrem na cadeia de caracteres de entrada.
Se não houver correspondências, o método retorna um objeto MatchCollection sem
membros. A propriedade MatchCollection.Item[] permite que você acesse membros
individuais da coleção por índice, de zero a um valor uma vez menor do que o valor da
propriedade MatchCollection.Count. Item[] é o indexador da coleção (no C#) e a
propriedade padrão (no Visual Basic).

Por padrão, a chamada para o método Regex.Matches usa a avaliação lenta para
preencher o objeto MatchCollection. O acesso a propriedades que requerem uma
coleção completamente preenchida, como as propriedades MatchCollection.Count e
MatchCollection.Item[], pode envolver uma penalização de desempenho. Como
resultado, recomendamos que você acesse a coleção usando o objeto IEnumerator
retornado pelo método MatchCollection.GetEnumerator. Linguagens individuais
oferecem constructos, como For Each no Visual Basic e foreach no C#, que encapsulam
a interface IEnumerator da coleção.

O exemplo a seguir usa o método Regex.Matches(String) para preencher um objeto


MatchCollection com todas as correspondências localizadas em uma cadeia de
caracteres de entrada. O exemplo enumera a coleção, copia as correspondências para
uma matriz de cadeia de caracteres e registra as posições dos caracteres em uma matriz
de inteiros.

C#

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
MatchCollection matches;
List<string> results = new List<string>();
List<int> matchposition = new List<int>();

// Create a new Regex object and define the regular expression.


Regex r = new Regex("abc");
// Use the Matches method to find all matches in the input string.
matches = r.Matches("123abc4abcd");
// Enumerate the collection to retrieve all matches and positions.
foreach (Match match in matches)
{
// Add the match string to the string array.
results.Add(match.Value);
// Record the character position where the match was found.
matchposition.Add(match.Index);
}
// List the results.
for (int ctr = 0; ctr < results.Count; ctr++)
Console.WriteLine("'{0}' found at position {1}.",
results[ctr], matchposition[ctr]);
}
}
// The example displays the following output:
// 'abc' found at position 3.
// 'abc' found at position 7.

A correspondência
A classe Match representa o resultado de uma única correspondência de expressão
regular. É possível acessar objetos Match de duas formas:

Recuperando-os do objeto MatchCollection que é retornado pelo método


Regex.Matches. Para recuperar objetos Match individuais, itere a coleção usando
um constructo foreach (no C#) ou For Each ... Next (no Visual Basic) ou use a
propriedade MatchCollection.Item[] para recuperar um objeto Match específico
por índice ou por nome. Também é possível recuperar objetos Match individuais
da coleção iterando a coleção por índice, de zero a um valor uma vez menor do
que o número de objetos na coleção de dados. Entretanto, esse método não utiliza
a avaliação lenta, pois ele acessa a propriedade MatchCollection.Count.

O exemplo a seguir recupera objetos Match individuais de um objeto


MatchCollection iterando a coleção usando o constructo foreach ou For
Each ... Next . A expressão regular simplesmente corresponde a cadeia de caracteres
“abc” na cadeia de caracteres de entrada.
C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "abc";
string input = "abc123abc456abc789";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("{0} found at position {1}.",
match.Value, match.Index);
}
}
// The example displays the following output:
// abc found at position 0.
// abc found at position 6.
// abc found at position 12.

Ao chamar o método Regex.Match, que retorna um objeto Match que representa a


primeira correspondência em uma cadeia de caracteres ou parte de uma cadeia. É
possível determinar se a correspondência foi localizada recuperando o valor da
propriedade Match.Success . Para recuperar objetos Match que representam
correspondências subsequentes, chame o método Match.NextMatch
repetidamente, até que a propriedade Success do objeto Match retornado seja
false .

O exemplo a seguir usa os métodos Regex.Match(String, String) e


Match.NextMatch para corresponder a cadeia de caracteres "abc" na cadeia de
caracteres de entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "abc";
string input = "abc123abc456abc789";
Match match = Regex.Match(input, pattern);
while (match.Success)
{
Console.WriteLine("{0} found at position {1}.",
match.Value, match.Index);
match = match.NextMatch();
}
}
}
// The example displays the following output:
// abc found at position 0.
// abc found at position 6.
// abc found at position 12.

Duas propriedades da classe Match retornam objetos de coleção:

A propriedade Match.Groups retorna um objeto GroupCollection que contém


informações sobre as subcadeias de caracteres que correspondem grupos de
captura no padrão de expressão regular.

A propriedade Match.Captures retorna um objeto CaptureCollection de uso


limitado. A coleção não é preenchida para um objeto Match cuja propriedade
Success é false . Caso contrário, ela contém um único objeto Capture que possui

as mesmas informações do objeto Match.

Para obter mais informações sobre esses objetos, consulte as seções A coleção de
Grupo e A coleção Capture mais adiante neste tópico.

Duas propriedades adicionais da classe Match oferecem informações sobre a


correspondência. A propriedade Match.Value retorna a subcadeia na cadeia de
caracteres de entrada que corresponde ao padrão de expressão regular. A propriedade
Match.Index retorna a posição inicial baseada em zero da cadeia correspondida na
cadeia de caracteres de entrada.

A classe Match também tem dois métodos de correspondência de padrões:

O método Match.NextMatch localiza a correspondência após a correspondência


representada pelo objeto Match atual e retorna um objeto Match que representa
essa correspondência.

O método Match.Result realiza uma operação de substituição especificada na


cadeia de caracteres correspondida e retorna o resultado.

O exemplo a seguir usa o método Match.Result para inserir um símbolo de $ e um


espaço antes de cada número que inclua dois dígitos fracionais.

C#

using System;
using System.Text.RegularExpressions;
public class Example
{
public static void Main()
{
string pattern = @"\b\d+(,\d{3})*\.\d{2}\b";
string input = "16.32\n194.03\n1,903,672.08";

foreach (Match match in Regex.Matches(input, pattern))


Console.WriteLine(match.Result("$$ $&"));
}
}
// The example displays the following output:
// $ 16.32
// $ 194.03
// $ 1,903,672.08

O padrão de expressão regular \b\d+(,\d{3})*\.\d{2}\b é definido conforme mostrado


na tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

\d+ Corresponde a um ou mais dígitos decimais.

(,\d{3})* Corresponder a zero ou mais ocorrências de uma vírgula seguida por três dígitos
decimais.

\. Corresponder ao caractere de vírgula decimal.

\d{2} Corresponde a dois dígitos decimais.

\b Termina a correspondência em um limite de palavra.

O padrão de substituição $$ $& indica que a subcadeia de caracteres correspondida


deve ser substituída por um símbolo de cifrão ($) (o padrão $$ ), um espaço e o valor da
correspondência (o padrão $& ).

Voltar ao início

A coleção de grupo
A propriedade Match.Groups retorna um objeto GroupCollection que contém Group
objetos que representam grupos capturados em uma única correspondência. O primeiro
objeto Group na coleção (no índice 0) representa a correspondência inteira. Cada objeto
que se segue representa os resultados de um único grupo de captura.
É possível recuperar objetos Group individuais na coleção usando a propriedade
GroupCollection.Item[]. É possível recuperar grupos não nomeados por sua posição
ordinal na coleção e recuperar grupos nomeados por nome ou posição ordinal.
Capturas não nomeadas aparecem primeiro na coleção e são indexadas da esquerda
para a direita na ordem na qual aparecem no padrão de expressão regular. Capturas
nomeadas são indexadas após as capturas não nomeadas, da esquerda para a direita, na
ordem em que aparecem no padrão de expressão regular. Para determinar quais grupos
numerados estão disponíveis na coleção retornada para um método específico de
correspondência de expressão regular, você pode chamar o método da instância
Regex.GetGroupNumbers. Para determinar quais grupos nomeados estão disponíveis na
coleção, você pode chamar o método da instância Regex.GetGroupNames. Os dois
métodos são especificamente úteis em rotinas gerais que analisam as correspondências
encontradas por qualquer expressão regular.

A propriedade GroupCollection.Item[] é o indexador da coleção no C# e a propriedade


padrão do objeto da coleção no Visual Basic. Isso significa que objetos Group
individuais podem ser acessados por índice (ou por nome, no caso de grupos
nomeados) como a seguir:

C#

Group group = match.Groups[ctr];

O exemplo a seguir define uma expressão regular que usa constructos de agrupamento
para capturar o mês, o dia e o ano de uma data.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(\w+)\s(\d{1,2}),\s(\d{4})\b";
string input = "Born: July 28, 1989";
Match match = Regex.Match(input, pattern);
if (match.Success)
for (int ctr = 0; ctr < match.Groups.Count; ctr++)
Console.WriteLine("Group {0}: {1}", ctr,
match.Groups[ctr].Value);
}
}
// The example displays the following output:
// Group 0: July 28, 1989
// Group 1: July
// Group 2: 28
// Group 3: 1989

O padrão de expressão regular \b(\w+)\s(\d{1,2}),\s(\d{4})\b é definido conforme


mostrado na tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra. Este é o primeiro grupo


de captura.

\s Corresponde a um caractere de espaço em branco.

(\d{1,2}) Corresponder a um ou dois dígitos decimais. Este é o segundo grupo de captura.

, Corresponde a uma vírgula.

\s Corresponde a um caractere de espaço em branco.

(\d{4}) Corresponder a quatro dígitos decimais. Este é o terceiro grupo de captura.

\b Termina a correspondência em um limite de palavra.

Voltar ao início

O grupo capturado
A classe Group representa o resultado de um único grupo de captura. Objetos de grupo
que representam os grupos de captura definidos em uma expressão regular são
retornados pela propriedade Item[] do objeto GroupCollection retornado pela
propriedade Match.Groups. A propriedade Item[] é o indexador (no C#) e a propriedade
padrão (no Visual Basic) da classe Group. Você também pode recuperar membros
individuais iterando a coleção usando o constructo foreach ou For Each . Para ver um
exemplo, consulte a seção anterior.

O exemplo a seguir usa constructos de agrupamento aninhados para capturar


subcadeias de caracteres em grupos. O padrão de expressão regular (a(b))c
corresponde à cadeia de caracteres “abc”. Ele atribui a subcadeia “ab” ao primeiro grupo
de captura e a subcadeia de caracteres “b” ao segundo grupo de captura.

C#

var matchposition = new List<int>();


var results = new List<string>();
// Define substrings abc, ab, b.
var r = new Regex("(a(b))c");
Match m = r.Match("abdabc");
for (int i = 0; m.Groups[i].Value != ""; i++)
{
// Add groups to string array.
results.Add(m.Groups[i].Value);
// Record character position.
matchposition.Add(m.Groups[i].Index);
}

// Display the capture groups.


for (int ctr = 0; ctr < results.Count; ctr++)
Console.WriteLine("{0} at position {1}",
results[ctr], matchposition[ctr]);
// The example displays the following output:
// abc at position 3
// ab at position 3
// b at position 4

O exemplo a seguir usa constructos de agrupamento nomeados para capturar


subcadeias de caracteres de uma cadeia que contém dados no formato
“DATANAME:VALUE”, que a expressão regular divide usando dois pontos (:).

C#

var r = new Regex(@"^(?<name>\w+):(?<value>\w+)");


Match m = r.Match("Section1:119900");
Console.WriteLine(m.Groups["name"].Value);
Console.WriteLine(m.Groups["value"].Value);
// The example displays the following output:
// Section1
// 119900

O padrão de expressão regular ^(?<name>\w+):(?<value>\w+) é definido conforme


mostrado na tabela a seguir.

Padrão Descrição

^ Começar a correspondência no início da cadeia de caracteres de entrada.

(? Fazer a correspondência a um ou mais caracteres de palavra. O nome deste grupo


<name>\w+) de captura é name .

: Corresponder a dois pontos.

(? Fazer a correspondência a um ou mais caracteres de palavra. O nome deste grupo


<value>\w+) de captura é value .
As propriedades da classe Group fornecem informações sobre o grupo capturado: a
propriedade Group.Value contém a subcadeia de caracteres capturada, a propriedade
Group.Index indica a posição inicial do grupo capturado no texto de entrada, a

propriedade Group.Length contém o comprimento do texto capturado e a propriedade


Group.Success indica se uma subcadeia de caracteres corresponde ao padrão definido

pelo grupo de captura.

Aplicar quantificadores a um grupo (para obter mais informações, confira


Quantificadores) modifica a relação de uma captura por grupo de captura de duas
formas:

Se o quantificador * ou *? (que especifica zero ou mais correspondências) for


aplicado a um grupo, um grupo de captura pode não ter uma correspondência na
cadeia de caracteres de entrada. Quando não há texto capturado, as propriedades
do objeto Group são definidas como mostrado na tabela a seguir.

Propriedade do grupo Valor

Success false

Value String.Empty

Length 0

O exemplo a seguir ilustra esse cenário. No padrão de expressão regular


aaa(bbb)*ccc , o primeiro grupo de captura (a subcadeia de caracteres “bbb”) pode

ser correspondido a zero ou mais vezes. Como a cadeia de caracteres de entrada


“aaaccc” corresponde ao padrão, o grupo de captura não tem correspondência.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "aaa(bbb)*ccc";
string input = "aaaccc";
Match match = Regex.Match(input, pattern);
Console.WriteLine("Match value: {0}", match.Value);
if (match.Groups[1].Success)
Console.WriteLine("Group 1 value: {0}",
match.Groups[1].Value);
else
Console.WriteLine("The first capturing group has no match.");
}
}
// The example displays the following output:
// Match value: aaaccc
// The first capturing group has no match.

Os quantificadores podem corresponder a diversas ocorrências de um padrão que


é definido por um grupo de captura. Nesse caso, as propriedades Value e Length
de um objeto Group contêm informações somente sobre a última subcadeia de
caracteres capturada. Por exemplo, a seguinte expressão regular corresponde a
uma única sentença que termina com um ponto. Ela utiliza dois constructos de
agrupamento: o primeiro captura palavras individuais, juntamente com um
caractere de espaço em branco; o segundo captura palavras individuais. Como a
saída do exemplo mostra, embora a expressão regular tenha sucesso ao capturar
uma sequência inteira, o segundo grupo de captura só captura a última palavra.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b((\w+)\s?)+\.";
string input = "This is a sentence. This is another sentence.";
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine("Match: " + match.Value);
Console.WriteLine("Group 2: " + match.Groups[2].Value);
}
}
}
// The example displays the following output:
// Match: This is a sentence.
// Group 2: sentence

Voltar ao início

A coleção de captura
O objeto Group contém informações somente sobre a última captura. No entanto, o
conjunto inteiro de capturas realizadas por um grupo de captura ainda estará disponível
do objeto CaptureCollection que é retornado pela propriedade Group.Captures. Cada
membro da coleção é um objeto Capture que representa uma captura realizada por
esse grupo de captura, na ordem na qual elas foram capturadas (e, portanto, na ordem
em que as cadeias capturadas foram correspondidas da esquerda para a direita na
cadeia de caracteres de entrada). Você pode recuperar objetos Capture individuais na
coleção de duas formas:

Ao iterar pela coleção usando um constructo como foreach (no C#) ou For Each
(no Visual Basic).

Ao usar a propriedade CaptureCollection.Item[] para recuperar um objeto


específico por índice. A propriedade Item[] é a propriedade padrão (no Visual
Basic) ou o indexador (no C#) do objeto CaptureCollection.

Se um quantificador não for aplicado a um grupo de captura, o objeto


CaptureCollection conterá um único objeto Capture de pouco interesse, pois ele fornece
informações sobre a mesma correspondência que seu objeto Group. Se um
quantificador for aplicado a um grupo de captura, o objeto CaptureCollection conterá
todas as capturas realizadas pelo grupo de captura, e o último membro da coleção
representará a mesma captura do objeto Group.

Por exemplo, se você usar o padrão de expressão regular ((a(b))c)+ (em que o
quantificador + especifica uma ou mais correspondências) para capturar
correspondências da cadeia de caracteres "abcabcabc", o objeto CaptureCollection para
cada objeto Group conterá três membros.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = "((a(b))c)+";
string input = "abcabcabc";

Match match = Regex.Match(input, pattern);


if (match.Success)
{
Console.WriteLine("Match: '{0}' at position {1}",
match.Value, match.Index);
GroupCollection groups = match.Groups;
for (int ctr = 0; ctr < groups.Count; ctr++) {
Console.WriteLine(" Group {0}: '{1}' at position {2}",
ctr, groups[ctr].Value, groups[ctr].Index);
CaptureCollection captures = groups[ctr].Captures;
for (int ctr2 = 0; ctr2 < captures.Count; ctr2++) {
Console.WriteLine(" Capture {0}: '{1}' at position {2}",
ctr2, captures[ctr2].Value,
captures[ctr2].Index);
}
}
}
}
}
// The example displays the following output:
// Match: 'abcabcabc' at position 0
// Group 0: 'abcabcabc' at position 0
// Capture 0: 'abcabcabc' at position 0
// Group 1: 'abc' at position 6
// Capture 0: 'abc' at position 0
// Capture 1: 'abc' at position 3
// Capture 2: 'abc' at position 6
// Group 2: 'ab' at position 6
// Capture 0: 'ab' at position 0
// Capture 1: 'ab' at position 3
// Capture 2: 'ab' at position 6
// Group 3: 'b' at position 7
// Capture 0: 'b' at position 1
// Capture 1: 'b' at position 4
// Capture 2: 'b' at position 7

O exemplo a seguir usa a expressão regular (Abc)+ para localizar uma ou mais
execuções consecutivas da cadeia de caracteres “Abc” na cadeia
“XYZAbcAbcAbcXYZAbcAb”. O exemplo ilustra o uso da propriedade Group.Captures
para retornar vários grupos de subcadeias de caracteres capturadas.

C#

int counter;
Match m;
CaptureCollection cc;
GroupCollection gc;

// Look for groupings of "Abc".


var r = new Regex("(Abc)+");
// Define the string to search.
m = r.Match("XYZAbcAbcAbcXYZAbcAb");
gc = m.Groups;

// Display the number of groups.


Console.WriteLine("Captured groups = " + gc.Count.ToString());

// Loop through each group.


for (int i = 0; i < gc.Count; i++)
{
cc = gc[i].Captures;
counter = cc.Count;
// Display the number of captures in this group.
Console.WriteLine("Captures count = " + counter.ToString());

// Loop through each capture in the group.


for (int ii = 0; ii < counter; ii++)
{
// Display the capture and its position.
Console.WriteLine(cc[ii] + " Starts at character " +
cc[ii].Index);
}
}
// The example displays the following output:
// Captured groups = 2
// Captures count = 1
// AbcAbcAbc Starts at character 3
// Captures count = 3
// Abc Starts at character 3
// Abc Starts at character 6
// Abc Starts at character 9

Voltar ao início

A captura individual
A classe Capture contém os resultados de uma única captura de subexpressão. A
propriedade Capture.Value contém o texto correspondido, e a propriedade
Capture.Index indica a posição baseada em zero na cadeia de caracteres de entrada na
qual a subcadeia de caracteres correspondida começa.

O exemplo a seguir analisa uma cadeia de caracteres de entrada para a temperatura das
cidades escolhidas. Uma vírgula (“,”) é usada para separar uma cidade e sua
temperatura, enquanto um ponto e vírgula (“;”) é usado para separar os dados de cada
cidade. A cadeia de caracteres de entrada inteira representa uma única correspondência.
No padrão de expressão regular ((\w+(\s\w+)*),(\d+);)+ , usado para analisar a cadeia
de caracteres, o nome da cidade é atribuído ao segundo grupo de captura, enquanto a
temperatura é atribuída ao quarto grupo de captura.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "Miami,78;Chicago,62;New York,67;San
Francisco,59;Seattle,58;";
string pattern = @"((\w+(\s\w+)*),(\d+);)+";
Match match = Regex.Match(input, pattern);
if (match.Success)
{
Console.WriteLine("Current temperatures:");
for (int ctr = 0; ctr < match.Groups[2].Captures.Count; ctr++)
Console.WriteLine("{0,-20} {1,3}",
match.Groups[2].Captures[ctr].Value,
match.Groups[4].Captures[ctr].Value);
}
}
}
// The example displays the following output:
// Current temperatures:
// Miami 78
// Chicago 62
// New York 67
// San Francisco 59
// Seattle 58

A expressão regular é definida como mostrado na tabela a seguir.

Padrão Descrição

\w+ Fazer a correspondência a um ou mais caracteres de palavra.

(\s\w+)* Corresponder a zero ou mais ocorrências de um caractere de espaço em branco


seguido por um ou mais caracteres de palavra. Este padrão corresponde a nomes de
cidade com várias palavras. Este é o terceiro grupo de captura.

(\w+ Corresponder a um ou mais caracteres de palavra seguido por zero ou mais


(\s\w+)*) ocorrências de um caractere de espaço em branco e um ou mais caracteres de
palavra. Este é o segundo grupo de captura.

, Corresponde a uma vírgula.

(\d+) Corresponder a um ou mais dígitos. Este é o quarto grupo de captura.

; Corresponder a um ponto e vírgula.

((\w+ Corresponder ao padrão de uma palavra seguida por qualquer palavra adicional
(\s\w+)*), seguida por uma vírgula, um ou mais dígitos e um ponto e vírgula, uma ou mais
(\d+);)+ vezes. Este é o primeiro grupo de captura.

Confira também
System.Text.RegularExpressions
Expressões regulares do .NET
Linguagem de expressões regulares – referência rápida
Detalhes do comportamento de
expressões regulares
Artigo • 10/05/2023

O mecanismo de expressões regulares do .NET é um correspondente de expressão


regular de retrocesso que incorpora um mecanismo de NFA (Automação Finita Não
Determinística) tradicional, como o usado pelo Perl, Python, Emacs e Tcl. Isso o distingue
de mecanismos de DFA (Autômato finito determinístico) de expressões regulares puras
mais rápidos, porém mais limitados, como os encontrados em awk, egrep ou lex.
Também o distingue de NFAs POSIX padronizados, porém mais lentos. A seção a seguir
descreve os três tipos de mecanismos de expressões regulares e explica por que as
expressões regulares no .NET são implementadas usando um mecanismo de NFA
tradicional.

Benefícios do mecanismo de NFA


Quando mecanismos de DFA executam a correspondência de padrões, a ordem de
processamento é orientada pela cadeia de caracteres de entrada. O mecanismo começa
no início da cadeia de caracteres de entrada e continua sequencialmente para
determinar se o próximo caractere corresponde ao padrão de expressão regular. Podem
assegurar uma correspondência com a cadeia de caracteres mais longa possível. Como
nunca testam o mesmo caractere duas vezes, os mecanismos de DFA não dão suporte
ao retrocesso. No entanto, como um mecanismo de DFA contém somente o estado
finito, não pode corresponder a um padrão com referências inversas; além disso, uma
vez que não constrói uma expansão explícita, não pode capturar subexpressões.

Ao contrário de mecanismos de DFA, quando mecanismos de NFA tradicionais executam


a correspondência de padrões, a ordem de processamento é orientada pelo padrão de
expressão regular. Como processa um elemento de linguagem específico, o mecanismo
usa a correspondência Greedy; ou seja, corresponde à maior parte possível da cadeia de
caracteres de entrada. Contudo, também consegue salvar seu estado correspondendo a
uma subexpressão. Se uma correspondência falhar, o mecanismo poderá retornar para
um estado salvo a fim de tentar correspondências adicionais. Esse processo de
abandonar uma correspondência de subexpressão bem-sucedida para que elementos
de linguagem posteriores na expressão regular também possam corresponder é
conhecido como retrocesso. Os mecanismos de NFA usam o retrocesso para testar todas
as possíveis expansões de uma expressão regular em uma ordem específica e aceitam a
primeira correspondência. Como um mecanismo de NFA tradicional constrói uma
expansão específica da expressão regular para uma correspondência de sucesso, pode
capturar correspondências de subexpressões e referências inversas correspondentes.
Entretanto, como um NFA tradicional retrocede, pode visitar o mesmo estado diversas
vezes se chegar no estado por diferentes caminhos. Como resultado, pode executar de
modo exponencial lentamente na pior das hipóteses. Já que um mecanismo de NFA
tradicional aceita a primeira correspondência que encontra, também pode deixar outras
correspondências (possivelmente mais longas) não descobertas.

Os mecanismos de NFA POSIX são como mecanismos de NFA tradicionais, exceto pelo
fato de continuarem retrocedendo até poderem assegurar que encontraram a
correspondência mais longa possível. Como resultado, um mecanismo de NFA POSIX é
mais lento do que um mecanismo de NFA tradicional; quando você usa um mecanismo
de NFA POSIX, não pode favorecer uma correspondência menor em detrimento de uma
maior alterando a ordem da pesquisa de retrocesso.

Os mecanismos de NFA tradicionais são favorecidos por programadores porque


oferecem maior controle sobre a correspondência da cadeia de caracteres do que
mecanismos de DFA ou NFA POSIX. Embora, na pior das hipóteses, possam ser
executados mais lentamente, você pode orientá-los para encontrar correspondências
em tempo linear ou polinomial usando padrões que reduzem ambiguidades e limitam o
retrocesso. Em outras palavras, embora os mecanismos de NFA troquem desempenho
por força e flexibilidade, na maioria dos casos seu desempenho é bom ou aceitável, se
uma expressão regular for bem escrita e evitar casos em que o retrocesso prejudique
exponencialmente o desempenho.

7 Observação

Para obter informações sobre a penalidade de desempenho causada por retrocesso


excessivo e maneiras de criar uma expressão regular para solucioná-lo, consulte
Retrocesso.

Funcionalidades do mecanismo .NET


Para tirar proveito dos benefícios de um mecanismo de NFA tradicional, o mecanismo
de expressões regulares do .NET inclui um conjunto completo de constructos para
permitir que os programadores conduzam o mecanismo de retrocesso. Tais constructos
podem ser usados para encontrar correspondências mais rapidamente ou favorecer
expansões específicas em detrimento de outras.

Outros recursos do mecanismo de expressões regulares do .NET incluem o seguinte:


Quantificadores lentos: ?? , *? , +? , { n , m }? . Esses constructos instruem o
mecanismo de retrocesso a pesquisar o número mínimo de repetições primeiro.
Por outro lado, quantificadores Greedy comuns tentam corresponder ao número
máximo de repetições primeiro. O exemplo a seguir mostra a diferença entre os
dois. Uma expressão regular corresponde a uma frase que termina em um número
e um grupo de captura deve extrair esse número. A expressão regular .+(\d+)\.
inclui o quantificador Greedy .+ , que faz com que o mecanismo de expressões
regulares capture o último dígito do número. Por outro lado, a expressão regular
.+?(\d+)\. inclui o quantificador lento .+? , que faz com que o mecanismo de
expressões regulares capture o número inteiro.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string greedyPattern = @".+(\d+)\.";
string lazyPattern = @".+?(\d+)\.";
string input = "This sentence ends with the number 107325.";
Match match;

// Match using greedy quantifier .+.


match = Regex.Match(input, greedyPattern);
if (match.Success)
Console.WriteLine("Number at end of sentence (greedy):
{0}",
match.Groups[1].Value);
else
Console.WriteLine("{0} finds no match.", greedyPattern);

// Match using lazy quantifier .+?.


match = Regex.Match(input, lazyPattern);
if (match.Success)
Console.WriteLine("Number at end of sentence (lazy): {0}",
match.Groups[1].Value);
else
Console.WriteLine("{0} finds no match.", lazyPattern);
}
}
// The example displays the following output:
// Number at end of sentence (greedy): 5
// Number at end of sentence (lazy): 107325

As versões Greedy e lenta dessa expressão regular são definidas como mostrado
na tabela a seguir:
Padrão Descrição

.+ Corresponder a pelo menos uma ocorrência de qualquer caractere. Isso faz


(quantificador com que o mecanismo de expressões regulares corresponda à cadeia de
Greedy) caracteres inteira e, em seguida, retroceda da forma necessária para
corresponder ao restante do padrão.

.+? Corresponder a pelo menos uma ocorrência de qualquer caractere, mas ao


(quantificador menor número possível.
lento)

(\d+) Corresponder a pelo menos um caractere numérico e atribuí-lo ao primeiro


grupo de captura.

\. Corresponde a um ponto final.

Para obter mais informações sobre quantificadores lentos, confira Quantificadores.

Lookahead positivo: (?= subexpression ) . Esse recurso permite que o mecanismo


de retrocesso retorne ao mesmo ponto no texto após corresponder a uma
subexpressão. É útil para pesquisar em todo o texto verificando vários padrões que
iniciam na mesma posição. Também permite que o mecanismo verifique se existe
uma subcadeia de caracteres no final da correspondência sem incluir a subcadeia
de caracteres no texto correspondente. O exemplo a seguir usa lookahead positivo
para extrair as palavras de uma frase que não são seguidas por símbolos de
pontuação.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b[A-Z]+\b(?=\P{P})";
string input = "If so, what comes next?";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// If
// what
// comes
A expressão regular \b[A-Z]+\b(?=\P{P}) é definida conforme mostrado na tabela
a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

[A-Z]+ Corresponder a qualquer caractere alfabético uma ou mais vezes. Como o


método Regex.Matches é chamado com a opção RegexOptions.IgnoreCase, essa
comparação não diferencia maiúsculas de minúsculas.

\b Termina a correspondência em um limite de palavra.

(? Antecipe para determinar se o próximo caractere é um símbolo de pontuação. Se


=\P{P}) não for, a correspondência será bem-sucedida.

Para obter mais informações sobre as asserções de lookahead positivo, consulte


Constructos de agrupamento.

Lookahead negativo: (?! subexpression ) . Esse recurso adiciona a capacidade de


corresponder a uma expressão somente se uma subexpressão não corresponder.
Ele é indicado para refinar uma pesquisa, pois, muitas vezes, é mais simples
fornecer uma expressão para um caso que deve ser eliminado do que para casos
que precisam ser incluídos. Por exemplo, é difícil escrever uma expressão para
palavras que não começam com “non”. O exemplo a seguir usa lookahead
negativo para excluir.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string pattern = @"\b(?!non)\w+\b";
string input = "Nonsense is not always non-functional.";
foreach (Match match in Regex.Matches(input, pattern,
RegexOptions.IgnoreCase))
Console.WriteLine(match.Value);
}
}
// The example displays the following output:
// is
// not
// always
// functional
O padrão de expressão regular \b(?!non)\w+\b é definido conforme mostrado na
tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(?!non) Antecipar para garantir que a cadeia de caracteres atual não comece com “non”.
Se isso acontecer, a correspondência falha.

(\w+) Fazer a correspondência a um ou mais caracteres de palavra.

\b Termina a correspondência em um limite de palavra.

Para obter mais informações sobre as asserções de lookahead negativo, consulte


Constructos de agrupamento.

Avaliação condicional: (?( expression ) yes | no ) and (?( name ) yes | no ) , onde
expression é uma subexpressão para corresponder, name é o nome de um grupo
de captura, yes é a cadeia de caracteres para corresponder se expression for
correspondente ou se name for um grupo capturado válido não vazio e no é a
subexpressão para corresponder se expression não é correspondente ou se name
não for um grupo capturado válido não vazio. Esse recurso permite que o
mecanismo pesquise usando mais de um padrão alternativo, dependendo do
resultado de uma correspondência de subexpressão anterior ou do resultado de
uma asserção de largura zero. Isso possibilita uma forma mais potente de
referência inversa que permite, por exemplo, corresponder a uma subexpressão
com base no fato de uma subexpressão anterior ser correspondente. A expressão
regular no exemplo a seguir corresponde a parágrafos que são destinados a uso
público e interno. Os parágrafos destinados apenas a uso interno começam com
uma marca <PRIVATE> . O padrão de expressão regular ^(?<Pvt>\<PRIVATE\>\s)?(?
(Pvt)((\w+\p{P}?\s)+)|((\w+\p{P}?\s)+))\r?$ usa avaliação condicional para
atribuir o conteúdo de parágrafos destinados a uso público e interno a grupos de
captura separados. Esses parágrafos podem ser tratados de maneiras diferentes.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string input = "<PRIVATE> This is not for public consumption."
+ Environment.NewLine +
"But this is for public consumption." +
Environment.NewLine +
"<PRIVATE> Again, this is confidential.\n";
string pattern = @"^(?<Pvt>\<PRIVATE\>\s)?(?(Pvt)((\w+\p{P}?
\s)+)|((\w+\p{P}?\s)+))\r?$";
string publicDocument = null, privateDocument = null;

foreach (Match match in Regex.Matches(input, pattern,


RegexOptions.Multiline))
{
if (match.Groups[1].Success)
{
privateDocument += match.Groups[1].Value + "\n";
}
else
{
publicDocument += match.Groups[3].Value + "\n";
privateDocument += match.Groups[3].Value + "\n";
}
}

Console.WriteLine("Private Document:");
Console.WriteLine(privateDocument);
Console.WriteLine("Public Document:");
Console.WriteLine(publicDocument);
}
}
// The example displays the following output:
// Private Document:
// This is not for public consumption.
// But this is for public consumption.
// Again, this is confidential.
//
// Public Document:
// But this is for public consumption.

O padrão de expressão regular é definido como mostra a tabela a seguir.

Padrão Descrição

^ Começar a correspondência no início de uma linha.

(?<Pvt>\ Corresponder a zero ou uma ocorrência da cadeia de caracteres


<PRIVATE\>\s)? <PRIVATE> seguida para um caractere de espaço em branco. Atribuir a
correspondência a um grupo de captura chamado Pvt .

(?(Pvt) Se o grupo de captura Pvt existir, corresponder a uma ou mais


((\w+\p{P}? ocorrências de um ou mais caracteres de palavra seguidos por zero ou
\s)+) um separador de pontuação, seguido por um caractere de espaço em
branco. Atribuir a subcadeia de caracteres ao primeiro grupo de captura.
Padrão Descrição

|((\w+\p{P}? Se o grupo de captura Pvt não existir, corresponder a uma ou mais


\s)+)) ocorrências de um ou mais caracteres de palavra seguidos por zero ou
um separador de pontuação, seguido por um caractere de espaço em
branco. Atribuir a subcadeia de caracteres ao terceiro grupo de captura.

\r?$ Corresponder ao final de uma linha ou ao final da cadeia de caracteres.

Para obter mais informações sobre a avaliação condicional, consulte Constructos


de alternância.

Equilibrando definições do grupo: (?< name1 - name2 > subexpression ) . Esse


recurso permite que o mecanismo de expressões regulares controle constructos
aninhados, como parênteses ou colchetes de abertura e fechamento. Para ver um
exemplo, consulte Constructos de agrupamento.

Grupos atômicos: (?> subexpressão ) . Esse recurso permite que o mecanismo de


retrocesso assegure que uma subexpressão corresponda apenas à primeira
correspondência encontrada para ela, como se a expressão estivesse sendo
executada independentemente da expressão que a contém. Se você não usar esse
constructo, as pesquisas de retrocesso de expressões maiores poderão alterar o
comportamento de uma subexpressão. Por exemplo, a expressão regular (a+)\w
corresponde a um ou mais caracteres "a", juntamente com um caractere de palavra
que segue a sequência de caracteres "a", e atribui a sequência de caracteres "a" ao
primeiro grupo de captura. No entanto, se o caractere final da cadeia de caracteres
de entrada também for "a", será correspondido pelo elemento de linguagem \w e
não estará incluído no grupo capturado.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "aaaaa", "aaaaab" };
string backtrackingPattern = @"(a+)\w";
Match match;

foreach (string input in inputs)


{
Console.WriteLine("Input: {0}", input);
match = Regex.Match(input, backtrackingPattern);
Console.WriteLine(" Pattern: {0}", backtrackingPattern);
if (match.Success)
{
Console.WriteLine(" Match: {0}", match.Value);
Console.WriteLine(" Group 1: {0}",
match.Groups[1].Value);
}
else
{
Console.WriteLine(" Match failed.");
}
}
Console.WriteLine();
}
}
// The example displays the following output:
// Input: aaaaa
// Pattern: (a+)\w
// Match: aaaaa
// Group 1: aaaa
// Input: aaaaab
// Pattern: (a+)\w
// Match: aaaaab
// Group 1: aaaaa

A expressão regular ((?>a+))\w impede esse comportamento. Como todos os


caracteres “a” consecutivos são correspondidos sem retrocesso, o primeiro grupo
de captura inclui todos os caracteres “a” consecutivos. Se os caracteres “a” não
forem seguidos por pelo menos um caractere diferente de “a”, a correspondência
falhará.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "aaaaa", "aaaaab" };
string nonbacktrackingPattern = @"((?>a+))\w";
Match match;

foreach (string input in inputs)


{
Console.WriteLine("Input: {0}", input);
match = Regex.Match(input, nonbacktrackingPattern);
Console.WriteLine(" Pattern: {0}",
nonbacktrackingPattern);
if (match.Success)
{
Console.WriteLine(" Match: {0}", match.Value);
Console.WriteLine(" Group 1: {0}",
match.Groups[1].Value);
}
else
{
Console.WriteLine(" Match failed.");
}
}
Console.WriteLine();
}
}
// The example displays the following output:
// Input: aaaaa
// Pattern: ((?>a+))\w
// Match failed.
// Input: aaaaab
// Pattern: ((?>a+))\w
// Match: aaaaab
// Group 1: aaaaa

Para saber mais sobre grupos atômicos, confira Constructos de Agrupamento.

Correspondência da direita para a esquerda, que é especificada fornecendo a


opção RegexOptions.RightToLeft para um construtor de classe Regex ou um
método de correspondência de instância estática. Esse recurso é útil durante a
pesquisa da direita para a esquerda em vez da esquerda para direita ou nos casos
em que é mais eficiente iniciar uma correspondência na parte direita do padrão
em vez de à esquerda. Como mostra o exemplo a seguir, o uso da
correspondência da direita para esquerda pode alterar o comportamento de
quantificadores Greedy. O exemplo realiza duas pesquisas por uma frase que
termina em número. A pesquisa da esquerda para a direita que usa o quantificador
Greedy + corresponde a um dos seis dígitos na frase, enquanto a pesquisa da
direita para a esquerda corresponde a todos os seis dígitos. Para obter uma
descrição do padrão de expressão regular, consulte o exemplo que ilustra
quantificadores lentos, no início desta seção.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string greedyPattern = @".+(\d+)\.";
string input = "This sentence ends with the number 107325.";
Match match;
// Match from left-to-right using lazy quantifier .+?.
match = Regex.Match(input, greedyPattern);
if (match.Success)
Console.WriteLine("Number at end of sentence (left-to-
right): {0}",
match.Groups[1].Value);
else
Console.WriteLine("{0} finds no match.", greedyPattern);

// Match from right-to-left using greedy quantifier .+.


match = Regex.Match(input, greedyPattern,
RegexOptions.RightToLeft);
if (match.Success)
Console.WriteLine("Number at end of sentence (right-to-
left): {0}",
match.Groups[1].Value);
else
Console.WriteLine("{0} finds no match.", greedyPattern);
}
}
// The example displays the following output:
// Number at end of sentence (left-to-right): 5
// Number at end of sentence (right-to-left): 107325

Para obter mais informações sobre a correspondência da direita para a esquerda,


consulte Opções de expressões regulares.

Lookbehind positivo e negativo: (?<= subexpression ) para lookbehind positivo e


(?<! subexpression ) para lookbehind negativo. Esse recurso é semelhante ao

lookahead, que é discutido neste tópico. Como o mecanismo de expressões


regulares possibilita uma correspondência completa da direita para a esquerda,
expressões regulares permitem lookbehinds irrestritos. O lookbehind positivo e
negativo também pode ser usado para evitar o aninhamento de quantificadores
quando a subexpressão aninhada é um superconjunto de uma expressão externa.
Expressões regulares com tais quantificadores aninhados geralmente oferecem um
desempenho ruim. Por exemplo, o exemplo a seguir verifica se uma cadeia de
caracteres começa e termina com um caractere alfanumérico e se qualquer outro
caractere na cadeia de caracteres faz parte de um subconjunto maior. Faz parte da
expressão regular usada para validar endereços de email. Para obter mais
informações, consulte Como verificar se cadeias de caracteres estão em um
formato de email válido.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string[] inputs = { "jack.sprat", "dog#", "dog#1", "me.myself",
"me.myself!" };
string pattern = @"^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-
Z0-9])$";
foreach (string input in inputs)
{
if (Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase))
Console.WriteLine("{0}: Valid", input);
else
Console.WriteLine("{0}: Invalid", input);
}
}
}
// The example displays the following output:
// jack.sprat: Valid
// dog#: Invalid
// dog#1: Valid
// me.myself: Valid
// me.myself!: Invalid

A expressão regular ^[A-Z0-9]([-!#$%&'.*+/=?^`{}|~\w])*(?<=[A-Z0-9])$ é


definida conforme mostrado na tabela a seguir.

Padrão Descrição

^ Começar a correspondência no início da cadeia de caracteres.

[A-Z0-9] Corresponder a qualquer caractere numérico ou alfanumérico. (A


comparação não diferencia maiúsculas de minúsculas.)

([-!#$%&'.*+/=? Corresponder a zero ou mais ocorrências de qualquer caractere de


^`{}|~\w])* palavra ou qualquer um destes caracteres: -, !, #, $, %, &, ', ., *, +, /, =, ?,
^, `, {, }, | ou ~.

(?<=[A-Z0-9]) Olhar para o caractere anterior, que precisa ser numérico ou


alfanumérico. (A comparação não diferencia maiúsculas de minúsculas.)

$ Encerrar a correspondência ao final da cadeia de caracteres.

Para obter mais informações sobre lookbehind positivo e negativo, consulte


Constructos de agrupamento.

Artigos relacionados
Título Descrição
Título Descrição

Retrocesso Fornece informações sobre como o retrocesso de expressões regulares


se ramifica para encontrar correspondências alternativas.

Compilação e Fornece informações sobre a compilação e a reutilização de expressões


reutilização regulares para aumentar o desempenho.

Acesso thread-safe Fornece informações sobre a segurança de thread de expressões


regulares e explica quando você deve sincronizar o acesso a objetos de
expressão regular.

Expressões regulares do Fornece uma visão geral sobre o aspecto de linguagem de


.NET programação das expressões regulares.

O modelo de objeto de Oferece informações e exemplos de código que mostram como usar as
expressão regular classes de expressão regular.

Linguagem de Oferece informações a respeito do conjunto de caracteres, operadores


expressões regulares – e constructos que você pode usar para definir expressões regulares.
referência rápida

Referência
System.Text.RegularExpressions
Retrocesso em expressões regulares
Artigo • 11/08/2023

O retrocesso ocorre quando um padrão de expressão regular contém quantificadores


opcionais ou constructos de alternância e o mecanismo de expressões regulares retorna
a um estado salvo anterior para retomar sua pesquisa por uma correspondência. O
retrocesso é indispensável para o poder das expressões regulares, ele permite que as
expressões sejam poderosas e flexíveis e correspondam a padrões muito complexos. No
entanto, todo esse poder tem um custo. O retrocesso muitas vezes é o fator individual
que mais afeta o desempenho do mecanismo de expressões regulares. Felizmente, o
desenvolvedor tem controle sobre o comportamento do mecanismo de expressões
regulares e como ele usa o retrocesso. Este tópico explica como o retrocesso funciona e
como ele pode ser controlado.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

Comparação linear sem retrocesso


Se um padrão de expressão regular não tem quantificadores ou constructos de
alternância opcionais, o mecanismo de expressões regulares é executado em tempo
linear. Ou seja, depois que o mecanismo de expressões regulares corresponde o
primeiro elemento de linguagem no padrão com o texto da cadeia de caracteres de
entrada, ele tenta corresponder o elemento de linguagem seguinte no padrão com o
próximo caractere ou grupo de caracteres na cadeia de caracteres de entrada. Esse
processo continuará até que a correspondência obtenha êxito ou falhe. Em ambos os
casos, o mecanismo de expressões regulares avança um caractere de cada vez na cadeia
de caracteres de entrada.

O exemplo a seguir ilustra esse cenário. A expressão regular e{2}\w\b procura duas
ocorrências da letra “e” seguidas por qualquer caractere de palavra seguido por um
limite de palavra.

C#
using System;
using System.Text.RegularExpressions;

public class Example1


{
public static void Run()
{
string input = "needing a reed";
string pattern = @"e{2}\w\b";
foreach (Match match in Regex.Matches(input, pattern))
Console.WriteLine("{0} found at position {1}",
match.Value, match.Index);
}
}
// The example displays the following output:
// eed found at position 11

Embora essa expressão regular inclua o quantificador {2} , ela é avaliada de uma
maneira linear. O mecanismo de expressões regulares não retrocede porque {2} não é
um quantificador opcional, ele especifica um número exato e não um número variável
de vezes que a subexpressão anterior deve corresponder. Como resultado, o mecanismo
de expressões regulares tenta corresponder o padrão da expressão regular com a
cadeia de caracteres de entrada conforme mostrado na tabela a seguir.

Operação Posição no Posição na cadeia de Result


padrão caracteres

1 e "needing a reed" (índice 0) Nenhuma correspondência.

2 e "eeding a reed" (índice 1) Possível correspondência.

3 e{2} "eding a reed" (índice 2) Possível correspondência.

4 \w "ding a reed" (índice 3) Possível correspondência.

5 \b "ing a reed" (índice 4) Possível falha de


correspondência.

6 e "eding a reed" (índice 2) Possível correspondência.

7 e{2} "ding a reed" (índice 3) Possível falha de


correspondência.

8 e "ding a reed" (índice 3) Falha de correspondência.

9 e "ing a reed" (índice 4) Nenhuma correspondência.

10 e "ng a reed" (índice 5) Nenhuma correspondência.

11 e "g a reed" (índice 6) Nenhuma correspondência.


Operação Posição no Posição na cadeia de Result
padrão caracteres

12 e " a reed" (índice 7) Nenhuma correspondência.

13 e "a reed" (índice 8) Nenhuma correspondência.

14 e " reed" (índice 9) Nenhuma correspondência.

15 e "reed" (índice 10) Nenhuma correspondência

16 e "eed" (índice 11) Possível correspondência.

17 e{2} "ed" (índice 12) Possível correspondência.

18 \w "d" (índice 13) Possível correspondência.

19 \b "" (índice 14) Correspondência.

Se um padrão de expressão regular não inclui nenhum quantificador ou construtor de


alternância opcional, o número máximo de comparações necessárias para corresponder
ao padrão da expressão regular com a cadeia de caracteres de entrada é
aproximadamente equivalente ao número de caracteres na cadeia de caracteres de
entrada. Nesse caso, o mecanismo de expressões regulares usa 19 comparações para
identificar possíveis correspondências nesta cadeia de 13 caracteres. Em outras palavras,
o mecanismo de expressões regulares é executado em tempo quase linear se não
contém quantificadores ou construtores de alternância opcionais.

Retrocesso com quantificadores opcionais ou


constructos de alternância
Quando uma expressão regular inclui quantificadores ou construtores de alternância
opcionais, a avaliação da cadeia de caracteres de entrada deixa de ser linear. A
correspondência de padrões com um mecanismo NFA (Nondeterministic Finite
Automaton) é orientada pelos elementos de linguagem da expressão regular e não
pelos caracteres a serem correspondidos na cadeia de caracteres de entrada. Assim, o
mecanismo de expressões regulares tenta fazer a correspondência total de
subexpressões opcionais ou alternativas. Quando ele avança para o elemento de
linguagem seguinte na subexpressão e a correspondência falha, o mecanismo de
expressões regulares pode abandonar uma parte de sua correspondência bem-sucedida
e retornar a um estado salvo anteriormente com o objetivo de corresponder a
expressão regular inteira com a cadeia de caracteres de entrada. Esse processo de
retornar a um estado salvo anterior para localizar uma correspondência é conhecido
como o retrocesso.
Por exemplo, considere o padrão de expressão regular .*(es) , o qual corresponde os
caracteres “es” e todos os caracteres que os precedem. Como mostra o exemplo a
seguir, se a cadeia de caracteres de entrada é "Essential services are provided by regular
expressions." (Serviços essenciais são fornecidos por expressões regulares.), o padrão
corresponde a cadeia de caracteres até o “es” (inclusive) em "expressions”.

C#

using System;
using System.Text.RegularExpressions;

public class Example2


{
public static void Run()
{
string input = "Essential services are provided by regular
expressions.";
string pattern = ".*(es)";
Match m = Regex.Match(input, pattern, RegexOptions.IgnoreCase);
if (m.Success)
{
Console.WriteLine("'{0}' found at position {1}",
m.Value, m.Index);
Console.WriteLine("'es' found at position {0}",
m.Groups[1].Index);
}
}
}
// 'Essential services are provided by regular expressions found at
position 0
// 'es' found at position 47

Para fazer isso, o mecanismo de expressões regulares usa o retrocesso da seguinte


forma:

Ele corresponde o .* (que corresponde a zero, uma ou mais ocorrências de


qualquer caractere) com a cadeia de caracteres de entrada inteira.

Ele tenta corresponder “e” no padrão da expressão regular. No entanto, a cadeia


de caracteres de entrada não tem nenhum caractere restante disponível para
corresponder.

Ele retrocede para sua última correspondência bem-sucedida, "Essential services


are provided by regular expressions", e tenta corresponder “e” com o ponto no
final da frase. A correspondência falha.

Ele continua a retroceder para uma correspondência bem-sucedida anterior um


caractere de cada vez até que a subcadeia de caracteres provisória correspondente
seja “Essential services are provided by regular expr". Ele então compara o “e” no
padrão com o segundo “e” em “expressions” e encontra uma correspondência.

Ele compara o “s” no padrão com o “s” após o caractere “e” que já foi
correspondido (o primeiro “s” em “expressions”). A correspondência é bem-
sucedida.

Quando o retrocesso é usado, corresponder o padrão de expressão regular com a


cadeia de caracteres de entrada, que tem 55 caracteres de comprimento, requer 67
operações de comparação. Geralmente, se um padrão de expressão regular tem um
único constructo de alternância ou um único quantificador opcional, o número de
operações de comparação necessárias para corresponder ao padrão é mais que duas
vezes maior do que o número de caracteres na cadeia de caracteres de entrada.

Retrocesso com quantificadores opcionais


aninhados
O número de operações de comparação necessárias para corresponder a um padrão de
expressão regular pode aumentar exponencialmente se o padrão inclui um grande
número de construtores de alternância, se ele inclui construtores de alternância
aninhados ou, mais comumente, se ele inclui quantificadores opcionais aninhados. Por
exemplo, o padrão de expressão regular ^(a+)+$ foi criado para corresponder a uma
cadeia de caracteres completa que contém um ou mais caracteres “a”. O exemplo
fornece duas cadeias de caracteres de entrada de comprimento idêntico, mas somente a
primeira cadeia de caracteres corresponde ao padrão. A classe
System.Diagnostics.Stopwatch é usada para determinar a duração da operação de
correspondência.

C#

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example3


{
public static void Run()
{
string pattern = "^(a+)+$";
string[] inputs = { "aaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaaaaaaaaaaaaaaaaaaaaaaaaa!" };
Regex rgx = new Regex(pattern);
Stopwatch sw;

foreach (string input in inputs)


{
sw = Stopwatch.StartNew();
Match match = rgx.Match(input);
sw.Stop();
if (match.Success)
Console.WriteLine($"Matched {match.Value} in {sw.Elapsed}");
else
Console.WriteLine($"No match found in {sw.Elapsed}");
}
}
}
// Matched aaaaaaaaaaaaaaaaaaaaaaaaaaa in 00:00:00.0018281
// No match found in 00:00:05.1882144

Como mostra a saída do exemplo, o mecanismo de expressão regular demorou


significativamente mais para descobrir que uma cadeia de caracteres de entrada não
correspondeu ao padrão, em relação ao tempo necessário para identificar uma cadeia de
caracteres correspondente. Isso acontece porque uma correspondência malsucedida
sempre representa um cenário de pior caso. O mecanismo de expressões regulares deve
usar a expressão regular para seguir todos os caminhos possíveis através dos dados
antes de concluir que a correspondência falhou e os parênteses aninhados criam vários
caminhos adicionais nos dados. O mecanismo de expressões regulares conclui que a
segunda cadeia de caracteres não correspondeu ao padrão ao fazer o seguinte:

Ele verifica que estava no início da cadeia de caracteres e então corresponde os


primeiros cinco caracteres da cadeia de caracteres com o padrão a+ . Ele então
determina que não há grupos adicionais de caracteres “a” na cadeia de caracteres.
Finalmente, ele testa o final da cadeia de caracteres. Como um caractere adicional
permanece na cadeia de caracteres, a correspondência falha. Essa correspondência
com falha requer 9 comparações. O mecanismo de expressão regular também
salva informações de estado de suas correspondências de "a" (que chamaremos de
correspondência 1), "aa" (correspondência 2), "aaaa" (correspondência 3) e "aaaa"
(correspondência 4).

Ele retorna à correspondência 4 salva anteriormente. Ele determina que há um


caractere adicional “a” a ser atribuído a um grupo capturado adicional. Finalmente,
ele testa o final da cadeia de caracteres. Como um caractere adicional permanece
na cadeia de caracteres, a correspondência falha. Essa correspondência com falha
requer 4 comparações. Até agora, foi executado um total de 13 comparações.

Ele retorna à correspondência 3 salva anteriormente. Ele determina que há dois


caracteres adicionais “a” a serem atribuídos a um grupo capturado adicional. No
entanto, o teste de fim da cadeia de caracteres falha. Em seguida, ele retorna para
corresponder a 3 e tenta corresponder aos dois caracteres "a" adicionais em dois
grupos capturados adicionais. No entanto, o teste de fim da cadeia de caracteres
continua a falhar. Essas correspondências com falha exigem 12 comparações. Até
agora, foi executado um total de 25 comparações.

A comparação de cadeia de caracteres de entrada com a expressão regular continuará


dessa forma até que o mecanismo de expressão regular tente todas as combinações
possíveis de correspondências e conclua que não há nenhuma correspondência. Devido
aos quantificadores aninhados, essa comparação é O(2n) ou uma operação exponencial,
em que n é o número de caracteres na cadeia de caracteres de entrada. Isso significa
que, no pior caso, uma cadeia de caracteres de entrada com 30 caracteres requer
aproximadamente 1.073.741.824 comparações e uma cadeia de caracteres de entrada
com 40 caracteres requer aproximadamente 1.099.511.627.776 comparações. Se você
usar cadeias de caracteres com esses tamanhos ou até mesmo com tamanhos maiores,
os métodos de expressões regulares poderão demorar um tempo extremamente longo
para terminar ao processarem uma entrada que não correspondam ao padrão de
expressão regular.

Controlar o retrocesso
O retrocesso permite a você criar expressões regulares avançadas e flexíveis. No
entanto, conforme mostrado na seção anterior, esses benefícios podem estar associados
a um baixo desempenho inaceitável. Para evitar o retrocesso excessivo, você deve
definir um intervalo de tempo limite no qual você criará uma instância de um objeto
Regex ou chamará um método de correspondência de expressão regular estático. Isso é
abordado na próxima seção. Além disso, o .NET dá suporte a três elementos de
linguagem de expressão regular que limitam ou suprimem o retrocesso e que dão
suporte a expressões regulares complexas com pouca ou nenhuma penalidade de
desempenho: grupos atômicos, asserções lookbehind e asserções lookahead. Para obter
mais informações sobre cada elemento de linguagem, confira Construções de
agrupamento.

Mecanismo de expressão regular sem retrocesso


Se você não precisar usar construções que exijam retrocesso (por exemplo, soluções
alternativas, referências inversas ou grupos atômicos), considere usar o modo
RegexOptions.NonBacktracking. Esse modo foi projetado para ser executado no tempo
proporcional ao comprimento da entrada. Para obter mais informações, confira o modo
NonBacktracking. Você também pode definir um valor de tempo limite.

Limitar o tamanho das entradas


Algumas expressões regulares têm desempenho aceitável, a menos que a entrada seja
excepcionalmente grande. Se todas as entradas de texto razoáveis em seu cenário
forem conhecidas por estarem sob um determinado comprimento, considere rejeitar
entradas mais longas antes de aplicar a expressão regular a elas.

Especificar um intervalo de tempo limite


Você pode definir um valor de tempo limite que representa o intervalo mais longo
durante o qual o mecanismo de expressão regular pesquisará uma única
correspondência antes de abandonar a tentativa e gerar uma exceção
RegexMatchTimeoutException. Você especifica o intervalo de tempo limite ao fornecer
um valor de TimeSpan para o construtor Regex(String, RegexOptions, TimeSpan) para
instanciar expressões regulares. Além disso, cada método de correspondência de
padrão estático tem uma sobrecarga com um parâmetro TimeSpan que permite a você
especificar um valor de tempo limite.

Se você não definir um valor de tempo limite explicitamente, o valor de tempo limite
padrão será determinado da seguinte maneira:

Usando o valor de tempo limite de todo o aplicativo, se existir um. Esse pode ser
qualquer valor de tempo limite que se aplica ao domínio do aplicativo no qual o
objeto Regex é instanciado ou a chamada de método estático é feita. Você pode
definir o valor de tempo limite de todo o aplicativo chamando o método
AppDomain.SetData para atribuir a representação de cadeia de caracteres de um
valor TimeSpan à propriedade "REGEX_DEFAULT_MATCH_TIMEOUT".
Usando o valor InfiniteMatchTimeout, se nenhum valor de tempo limite de todo o
aplicativo tiver sido definido.

Por padrão, o intervalo de tempo limite é definido para Regex.InfiniteMatchTimeout, o


que significa que o mecanismo de expressões regulares nunca excede o tempo limite.

) Importante

Se você não usa RegexOptions.NonBacktracking, recomendamos que você sempre


defina um intervalo de tempo limite se sua expressão regular depender de
rastreamento inverso ou operar em entradas não confiáveis.

Uma exceção RegexMatchTimeoutException indica que o mecanismo de expressões


regulares não pôde localizar uma correspondência dentro do intervalo de tempo limite
especificado, mas não indica como a exceção foi gerada. O motivo pode ser um
retrocesso excessivo, mas também é possível que o intervalo de tempo limite tenha sido
definido muito baixo, dada a carga do sistema no momento em que a exceção foi
gerada. Ao tratar a exceção, você pode escolher entre abandonar as correspondências
adicionais com a cadeia de caracteres de entrada ou aumentar o intervalo de tempo
limite e repetir a operação de correspondência.

Por exemplo, o código a seguir chama o construtor Regex(String, RegexOptions,


TimeSpan) para instanciar um objeto Regex com um valor de tempo limite de 1
segundo. O padrão de expressão regular (a+)+$ , que faz a correspondência de uma ou
várias sequências de um ou mais caracteres de “a” no final de uma linha, está sujeito ao
retrocesso excessivo. Se um RegexMatchTimeoutException for gerado, o exemplo
aumentará o valor de tempo limite para um intervalo máximo de 3 segundos. Depois
disso, ele abandona a tentativa de corresponder o padrão.

C#

using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Security;
using System.Text.RegularExpressions;
using System.Threading;

public class Example


{
const int MaxTimeoutInSeconds = 3;

public static void Main()


{
string pattern = @"(a+)+$"; // DO NOT REUSE THIS PATTERN.
Regex rgx = new Regex(pattern, RegexOptions.IgnoreCase,
TimeSpan.FromSeconds(1));
Stopwatch? sw = null;

string[] inputs = { "aa", "aaaa>",


"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
"aaaaaaaaaaaaaaaaaaaaaa>",
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>" };

foreach (var inputValue in inputs)


{
Console.WriteLine("Processing {0}", inputValue);
bool timedOut = false;
do
{
try
{
sw = Stopwatch.StartNew();
// Display the result.
if (rgx.IsMatch(inputValue))
{
sw.Stop();
Console.WriteLine(@"Valid: '{0}' ({1:ss\.fffffff}
seconds)",
inputValue, sw.Elapsed);
}
else
{
sw.Stop();
Console.WriteLine(@"'{0}' is not a valid string.
({1:ss\.fffff} seconds)",
inputValue, sw.Elapsed);
}
}
catch (RegexMatchTimeoutException e)
{
sw.Stop();
// Display the elapsed time until the exception.
Console.WriteLine(@"Timeout with '{0}' after
{1:ss\.fffff}",
inputValue, sw.Elapsed);
Thread.Sleep(1500); // Pause for 1.5 seconds.

// Increase the timeout interval and retry.


TimeSpan timeout =
e.MatchTimeout.Add(TimeSpan.FromSeconds(1));
if (timeout.TotalSeconds > MaxTimeoutInSeconds)
{
Console.WriteLine("Maximum timeout interval of {0}
seconds exceeded.",
MaxTimeoutInSeconds);
timedOut = false;
}
else
{
Console.WriteLine("Changing the timeout interval to
{0}",
timeout);
rgx = new Regex(pattern, RegexOptions.IgnoreCase,
timeout);
timedOut = true;
}
}
} while (timedOut);
Console.WriteLine();
}
}
}
// The example displays output like the following :
// Processing aa
// Valid: 'aa' (00.0000779 seconds)
//
// Processing aaaa>
// 'aaaa>' is not a valid string. (00.00005 seconds)
//
// Processing aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
// Valid: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa' (00.0000043
seconds)
//
// Processing aaaaaaaaaaaaaaaaaaaaaa>
// Timeout with 'aaaaaaaaaaaaaaaaaaaaaa>' after 01.00469
// Changing the timeout interval to 00:00:02
// Timeout with 'aaaaaaaaaaaaaaaaaaaaaa>' after 02.01202
// Changing the timeout interval to 00:00:03
// Timeout with 'aaaaaaaaaaaaaaaaaaaaaa>' after 03.01043
// Maximum timeout interval of 3 seconds exceeded.
//
// Processing aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>
// Timeout with 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa>' after
03.01018
// Maximum timeout interval of 3 seconds exceeded.

Grupos atômicos.
O elemento de linguagem (?> subexpressão ) é um agrupamento atômico. Ele impede o
rastreamento inverso para a subexpressão. Depois que esse elemento de linguagem for
correspondido com êxito, ele não abrirá mão de nenhuma parte de sua correspondência
para o retrocesso subsequente. Por exemplo, no padrão (?>\w*\d*)1 , se o 1 não puder
ser correspondido, o \d* não abrirá mão de nenhuma de suas correspondências,
mesmo que isso signifique que ela permitiria a correspondência com êxito de 1 . Grupos
atômicos podem ajudar a evitar os problemas de desempenho associados a
correspondências com falha.

O exemplo a seguir ilustra como suprimir o retrocesso melhora o desempenho quando


quantificadores aninhados são usados. Ele mede o tempo necessário para que o
mecanismo de expressão regular determine que uma cadeia de caracteres de entrada
não corresponde a duas expressões regulares. A primeira expressão regular usa o
retrocesso para tentar corresponder uma cadeia de caracteres que contém uma ou mais
ocorrências de um ou mais dígitos hexadecimais, seguidos por dois-pontos, seguido por
um ou mais dígitos hexadecimais, seguidos por dois dois-pontos. A segunda expressão
regular é idêntica à primeira, exceto que ela desabilita o retrocesso. Como a saída do
exemplo mostra, a melhora do desempenho resultante da desabilitação do retrocesso é
significativa.

C#

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example4


{
public static void Run()
{
string input =
"b51:4:1DB:9EE1:5:27d60:f44:D4:cd:E:5:0A5:4a:D24:41Ad:";
bool matched;
Stopwatch sw;

Console.WriteLine("With backtracking:");
string backPattern = "^(([0-9a-fA-F]{1,4}:)*([0-9a-fA-F]{1,4}))*
(::)$";
sw = Stopwatch.StartNew();
matched = Regex.IsMatch(input, backPattern);
sw.Stop();
Console.WriteLine("Match: {0} in {1}", Regex.IsMatch(input,
backPattern), sw.Elapsed);
Console.WriteLine();

Console.WriteLine("Without backtracking:");
string noBackPattern = "^((?>[0-9a-fA-F]{1,4}:)*(?>[0-9a-fA-F]
{1,4}))*(::)$";
sw = Stopwatch.StartNew();
matched = Regex.IsMatch(input, noBackPattern);
sw.Stop();
Console.WriteLine("Match: {0} in {1}", Regex.IsMatch(input,
noBackPattern), sw.Elapsed);
}
}
// The example displays output like the following:
// With backtracking:
// Match: False in 00:00:27.4282019
//
// Without backtracking:
// Match: False in 00:00:00.0001391

Asserções lookbehind
O .NET inclui dois elementos de linguagem, (?<= subexpression ) e (?<! subexpression ) ,
que correspondem ao caractere ou aos caracteres anteriores na cadeia de caracteres de
entrada. Ambos os elementos de linguagem são asserções de largura zero, ou seja, eles
determinam se o caractere ou os caracteres que precedem imediatamente o caractere
atual podem ser correspondidos pela subexpressão, sem avanço ou retrocesso.

(?<= subexpression ) é uma asserção lookbehind positiva, ou seja, o caractere ou os

caracteres antes da posição atual devem corresponder à subexpression. (?


<! subexpression ) é uma asserção lookbehind negativa, ou seja, o caractere ou os

caracteres antes da posição atual não devem corresponder à subexpression. As asserções


lookbehind positivas e negativas são mais úteis quando subexpressão for um
subconjunto da subexpressão anterior.
O exemplo a seguir usa dois padrões equivalentes à expressão regular que validam o
nome de usuário em um endereço de email. O primeiro padrão está sujeito a baixo
desempenho devido ao retrocesso excessivo. O segundo padrão modifica a primeira
expressão regular ao substituir um quantificador aninhado por uma asserção
lookbehind positiva. A saída do exemplo exibe, o tempo de execução do método
Regex.IsMatch.

C#

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example5


{
public static void Run()
{
Stopwatch sw;
string input = "test@contoso.com";
bool result;

string pattern = @"^[0-9A-Z]([-.\w]*[0-9A-Z])?@";


sw = Stopwatch.StartNew();
result = Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
sw.Stop();
Console.WriteLine("Match: {0} in {1}", result, sw.Elapsed);

string behindPattern = @"^[0-9A-Z][-.\w]*(?<=[0-9A-Z])@";


sw = Stopwatch.StartNew();
result = Regex.IsMatch(input, behindPattern,
RegexOptions.IgnoreCase);
sw.Stop();
Console.WriteLine("Match with Lookbehind: {0} in {1}", result,
sw.Elapsed);
}
}
// The example displays output similar to the following:
// Match: True in 00:00:00.0017549
// Match with Lookbehind: True in 00:00:00.0000659

O primeiro padrão de expressão regular, ^[0-9A-Z]([-.\w]*[0-9A-Z])*@ , é definido


como mostrado na tabela a seguir.

Padrão Descrição

^ Começa a correspondência no início da cadeia de caracteres.

[0-9A-Z] Corresponde a um caractere alfanumérico. Essa comparação não diferencia


maiúsculas de minúsculas porque o método Regex.IsMatch é chamado com a
opção RegexOptions.IgnoreCase.
Padrão Descrição

[-.\w]* Corresponde a zero, uma ou mais ocorrências de um hífen, ponto ou caractere de


palavra.

[0-9A-Z] Corresponde a um caractere alfanumérico.

([-.\w]*[0- Corresponde a zero ou mais ocorrências da combinação de zero ou mais hífens,


9A-Z])* pontos ou caracteres de palavra, seguidos por um caractere alfanumérico. Este é o
primeiro grupo de captura.

@ Corresponde a um sinal de arroba (“@").

O segundo padrão de expressão regular, ^[0-9A-Z][-.\w]*(?<=[0-9A-Z])@ , usa uma


asserção lookbehind positiva. Ele é definido conforme mostrado na tabela a seguir.

Padrão Descrição

^ Começa a correspondência no início da cadeia de caracteres.

[0-9A-Z] Corresponde a um caractere alfanumérico. Essa comparação não diferencia


maiúsculas de minúsculas porque o método Regex.IsMatch é chamado com a opção
RegexOptions.IgnoreCase.

[-.\w]* Corresponde a zero ou mais ocorrências de um hífen, ponto ou caractere de palavra.

(?<=[0- Examina de volta o último caractere correspondente e continua a correspondência se


9A-Z]) ele é alfanumérico. Observe que os caracteres alfanuméricos são um subconjunto do
conjunto que consiste em pontos, hífens e todos os caracteres de palavra.

@ Corresponde a um sinal de arroba (“@").

Asserções lookahead
O .NET inclui dois elementos de linguagem, (?= subexpression ) e (?! subexpression ) ,
que correspondem ao próximo caractere ou aos próximos caracteres na cadeia de
caracteres de entrada. Ambos os elementos de linguagem são asserções de largura
zero, ou seja, eles determinam se o caractere ou os caracteres que seguem
imediatamente o caractere atual podem ser correspondidos pela subexpressão, sem
avanço ou retrocesso.

(?= subexpression ) é uma asserção lookahead positiva, ou seja, o caractere ou os

caracteres depois da posição atual devem corresponder à subexpression.


(?! subexpression ) é uma asserção lookahead negativa, ou seja, o caractere ou os

caracteres depois da posição atual não devem corresponder à subexpression. As


asserções lookahead positivas e negativas são mais úteis quando subexpression é um
subconjunto da subexpression seguinte.

O exemplo a seguir usa dois padrões de expressão regular equivalentes que validam um
nome de tipo totalmente qualificado. O primeiro padrão está sujeito a baixo
desempenho devido ao retrocesso excessivo. O segundo modifica a primeira expressão
regular ao substituir um quantificador aninhado por uma asserção lookahead positiva. A
saída do exemplo exibe, o tempo de execução do método Regex.IsMatch.

C#

using System;
using System.Diagnostics;
using System.Text.RegularExpressions;

public class Example6


{
public static void Run()
{
string input = "aaaaaaaaaaaaaaaaaaaaaa.";
bool result;
Stopwatch sw;

string pattern = @"^(([A-Z]\w*)+\.)*[A-Z]\w*$";


sw = Stopwatch.StartNew();
result = Regex.IsMatch(input, pattern, RegexOptions.IgnoreCase);
sw.Stop();
Console.WriteLine("{0} in {1}", result, sw.Elapsed);

string aheadPattern = @"^((?=[A-Z])\w+\.)*[A-Z]\w*$";


sw = Stopwatch.StartNew();
result = Regex.IsMatch(input, aheadPattern,
RegexOptions.IgnoreCase);
sw.Stop();
Console.WriteLine("{0} in {1}", result, sw.Elapsed);
}
}
// The example displays the following output:
// False in 00:00:03.8003793
// False in 00:00:00.0000866

O primeiro padrão de expressão regular, ^(([A-Z]\w*)+\.)*[A-Z]\w*$ , é definido como


mostrado na tabela a seguir.

Padrão Descrição

^ Começa a correspondência no início da cadeia de caracteres.

([A- Corresponde a um caractere alfabético (A-Z) seguido por zero ou mais caracteres
Z]\w*)+\. de palavra uma ou mais vezes, seguidos de um ponto. Essa comparação não
Padrão diferencia
Descrição maiúsculas de minúsculas porque o método Regex.IsMatch é chamado
com a opção RegexOptions.IgnoreCase.

(([A- Corresponde ao padrão anterior zero vezes ou mais.


Z]\w*)+\.)*

[A-Z]\w* Corresponder a um caractere alfabético seguido por zero ou mais caracteres de


palavra.

$ Finalizar a correspondência no final da cadeia de caracteres de entrada.

O segundo padrão de expressão regular, ^((?=[A-Z])\w+\.)*[A-Z]\w*$ , usa uma


asserção lookahead positiva. Ele é definido conforme mostrado na tabela a seguir.

Padrão Descrição

^ Começa a correspondência no início da cadeia de caracteres.

(?=[A-Z]) Examine além do primeiro caractere e continue a correspondência se ele for


alfabético (A-Z). Essa comparação não diferencia maiúsculas de minúsculas
porque o método Regex.IsMatch é chamado com a opção
RegexOptions.IgnoreCase.

\w+\. Corresponde a um ou mais caracteres de palavra seguidos por um ponto.

((?=[A- Corresponde ao padrão de um ou mais caracteres de palavra seguidos por um


Z])\w+\.)* ponto zero ou mais vezes. O caractere de palavra inicial deve ser alfabético.

[A-Z]\w* Corresponder a um caractere alfabético seguido por zero ou mais caracteres de


palavra.

$ Finalizar a correspondência no final da cadeia de caracteres de entrada.

Considerações gerais sobre o desempenho


As seguintes sugestões não são específicas para evitar rastreamentos inversos
excessivos, mas podem ajudar a aumentar o desempenho de sua expressão regular:

1. Pré-compilar padrões muito usados. A melhor maneira de fazer isso é usar o


gerador de origem da expressão regular para pré-compilá-lo. Se o gerador de
origem não estiver disponível para seu aplicativo, por exemplo, você não estiver
direcionando o .NET 7 ou posterior ou não souber o padrão em tempo de
compilação, use a opção RegexOptions.Compiled.

2. Armazene em cache objetos Regex muito usados. Isso ocorre implicitamente


quando você está usando o gerador de origem. Caso contrário, crie um objeto
Regex e armazene-o para reutilização, em vez de usar os métodos Regex estáticos
ou criar e jogar fora um objeto Regex.

3. Inicie a correspondência de um deslocamento. Se você souber que as


correspondências sempre começarão além de um determinado deslocamento para
o padrão, passe o deslocamento usando uma sobrecarga como
Regex.Match(String, Int32). Isso reduzirá a quantidade de texto que o mecanismo
precisa considerar.

4. Reúna apenas as informações necessárias. Se você só precisa saber se uma


correspondência ocorre, mas não onde a correspondência ocorre, prefira
Regex.IsMatch. Se você só precisa saber quantas vezes algo corresponde, prefira
usar Regex.Count. Se você só precisa saber os limites de uma correspondência,
mas não qualquer coisa sobre capturas de uma correspondência, prefira usar
Regex.EnumerateMatches. Quanto menos informações o mecanismo precisar
fornecer, melhor.

5. Evite capturas desnecessárias. Parênteses em seu padrão formam um grupo de


captura por padrão. Se você não precisar de capturas, especifique
RegexOptions.ExplicitCapture ou use grupos sem captura. Isso poupa o
mecanismo de manter o controle dessas capturas.

Confira também
Expressões regulares do .NET
Linguagem de expressões regulares – referência rápida
Quantificadores
Constructos de alternância
Agrupando constructos
Compilação e reutilização em
expressões regulares
Artigo • 09/05/2023

Você pode otimizar o desempenho de aplicativos que fazem uso intensivo de


expressões regulares compreendendo como o mecanismo de expressões regulares
compila expressões e como as expressões regulares são armazenadas em cache. Este
tópico discute a compilação e o cache.

Expressões regulares compiladas


Por padrão, o mecanismo de expressões regulares compila uma expressão regular para
uma sequência de instruções internas (esses são códigos de alto nível que são
diferentes da Microsoft Intermediate Language ou MSIL). Quando o mecanismo executa
uma expressão regular, ele interpreta os códigos internos.

Se um objeto Regex for construído com a opção RegexOptions.Compiled, ele compilará


a expressão regular para o código explícito em MSIL, em vez de instruções internas de
expressões regulares de alto nível. Isso permite que o compilador JIT (just-in-time) do
.NET converta a expressão para um código de computador nativo para um melhor
desempenho. O custo da construção do objeto Regex pode ser maior, mas o custo de
executar correspondências com ele provavelmente é muito menor.

Uma alternativa é usar expressões regulares pré-compiladas. Você pode compilar todas
as suas expressões em uma DLL reutilizável usando o método CompileToAssembly. Isso
evita a necessidade de compilar em tempo de execução e ainda se beneficia da
velocidade das expressões regulares compiladas.

Cache de expressões regulares


Para melhorar o desempenho, o mecanismo de expressões regulares mantém um cache
em todo o aplicativo com as expressões regulares compiladas. O cache armazena
padrões de expressões regulares que são usados somente em chamadas de método
estático. (Os padrões de expressão regular fornecidos aos métodos de instância não são
armazenados em cache.) Isso evita a necessidade de reanalisar uma expressão em
código de bytes de alto nível sempre que ela for usada.

O número máximo de expressões regulares em cache é determinado pelo valor da


propriedade static ( Shared no Visual Basic) Regex.CacheSize. Por padrão, o mecanismo
de expressões regulares armazena em cache até 15 expressões regulares compiladas. Se
o número de expressões regulares compiladas ultrapassar o tamanho do cache, a
expressão regular menos utilizada recentemente será descartada e a nova expressão
regular será armazenada em cache.

Seu aplicativo pode aproveitar expressões regulares pré-compiladas de uma das duas
maneiras a seguir:

Usando um método estático do objeto Regex para definir a expressão regular. Se


você estiver usando um padrão de expressão regular já definido em outra
chamada de método estático, o mecanismo de expressões regulares o recuperará
do cache. Caso contrário, o mecanismo compilará a expressão regular e a
adicionará ao cache.

Reutilizando um objeto Regex existente enquanto o padrão de expressão regular


for necessário.

Devido à sobrecarga da instanciação de objetos e à compilação da expressão regular,


criar e destruir rapidamente vários objetos Regex é um processo muito caro. Para
aplicativos que usam um grande número de expressões regulares diferentes, você pode
otimizar o desempenho usando chamadas para métodos Regex estáticos e,
possivelmente, aumentando o tamanho do cache de expressão regular.

Confira também
Expressões regulares do .NET
Acesso thread-safe em expressões
regulares
Artigo • 10/05/2023

A própria classe Regex é thread-safe e imutável (somente leitura). Ou seja, os objetos de


Regex podem ser criados em qualquer thread e compartilhados entre os threads.
Métodos correspondentes podem ser chamados de qualquer thread e nunca alteram
nenhum estado global.

No entanto, os objetos de resultado (Match e MatchCollection) retornados pela Regex


devem ser usados em um único thread. Embora muitos desses objetos sejam
logicamente imutáveis, suas implementações poderiam atrasar a computação de alguns
resultados para melhorar o desempenho e, como resultado, os chamadores devem
serializar o acesso a eles.

Se houver a necessidade de compartilhar os objetos de resultado da Regex em vários


threads, esses objetos poderão ser convertidos em instâncias thread-safe chamando
seus métodos sincronizados. Com exceção dos enumeradores, todas as classes de
expressões regulares são thread-safe ou podem ser convertidas em objetos thread-safe
por um método sincronizado.

Os enumeradores são a única exceção. Um aplicativo precisa serializar as chamadas a


enumeradores de coleções. A regra é que se uma coleção pode ser enumerada em mais
de um thread simultaneamente, você deve sincronizar os métodos do enumerador no
objeto raiz da coleção percorrida pelo enumerador.

Confira também
Expressões regulares do .NET
Exemplo de expressão regular:
Verificação de HREFs
Artigo • 25/03/2023

O exemplo a seguir procura uma cadeia de caracteres de entrada e exibe todos os


valores href="…" e suas localizações na cadeia de caracteres.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

O objeto Regex
Como o método DumpHRefs pode ser chamado várias vezes do código do usuário, ele
usa o método static ( Shared no Visual Basic) Regex.Match(String, String,
RegexOptions). Isso permite que o mecanismo de expressões regulares armazene em
cache a expressão regular e evite a sobrecarga de instanciar um novo objeto Regex
sempre que o método é chamado. Um objeto Match é usado para iterar por todas as
correspondências na cadeia de caracteres.

C#

private static void DumpHRefs(string inputString)


{
string hrefPattern = @"href\s*=\s*(?:[""'](?<1>[^""']*)[""']|(?<1>
[^>\s]+))";

try
{
Match regexMatch = Regex.Match(inputString, hrefPattern,
RegexOptions.IgnoreCase |
RegexOptions.Compiled,
TimeSpan.FromSeconds(1));
while (regexMatch.Success)
{
Console.WriteLine($"Found href {regexMatch.Groups[1]} at
{regexMatch.Groups[1].Index}");
regexMatch = regexMatch.NextMatch();
}
}
catch (RegexMatchTimeoutException)
{
Console.WriteLine("The matching operation timed out.");
}
}

O exemplo a seguir mostra uma chamada para o método DumpHRefs .

C#

public static void Main()


{
string inputString = "My favorite web sites include:</P>" +
"<A HREF=\"https://learn.microsoft.com/en-
us/dotnet/\">" +
".NET Documentation</A></P>" +
"<A HREF=\"http://www.microsoft.com\">" +
"Microsoft Corporation Home Page</A></P>" +
"<A
HREF=\"https://devblogs.microsoft.com/dotnet/\">" +
".NET Blog</A></P>";
DumpHRefs(inputString);
}
// The example displays the following output:
// Found href https://learn.microsoft.com/dotnet/ at 43
// Found href http://www.microsoft.com at 114
// Found href https://devblogs.microsoft.com/dotnet/ at 188

O padrão da expressão regular href\s*=\s*(?:["'](?<1>[^"']*)["']|(?<1>[^>\s]+)) é


interpretado conforme mostrado na tabela a seguir.

Padrão Descrição

href Corresponder à cadeia de caracteres literal “href”. A correspondência não diferencia


maiúsculas de minúsculas.

\s* Corresponder a zero ou mais caracteres de espaço em branco.

= Corresponder ao sinal de igual.

\s* Corresponder a zero ou mais caracteres de espaço em branco.

(?: Inicie um grupo sem captura.

["'](? Combine um sinal de aspas ou apóstrofo, seguido por um grupo de captura que
<1> corresponda a qualquer caractere que não um de aspas ou apóstrofo, seguido por um
[^"']*) sinal de aspas ou apóstrofo. O grupo chamado 1 está incluído nesse padrão.
["']

| OR booliano que corresponde à expressão anterior ou seguinte.


Padrão Descrição

(?<1> Um grupo de captura que usa um conjunto negado para corresponder a qualquer
[^>\s]+) caractere diferente de um sinal maior que um caractere de espaço em branco. O
grupo chamado 1 está incluído nesse padrão.

) Encerre o grupo sem captura.

Classe de resultado de correspondência


Os resultados de uma pesquisa são armazenados na classe Match, que fornece acesso a
todas as subcadeias de caracteres extraídas pela pesquisa. Também lembra a cadeia de
caracteres que está sendo pesquisada e a expressão regular que está sendo usada para
poder chamar o método Match.NextMatch para executar outra pesquisa iniciando onde
a última terminou.

Capturas nomeadas explicitamente


Em expressões regulares tradicionais, os parênteses de captura são numerados
sequencialmente de forma automática. Isso causa dois problemas. Primeiro, se uma
expressão regular for modificada pela inserção ou remoção de um conjunto de
parênteses, todo o código que se refere às captura numeradas deverá ser reescrito para
refletir a nova numeração. Em segundo lugar, como diferentes conjuntos de parênteses
geralmente são usados para fornecer duas expressões alternativas para uma
correspondência aceitável, pode ser difícil determinar qual das duas expressões
realmente retornou um resultado.

Para resolver esses problemas, a classe Regex dá suporte à sintaxe (?<name>…) para
capturar uma correspondência em um slot especificado (o slot pode ser nomeado
usando uma cadeia de caracteres ou um inteiro; inteiros podem ser recuperados mais
rapidamente). Assim, todas as correspondências alternativas para a mesma cadeia de
caracteres podem ser direcionadas para o mesmo local. Em caso de conflito, a última
correspondência solta em um slot é a correspondência com êxito. (No entanto, está
disponível uma lista completa de diversas correspondências para um único slot. Confira
a coleção Group.Captures para detalhes.)

Confira também
Expressões regulares do .NET
Exemplo de expressão regular:
Alterando formatos de data
Artigo • 25/03/2023

O exemplo de código a seguir usa o método Regex.Replace para substituir datas com o
formato mm/dd/aa por datas com o formato dd-mm-aa.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

Exemplo
C#

static string MDYToDMY(string input)


{
try {
return Regex.Replace(input,
@"\b(?<month>\d{1,2})/(?<day>\d{1,2})/(?<year>\d{2,4})\b",
"${day}-${month}-${year}", RegexOptions.None,
TimeSpan.FromMilliseconds(150));
}
catch (RegexMatchTimeoutException) {
return input;
}
}

O código a seguir mostra como o método MDYToDMY pode ser chamado em um


aplicativo.

C#

using System;
using System.Globalization;
using System.Text.RegularExpressions;

public class Class1


{
public static void Main()
{
string dateString = DateTime.Today.ToString("d",
DateTimeFormatInfo.InvariantInfo);
string resultString = MDYToDMY(dateString);
Console.WriteLine("Converted {0} to {1}.", dateString, resultString);
}

static string MDYToDMY(string input)


{
try {
return Regex.Replace(input,
@"\b(?<month>\d{1,2})/(?<day>\d{1,2})/(?<year>\d{2,4})\b",
"${day}-${month}-${year}", RegexOptions.None,
TimeSpan.FromMilliseconds(150));
}
catch (RegexMatchTimeoutException) {
return input;
}
}
}
// The example displays the following output to the console if run on
8/21/2007:
// Converted 08/21/2007 to 21-08-2007.

Comentários
O padrão da expressão regular \b(?<month>\d{1,2})/(?<day>\d{1,2})/(?
<year>\d{2,4})\b é interpretado conforme mostrado na tabela a seguir.

Padrão Descrição

\b Começar a correspondência em um limite de palavra.

(? Corresponder a um ou dois dígitos decimais. Este é o grupo capturado do


<month>\d{1,2}) month .

/ Corresponde à barra.

(?<day>\d{1,2}) Corresponder a um ou dois dígitos decimais. Este é o grupo capturado do


day .

/ Corresponde à barra.

(? Corresponder de dois a quatro dígitos decimais. Este é o grupo capturado do


<year>\d{2,4}) year .

\b Termina a correspondência em um limite de palavra.


O padrão ${day}-${month}-${year} define a cadeia de caracteres de substituição
conforme mostrado na tabela a seguir.

Padrão Descrição

$(day) Adicionar a cadeia de caracteres capturada pelo grupo de captura day .

- Adicionar um hífen.

$(month) Adicionar a cadeia de caracteres capturada pelo grupo de captura month .

- Adicionar um hífen.

$(year) Adicionar a cadeia de caracteres capturada pelo grupo de captura year .

Confira também
Expressões regulares do .NET
Como: extrair um protocolo e um
número da porta de uma URL
Artigo • 25/03/2023

O exemplo a seguir extrai um protocolo e um número da porta de uma URL.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

Exemplo
O exemplo usa o método Match.Result para retornar o protocolo seguido por dois-
pontos e pelo número da porta.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
public static void Main()
{
string url = "http://www.contoso.com:8080/letters/readme.html";

Regex r = new Regex(@"^(?<proto>\w+)://[^/]+?(?<port>:\d+)?/",


RegexOptions.None,
TimeSpan.FromMilliseconds(150));
Match m = r.Match(url);
if (m.Success)
Console.WriteLine(m.Result("${proto}${port}"));
}
}
// The example displays the following output:
// http:8080

O padrão de expressão regular ^(?<proto>\w+)://[^/]+?(?<port>:\d+)?/ pode ser


interpretado conforme mostrado na tabela a seguir.
Padrão Descrição

^ Comece a correspondência no início da cadeia de caracteres.

(? Fazer a correspondência a um ou mais caracteres de palavra. Nomeie esse grupo


<proto>\w+) como proto .

:// Fazer a correspondência de um sinal de dois-pontos seguido por duas barras "/".

[^/]+? Fazer a correspondência de uma ou mais ocorrências (mas o menor número


possível) de qualquer caractere que não seja uma barra "/".

(? Fazer a correspondência de zero ou uma ocorrência de um sinal de dois-pontos


<port>:\d+)? seguido por um ou mais caracteres de dígito. Nomeie esse grupo como port .

/ Fazer a correspondência de uma barra "/".

O método Match.Result expande a sequência de substituição ${proto}${port} , que


concatena o valor dos dois grupos nomeados capturados no padrão de expressão
regular. Essa é uma alternativa conveniente para concatenar explicitamente as cadeias
de caracteres recuperadas do objeto da coleção retornado pela propriedade
Match.Groups.

O exemplo usa o método Match.Result com duas substituições, ${proto} e ${port} ,


para incluir os grupos capturados na cadeia de caracteres de saída. Você pode recuperar
os grupos capturados do objeto GroupCollection da correspondência em vez disso,
conforme mostra o código a seguir.

C#

Console.WriteLine(m.Groups["proto"].Value + m.Groups["port"].Value);

Confira também
Expressões regulares do .NET
Como: retirar caracteres inválidos de
uma cadeia de caracteres
Artigo • 25/03/2023

O exemplo a seguir usa o método Regex.Replace estático para retirar caracteres


inválidos de uma cadeia de caracteres.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

Exemplo
Você pode usar o método CleanInput definido neste exemplo para retirar caracteres
potencialmente prejudiciais que tenham sido inseridos em um campo de texto que
aceita entrada do usuário. Nesse caso, o CleanInput remove todos os caracteres não
alfanuméricos, exceto pontos (.), arrobas (@) e hifens (-) e retorna a cadeia de caracteres
restante. No entanto, você pode modificar o padrão da expressão regular para que ela
elimine qualquer caractere que não deve ser incluído em uma cadeia de caracteres de
entrada.

C#

using System;
using System.Text.RegularExpressions;

public class Example


{
static string CleanInput(string strIn)
{
// Replace invalid characters with empty strings.
try {
return Regex.Replace(strIn, @"[^\w\.@-]", "",
RegexOptions.None,
TimeSpan.FromSeconds(1.5));
}
// If we timeout when replacing invalid characters,
// we should return Empty.
catch (RegexMatchTimeoutException) {
return String.Empty;
}
}
}

O padrão da expressão regular [^\w\.@-] corresponde a qualquer caractere que não


seja um caractere de palavra, um ponto, um símbolo de @ ou um hífen. Um caractere
de palavra é qualquer letra, dígito decimal ou conector de pontuação, como um
sublinhado. Qualquer caractere que corresponde a esse padrão é substituído pelo
String.Empty, que é a cadeia de caracteres definida pelo padrão de substituição. Para
permitir caracteres adicionais na entrada do usuário, adicione esses caracteres à classe
de caractere no padrão de expressão regular. Por exemplo, o padrão de expressão
regular [^\w\.@-\\%] também permite um símbolo percentual e uma barra invertida em
uma cadeia de caracteres de entrada.

Confira também
Expressões regulares do .NET
Como verificar se cadeias de caracteres
estão em um formato de email válido
Artigo • 25/03/2023

O exemplo neste artigo usa uma expressão regular para verificar se uma cadeia de
caracteres está em um formato de email válido.

Essa expressão regular é relativamente simples em comparação com o que pode ser
usado como um email. Usar uma expressão regular para validar um email é útil para
garantir que a estrutura dele está correta. No entanto, essa substituição não verifica se o
email realmente existe.

✔️USE uma expressão regular pequena para verificar a estrutura válida de um email.

✔️ENVIE um email de teste para o endereço fornecido por um usuário do seu


aplicativo.

❌ NÃO use uma expressão regular como a única maneira de validar um email.

Se você tentar criar a expressão regular perfeita para validar se a estrutura de um email
está correta, a expressão se tornará tão complexa que será incrivelmente difícil depurar
ou melhorar. Expressões regulares não podem validar se existe um email, mesmo que
ele esteja estruturado corretamente. A melhor maneira de validar um email é enviar um
email de teste para o endereço.

2 Aviso

Ao usar System.Text.RegularExpressions para processar entradas não confiáveis,


passe um tempo limite. Um usuário mal-intencionado pode fornecer entrada para
RegularExpressions , causando um ataque de negação de serviço . APIs ASP.NET
Core Framework que usam RegularExpressions passam um tempo limite.

Exemplo
O exemplo define um método IsValidEmail , que retornará true se a cadeia de
caracteres contiver um endereço de email válido e false se não contiver, mas não
realiza outra ação.

Para verificar se o endereço de email é válido, o método IsValidEmail chama o método


Regex.Replace(String, String, MatchEvaluator) com o padrão da expressão regular (@)
(.+)$ para separar o nome de domínio do endereço de email. O terceiro parâmetro é

um representante MatchEvaluator, que representa o método que processa e substitui o


texto correspondente. O padrão da expressão regular é interpretado da seguinte
maneira:

Padrão Descrição

(@) Coincida o caractere @. Essa parte é o primeiro grupo de captura.

(.+) Coincida uma ou mais ocorrências de qualquer caractere. Essa parte é o segundo grupo
de captura.

$ Encerrar a correspondência ao final da cadeia de caracteres.

O nome de domínio, juntamente com o caractere @, é passado para o método


DomainMapper . O método usa a classe IdnMapping para traduzir caracteres Unicode que

estão fora do intervalo de caracteres US-ASCII para Punycode. O método também


define o sinalizador invalid como True , caso o método IdnMapping.GetAscii detecte
algum caractere inválido no nome do domínio. O método retorna o nome de domínio
Punycode precedido pelo símbolo @ para o método IsValidEmail .

 Dica

É recomendável que você use o padrão de expressão regular simples (@)(.+)$


para normalizar o domínio e retornar um valor que indica que ele passou ou
falhou. No entanto, o exemplo neste artigo descreve como usar ainda mais uma
expressão regular para validar o email. Independentemente de como você valida
um email, você sempre deve enviar um email de teste para o endereço a fim de
verificar se ele existe.

Em seguida, o método IsValidEmail chama o método Regex.IsMatch(String, String)


para verificar se o endereço está adequado para um padrão de expressão regular.

O método IsValidEmail apenas determina se o formato de email é válido para um


endereço de email; ele não valida se o email existe. Além disso, o método IsValidEmail
não verifica se o nome de domínio primário é um nome de domínio válido listado no
IANA Root Zone Database (Banco de dados de zona raiz da IANA), o que exigiria uma
operação de pesquisa.

C#

using System;
using System.Globalization;
using System.Text.RegularExpressions;

namespace RegexExamples
{
class RegexUtilities
{
public static bool IsValidEmail(string email)
{
if (string.IsNullOrWhiteSpace(email))
return false;

try
{
// Normalize the domain
email = Regex.Replace(email, @"(@)(.+)$", DomainMapper,
RegexOptions.None,
TimeSpan.FromMilliseconds(200));

// Examines the domain part of the email and normalizes it.


string DomainMapper(Match match)
{
// Use IdnMapping class to convert Unicode domain names.
var idn = new IdnMapping();

// Pull out and process domain name (throws


ArgumentException on invalid)
string domainName = idn.GetAscii(match.Groups[2].Value);

return match.Groups[1].Value + domainName;


}
}
catch (RegexMatchTimeoutException e)
{
return false;
}
catch (ArgumentException e)
{
return false;
}

try
{
return Regex.IsMatch(email,
@"^[^@\s]+@[^@\s]+\.[^@\s]+$",
RegexOptions.IgnoreCase,
TimeSpan.FromMilliseconds(250));
}
catch (RegexMatchTimeoutException)
{
return false;
}
}
}
}
Neste exemplo, o padrão de expressão regular ^[^@\s]+@[^@\s]+\.[^@\s]+$ é
interpretado conforme mostrado na tabela a seguir. A expressão regular é compilada
com o sinalizador RegexOptions.IgnoreCase.

Padrão Descrição

^ Comece a correspondência no início da cadeia de caracteres.

[^@\s]+ Corresponda uma ou mais ocorrências de qualquer caractere que não seja o caractere
@ ou o espaço em branco.

@ Coincida o caractere @.

[^@\s]+ Corresponda uma ou mais ocorrências de qualquer caractere que não seja o caractere
@ ou o espaço em branco.

\. Corresponda a um único caractere de ponto.

[^@\s]+ Corresponda uma ou mais ocorrências de qualquer caractere que não seja o caractere
@ ou o espaço em branco.

$ Encerrar a correspondência ao final da cadeia de caracteres.

) Importante

Essa expressão regular não se destina a abranger todos os aspectos de um


endereço de email válido. Ela é fornecida como um exemplo para você estender
conforme necessário.

Confira também
Expressões regulares do .NET
Até onde deve ir a validação de endereço de email?
Serialização no .NET
Artigo • 27/01/2024

A serialização é o processo de conversão do estado de um objeto em um formulário


que possa ser persistido ou transportado. O complemento de serialização é
desserialização, que converte um fluxo em um objeto. Juntos, esses processos permitem
que os dados sejam armazenados e transferidos com facilidade.

O .NET apresenta as seguintes tecnologias de serialização:

A serialização binária preserva a fidelidade do tipo, o que é útil para preservar o


estado de um objeto entre diferentes chamadas de um aplicativo. Por exemplo,
você pode compartilhar um objeto entre diferentes aplicativos serializando-o para
a área de transferência. Você pode serializar um objeto para um fluxo, um disco, a
memória, pela rede, e assim por diante. O acesso remoto usa a serialização para
passar objetos “por valor” de um computador ou domínio de aplicativo para outro.

A serialização XML e SOAP só serializa as propriedades públicas e os campos e não


preserva a fidelidade de tipo. Isso é útil quando você deseja fornecer ou consumir
dados sem restringir o aplicativo que usa os dados. Como o XML é um padrão
aberto, é uma opção atrativa para compartilhar dados pela Web. SOAP é, da
mesma forma, um padrão aberto, uma opção atrativa.

A serialização JSON serializa somente as propriedades públicas e não preserva a


fidelidade de tipo. O JSON é um padrão aberto que é uma opção interessante para
compartilhar dados na Web.

Referência
System.Runtime.Serialization
Contém classes que podem ser usadas para serialização e desserialização de objetos.

System.Xml.Serialization
Contém classes que podem ser usadas para serializar objetos em documentos ou fluxos
de formato XML.

System.Text.Json
Contém classes que podem ser usadas para serializar objetos em documentos ou fluxos
de formato JSON.
Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Serialização e desserialização JSON
(marshalling e unmarshalling) no .NET –
visão geral
Artigo • 30/11/2023

O namespace System.Text.Json apresenta funcionalidade para serialização e


desserialização do JSON (JavaScript Object Notation). A Serialização é o processo de
conversão do estado de um objeto, ou seja, os valores de suas propriedades, em um
formulário que pode ser armazenado ou transmitido. O formulário serializado não inclui
nenhuma informação sobre os métodos associados de um objeto. A desserialização
reconstrói um objeto do formulário serializado.

O design da biblioteca System.Text.Json enfatiza o alto desempenho e a baixa alocação


de memória em um amplo conjunto de recursos. O suporte interno a UTF-8 otimiza o
processo de leitura e gravação de texto JSON codificado como UTF-8, que é a
codificação mais predominante para dados na Web e arquivos em disco.

A biblioteca também fornece classes para trabalhar com um DOM (modelo de objeto do
documento) na memória. Esse recurso permite o acesso aleatório aos elementos em
uma cadeia de caracteres ou arquivo JSON.

Para o Visual Basic, há algumas limitações em quais partes da biblioteca você pode usar.
Para saber mais, confira Suporte para Visual Basic.

Como obter a biblioteca


A biblioteca é interna como parte da estrutura compartilhada para o .NET Core 3.0 e
versões posteriores. O recurso de geração de origem é interno e faz parte da estrutura
compartilhada do .NET 6 e versões posteriores.

Para versões de estrutura anteriores ao .NET Core 3.0, instale o pacote do NuGet
System.Text.Json . O pacote é compatível com:

.NET Standard 2.0 e posteriores


.NET Framework 4.6.2 e versões posteriores
.NET Core 2.1 e posteriores
.NET 5 e posteriores

Namespaces e APIs
O namespace System.Text.Json contém todos os pontos de entrada e os tipos
principais.
O namespace System.Text.Json.Serialization contém atributos e APIs para cenários
avançados e personalização específicos para serialização e desserialização.

Os exemplos de código mostrados neste artigo exigem diretivas using para um ou


ambos os namespaces.

) Importante

System.Text.Json não dá suporte às seguintes APIs de serialização que você pode

ter usado anteriormente:

Atributos do namespace System.Runtime.Serialization.


O atributo System.SerializableAttribute e a interface ISerializable. Esses tipos
são usados apenas para Serialização binária e XML.

Métodos de extensão HttpClient e HttpContent


Serializar e desserializar cargas JSON da rede são operações comuns. Os métodos de
extensão em HttpClient e HttpContent permitem que você faça essas operações em
uma só linha de código. Esses métodos de extensão usam padrões da Web para
JsonSerializerOptions.

O seguinte exemplo ilustra o uso de HttpClientJsonExtensions.GetFromJsonAsync e


HttpClientJsonExtensions.PostAsJsonAsync:

C#

using System.Net.Http.Json;

namespace HttpClientExtensionMethods
{
public class User
{
public int Id { get; set; }
public string? Name { get; set; }
public string? Username { get; set; }
public string? Email { get; set; }
}

public class Program


{
public static async Task Main()
{
using HttpClient client = new()
{
BaseAddress = new
Uri("https://jsonplaceholder.typicode.com")
};

// Get the user information.


User? user = await client.GetFromJsonAsync<User>("users/1");
Console.WriteLine($"Id: {user?.Id}");
Console.WriteLine($"Name: {user?.Name}");
Console.WriteLine($"Username: {user?.Username}");
Console.WriteLine($"Email: {user?.Email}");

// Post a new user.


HttpResponseMessage response = await
client.PostAsJsonAsync("users", user);
Console.WriteLine(
$"{(response.IsSuccessStatusCode ? "Success" : "Error")} -
{response.StatusCode}");
}
}
}

// Produces output like the following example but with different names:
//
//Id: 1
//Name: Tyler King
//Username: Tyler
//Email: Tyler @contoso.com
//Success - Created

Também há métodos de extensão para System.Text.Json em HttpContent.

Reflexão versus geração de origem


Por padrão, System.Text.Json reúne os metadados necessários para acessar
propriedades de objetos para serialização e desserialização em tempo de execução
usando reflexão. Como alternativa, System.Text.Json pode usar o recurso de geração de
origem C# para melhorar o desempenho, reduzir o uso de memória privada e facilitar o
corte do assembly, o que reduz o tamanho do aplicativo.

Para obter mais informações, confira Reflexão versus geração de origens.

Informações de segurança
Para informações sobre ameaças à segurança consideradas ao projetar JsonSerializer e
como elas podem ser atenuadas, confira Modelo de riscoSystem.Text.Json .
Acesso thread-safe
O serializador System.Text.Json foi projetado com a segurança e o acesso thread-safe
em mente. Na prática, isso significa que, uma vez bloqueadas, as instâncias de
JsonSerializerOptions podem ser compartilhadas com segurança em vários threads.
JsonDocument fornece uma representação do DOM imutável e no .NET 8 e versões
posteriores, thread-safe, representação do DOM para valores JSON.

Recursos adicionais
Como usar a biblioteca

6 Colaborar conosco no .NET feedback


GitHub .NET is an open source project.
A fonte deste conteúdo pode Select a link to provide feedback:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Como escrever objetos .NET como JSON
(serializar)
Artigo • 30/11/2023

Este artigo mostra como usar o namespace System.Text.Json para serializar em JSON
(JavaScript Object Notation). Se você estiver portando o código existente de
Newtonsoft.Json , confira Como migrar para System.Text.Json.

Para gravar JSON em uma cadeia de caracteres ou em um arquivo, chame o método


JsonSerializer.Serialize.

O seguinte exemplo cria JSON como uma cadeia de caracteres:

C#

using System.Text.Json;

namespace SerializeBasic
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

string jsonString = JsonSerializer.Serialize(weatherForecast);

Console.WriteLine(jsonString);
}
}
}
// output:
//{"Date":"2019-08-01T00:00:00-
07:00","TemperatureCelsius":25,"Summary":"Hot"}
A saída JSON é minimizada (caracteres de espaço em branco, recuo e nova linha são
removidos) por padrão.

O seguinte exemplo usa código síncrono para criar um arquivo JSON:

C#

using System.Text.Json;

namespace SerializeToFile
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

string fileName = "WeatherForecast.json";


string jsonString = JsonSerializer.Serialize(weatherForecast);
File.WriteAllText(fileName, jsonString);

Console.WriteLine(File.ReadAllText(fileName));
}
}
}
// output:
//{"Date":"2019-08-01T00:00:00-
07:00","TemperatureCelsius":25,"Summary":"Hot"}

O seguinte exemplo usa código assíncrono para criar um arquivo JSON:

C#

using System.Text.Json;

namespace SerializeToFileAsync
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static async Task Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

string fileName = "WeatherForecast.json";


using FileStream createStream = File.Create(fileName);
await JsonSerializer.SerializeAsync(createStream,
weatherForecast);
await createStream.DisposeAsync();

Console.WriteLine(File.ReadAllText(fileName));
}
}
}
// output:
//{"Date":"2019-08-01T00:00:00-
07:00","TemperatureCelsius":25,"Summary":"Hot"}

Os exemplos anteriores usam inferência de tipos para o tipo que está sendo serializado.
Uma sobrecarga de Serialize() usa um parâmetro de tipo genérico:

C#

using System.Text.Json;

namespace SerializeWithGenericParameter
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

string jsonString = JsonSerializer.Serialize<WeatherForecast>


(weatherForecast);

Console.WriteLine(jsonString);
}
}
}
// output:
//{"Date":"2019-08-01T00:00:00-
07:00","TemperatureCelsius":25,"Summary":"Hot"}

Comportamento de serialização
Por padrão, todas as propriedades públicas são serializadas. Você pode especificar
propriedades a serem ignoradas. Você também pode incluir membros privados.
O codificador padrão escapa caracteres não ASCII, caracteres sensíveis a HTML
dentro do intervalo ASCII e caracteres que devem ser escapados de acordo com a
especificação JSON RFC 8259 .
Por padrão, o JSON é minimizado. Você pode definir os estilos de formatação do
JSON.
Por padrão, o uso de maiúsculas e minúsculas de nomes JSON corresponde aos
nomes .NET. Você pode personalizar o uso de maiúsculas e minúsculas de nome
JSON.
Por padrão, são detectadas referências circulares e exceções geradas. Você pode
preservar referências e manipular referências circulares.
Por padrão, os campos são ignorados. Você pode incluir campos.

Quando você usa System.Text.Json indiretamente em um aplicativo ASP.NET Core,


alguns comportamentos padrão são diferentes. Para obter mais informações, confira
Padrões da Web para JsonSerializerOptions.

Os tipos compatíveis incluem:

Primitivos do .NET que são mapeados para primitivos JavaScript, como tipos
numéricos, cadeias de caracteres e boolianos.

POCOs (objetos CLR básicos) definidos pelo usuário.

Matrizes unidimensionais e irregulares ( T[][] ).

Coleções e dicionários dos seguintes namespaces:


System.Collections
System.Collections.Generic
System.Collections.Immutable
System.Collections.Concurrent
System.Collections.Specialized
System.Collections.ObjectModel

Para mais informações, confira Tipos de coleção com suporte em System.Text.Json.

Você pode implementar conversores personalizados para lidar com tipos adicionais ou
fornecer funcionalidades que não são compatíveis com os conversores internos.

Veja um exemplo mostrando como uma classe que contém propriedades de coleção e
um tipo definido pelo usuário é serializada:

C#

using System.Text.Json;

namespace SerializeExtra
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public string? SummaryField;
public IList<DateTimeOffset>? DatesAvailable { get; set; }
public Dictionary<string, HighLowTemps>? TemperatureRanges { get;
set; }
public string[]? SummaryWords { get; set; }
}

public class HighLowTemps


{
public int High { get; set; }
public int Low { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot",
SummaryField = "Hot",
DatesAvailable = new List<DateTimeOffset>()
{ DateTime.Parse("2019-08-01"), DateTime.Parse("2019-08-
02") },
TemperatureRanges = new Dictionary<string, HighLowTemps>
{
["Cold"] = new HighLowTemps { High = 20, Low = -10
},
["Hot"] = new HighLowTemps { High = 60 , Low = 20 }
},
SummaryWords = new[] { "Cool", "Windy", "Humid" }
};

var options = new JsonSerializerOptions { WriteIndented = true


};
string jsonString = JsonSerializer.Serialize(weatherForecast,
options);

Console.WriteLine(jsonString);
}
}
}
// output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "TemperatureRanges": {
// "Cold": {
// "High": 20,
// "Low": -10
// },
// "Hot": {
// "High": 60,
// "Low": 20
// }
// },
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}

Serializar para UTF-8


É 5 a 10% mais rápido serializar para uma matriz de bytes UTF-8 do que usar os
métodos baseados em cadeia de caracteres. Isso ocorre porque os bytes (como UTF-8)
não precisam ser convertidos em cadeias de caracteres (UTF-16).
Para serializar para uma matriz de bytes UTF-8, chame o método
JsonSerializer.SerializeToUtf8Bytes:

C#

byte[] jsonUtf8Bytes =JsonSerializer.SerializeToUtf8Bytes(weatherForecast);

Uma sobrecarga Serialize que usa um Utf8JsonWriter também está disponível.

Serializar para JSON formatado


Para estruturar a saída JSON, defina JsonSerializerOptions.WriteIndented como true :

C#

using System.Text.Json;

namespace SerializeWriteIndented
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

var options = new JsonSerializerOptions { WriteIndented = true


};
string jsonString = JsonSerializer.Serialize(weatherForecast,
options);

Console.WriteLine(jsonString);
}
}
}
// output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot"
//}

 Dica

Se você usar JsonSerializerOptions repetidamente com as mesmas opções, não


crie uma instância JsonSerializerOptions sempre que a usar. Reutilize a mesma
instância para cada chamada. Para mais informações, confira Reutilizar instâncias
de JsonSerializerOptions.

6 Colaborar conosco no .NET feedback


GitHub .NET is an open source project.
A fonte deste conteúdo pode Select a link to provide feedback:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Como personalizar nomes e valores de
propriedade com System.Text.Json
Artigo • 25/10/2023

Por padrão, os nomes de propriedade e as chaves de dicionário não são alterados na


saída JSON, incluindo maiúsculas e minúsculas. Os valores de enumeração são
representados como números. E as propriedades são serializadas na ordem em que são
definidas. No entanto, é possível personalizar esse comportamento:

Especificando nomes de propriedade serializados específicos.


Usando uma política de nomenclatura interna, como camelCase, snake_case ou
kebab-case, para nomes de propriedade e chaves de dicionário.
Usando uma política de nomenclatura personalizada para nomes de propriedade e
chaves de dicionário.
Serializando valores de enumeração como cadeias de caracteres, com ou sem uma
política de nomenclatura.
Configurando a ordem das propriedades serializadas.

7 Observação

A política de nomenclatura padrão da Web é camel case.

Para outros cenários que exigem tratamento especial de nomes e valores de


propriedade JSON, você pode implementar conversores personalizados.

Personalizar nomes de propriedades individuais


Para definir o nome das propriedades individuais, use o atributo [JsonPropertyName].

Aqui está um tipo de exemplo para serializar e o JSON resultante:

C#

public class WeatherForecastWithPropertyNameAttribute


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}
JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"Wind": 35
}

O nome da propriedade definida por este atributo:

Aplica-se em ambas as direções, para serialização e desserialização.


Tem precedência sobre políticas de nomenclatura de propriedade.
Não afeta a correspondência de nome de parâmetro para construtores
parametrizados.

Usar uma política de nomenclatura interna


A tabela a seguir mostra as políticas de nomenclatura internas e como elas afetam os
nomes de propriedades.

Política de Descrição Nome da Nome da


nomenclatura propriedade propriedade
original convertido

CamelCase A primeira palavra começa TempCelsius tempCelsius


com um caractere
minúsculo.
As palavras sucessivas
começam com um caractere
maiúsculo.

KebabCaseLower* As palavras são separadas TempCelsius temp-celsius


por hifens.
Todos os caracteres são
minúsculos.

KebabCaseUpper* As palavras são separadas TempCelsius TEMP-CELSIUS


por hifens.
Todos os caracteres são
maiúsculos.

SnakeCaseLower* As palavras são separadas TempCelsius temp_celsius


por sublinhados.
Todos os caracteres são
minúsculos.
Política de Descrição Nome da Nome da
nomenclatura propriedade propriedade
original convertido

SnakeCaseUpper* As palavras são separadas TempCelsius TEMP_CELSIUS


por sublinhados.
Todos os caracteres são
maiúsculos.

* Disponível no .NET 8 e versões posteriores.

P exemplo a seguir mostra como usar o camel case para todos os nomes de
propriedade JSON, configurando JsonSerializerOptions.PropertyNamingPolicy como
JsonNamingPolicy.CamelCase:

C#

var serializeOptions = new JsonSerializerOptions


{
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);

Aqui está uma classe de exemplo para serializar e saída JSON:

C#

public class WeatherForecastWithPropertyNameAttribute


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}

JSON

{
"date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"summary": "Hot",
"Wind": 35
}

A política de nomenclatura:
Aplica-se à serialização e à desserialização.
É substituído por atributos [JsonPropertyName] . É por isso que o nome da
propriedade JSON Wind no exemplo não está em maiúsculas e minúsculas
concatenadas.

7 Observação

Nenhuma das políticas de nomenclatura internas oferece suporte a letras que são
pares substitutos. Para obter mais informações, confira problema #90352
dotnet/runtime .

Usar uma política de nomenclatura de


propriedade JSON personalizada
Para usar uma política de nomenclatura de propriedade JSON personalizada, crie uma
classe que deriva JsonNamingPolicy e substitui o método ConvertName, conforme
mostra o seguinte exemplo:

C#

using System.Text.Json;

namespace SystemTextJsonSamples
{
public class UpperCaseNamingPolicy : JsonNamingPolicy
{
public override string ConvertName(string name) =>
name.ToUpper();
}
}

Em seguida, defina a propriedade JsonSerializerOptions.PropertyNamingPolicy como


uma instância da classe de política de nomenclatura:

C#

var options = new JsonSerializerOptions


{
PropertyNamingPolicy = new UpperCaseNamingPolicy(),
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);
Aqui está uma classe de exemplo para serializar e saída JSON:

C#

public class WeatherForecastWithPropertyNameAttribute


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonPropertyName("Wind")]
public int WindSpeed { get; set; }
}

JSON

{
"DATE": "2019-08-01T00:00:00-07:00",
"TEMPERATURECELSIUS": 25,
"SUMMARY": "Hot",
"Wind": 35
}

A política de nomenclatura da propriedade JSON:

Aplica-se à serialização e à desserialização.


É substituído por atributos [JsonPropertyName] . É por isso que o nome da
propriedade JSON Wind no exemplo não está em maiúsculas.

Usar uma política de nomenclatura para chaves


de dicionário
Se uma propriedade de um objeto a ser serializado for do tipo
Dictionary<string,TValue> , as chaves string poderão ser convertidas usando uma

política de nomenclatura, como o camel case. Para fazer isso, defina


JsonSerializerOptions.DictionaryKeyPolicy como a política de nomenclatura desejada. O
exemplo a seguir usa a política de nomenclatura CamelCase :

C#

var options = new JsonSerializerOptions


{
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);
Serializar um objeto com um dicionário chamado TemperatureRanges que tem pares
chave-valor "ColdMinTemp", 20 e "HotMinTemp", 40 resultaria em saída JSON como a do
seguinte exemplo:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
"TemperatureRanges": {
"coldMinTemp": 20,
"hotMinTemp": 40
}
}

As políticas de nomenclatura para chaves de dicionário se aplicam somente à


serialização. Se você desserializar um dicionário, as chaves corresponderão ao arquivo
JSON mesmo que você defina JsonSerializerOptions.DictionaryKeyPolicy como uma
política de nomenclatura não padrão.

Enumerações como cadeias de caracteres


Por padrão, as enumerações são serializadas como números. Para serializar nomes de
enumeração como cadeias de caracteres, use o conversor JsonStringEnumConverter ou
JsonStringEnumConverter<TEnum>. Somente JsonStringEnumConverter<TEnum> é
compatível com o tempo de execução nativo do AOT.

Por exemplo, suponha que você precise serializar a seguinte classe que tem uma
enumeração:

C#

public class WeatherForecastWithEnum


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Summary? Summary { get; set; }
}

public enum Summary


{
Cold, Cool, Warm, Hot
}

Se o Resumo for Hot , por padrão, o JSON serializado terá o valor numérico 3:
JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": 3
}

O seguinte código de exemplo serializa os nomes de enumeração em vez dos valores


numéricos e converte os nomes em maiúsculas e minúsculas concatenadas:

C#

options = new JsonSerializerOptions


{
WriteIndented = true,
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
}
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

O JSON resultante é semelhante ao seguinte exemplo:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "hot"
}

O JsonStringEnumConverter interno também pode desserializar valores de cadeia de


caracteres. Ele funciona com ou sem uma política de nomenclatura especificada. O
seguinte exemplo mostra a desserialização usando CamelCase :

C#

options = new JsonSerializerOptions


{
Converters =
{
new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)
}
};
weatherForecast = JsonSerializer.Deserialize<WeatherForecastWithEnum>
(jsonString, options)!;

Você também pode especificar o conversor a ser usado anotando a enumeração com
JsonConverterAttribute. O exemplo a seguir mostra como especificar o
JsonStringEnumConverter<TEnum> (disponível no .NET 8 e versões posteriores) usando
o atributo JsonConverterAttribute. Por exemplo, suponha que você precise serializar a
seguinte classe que tem uma enumeração:

C#

public class WeatherForecastWithPrecipEnum


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation? Precipitation { get; set; }
}

[JsonConverter(typeof(JsonStringEnumConverter<Precipitation>))]
public enum Precipitation
{
Drizzle, Rain, Sleet, Hail, Snow
}

O seguinte código de exemplo serializa os nomes de enumeração, em vez dos valores


numéricos:

C#

var options = new JsonSerializerOptions


{
WriteIndented = true,
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

O JSON resultante é semelhante ao seguinte exemplo:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Precipitation": "Sleet"
}

Para usar o conversor com geração de origem, confira Serializar campos de enumeração
como cadeias de caracteres.
Configurar a ordem das propriedades
serializadas
Por padrão, as propriedades são serializadas na ordem em que são definidas na classe.
O atributo [JsonPropertyOrder] permite que você especifique a ordem das propriedades
na saída JSON da serialização. O valor padrão da propriedade Order é zero. Defina
Order como um número positivo para posicionar uma propriedade depois daquelas

que têm o valor padrão. Um Order negativo posiciona uma propriedade antes daquelas
que têm o valor padrão. As propriedades são escritas na ordem do valor Order mais
baixo ao mais alto. Veja um exemplo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace PropertyOrder
{
public class WeatherForecast
{
[JsonPropertyOrder(-5)]
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
[JsonPropertyOrder(-2)]
public int TemperatureF { get; set; }
[JsonPropertyOrder(5)]
public string? Summary { get; set; }
[JsonPropertyOrder(2)]
public int WindSpeed { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureC = 25,
TemperatureF = 25,
Summary = "Hot",
WindSpeed = 10
};

var options = new JsonSerializerOptions { WriteIndented = true


};
string jsonString = JsonSerializer.Serialize(weatherForecast,
options);
Console.WriteLine(jsonString);
}
}
}
// output:
//{
// "Date": "2019-08-01T00:00:00",
// "TemperatureF": 25,
// "TemperatureC": 25,
// "WindSpeed": 10,
// "Summary": "Hot"
//}

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Como ignorar propriedades com
System.Text.Json
Artigo • 20/02/2024

Ao serializar objetos C# para JSON (JavaScript Object Notation), por padrão, todas as
propriedades públicas são serializadas. Há várias opções se você não quer que alguns
deles apareçam no JSON resultante. Neste artigo, você aprenderá a ignorar
propriedades com base em vários critérios:

Propriedades individuais
Propriedades somente leitura
Todas as propriedades de valor nulo
Todas as propriedades de valor padrão

Ignorar propriedades individuais


Para ignorar propriedades individuais, use o atributo [JsonIgnore].

O exemplo a seguir mostra um tipo a ser serializado. Ele também mostra a saída JSON:

C#

public class WeatherForecastWithIgnoreAttribute


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
[JsonIgnore]
public string? Summary { get; set; }
}

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
}

Você pode especificar a exclusão condicional definindo a propriedade Condition do


atributo [JsonIgnore]. A enumeração JsonIgnoreCondition fornece as seguintes opções:

Always – a propriedade é sempre ignorada. Se nenhum Condition for

especificado, essa opção será presumida.


Never – a propriedade é sempre serializada e desserializada, independentemente

das configurações globais de DefaultIgnoreCondition , IgnoreReadOnlyProperties e


IgnoreReadOnlyFields .
WhenWritingDefault – a propriedade será ignorada na serialização se for um tipo

de referência null , um tipo de valor anulável null ou um tipo de valor default .


WhenWritingNull – a propriedade é ignorada na serialização se for um tipo de

referência null ou um tipo de valor anulável null .

O seguinte exemplo ilustra o uso da propriedade Condition do atributo [JsonIgnore]:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace JsonIgnoreAttributeExample
{
public class Forecast
{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public DateTime Date { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public int TemperatureC { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Summary { get; set; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new()
{
Date = default,
Summary = null,
TemperatureC = default
};

JsonSerializerOptions options = new()


{
DefaultIgnoreCondition =
JsonIgnoreCondition.WhenWritingDefault
};

string forecastJson =
JsonSerializer.Serialize<Forecast>(forecast,options);

Console.WriteLine(forecastJson);
}
}
}

// Produces output like the following example:


//
//{"TemperatureC":0}

Ignorar todas as propriedades somente leitura


Uma propriedade será somente leitura se contiver um getter público, mas não um setter
público. Para ignorar todas as propriedades somente leitura ao serializar, defina
JsonSerializerOptions.IgnoreReadOnlyPropertiescomo true , conforme mostrado no
seguinte exemplo:

C#

var options = new JsonSerializerOptions


{
IgnoreReadOnlyProperties = true,
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options);

O exemplo a seguir mostra um tipo a ser serializado. Ele também mostra a saída JSON:

C#

public class WeatherForecastWithROProperty


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public int WindSpeedReadOnly { get; private set; } = 35;
}

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "Hot",
}

Essa opção se aplica apenas a propriedades. Para ignorar campos somente leitura ao
serializar campos, use a configuração JsonSerializerOptions.IgnoreReadOnlyFields
global.
7 Observação

As propriedades do tipo de coleção somente leitura continuam sendo serializadas,


mesmo se JsonSerializerOptions.IgnoreReadOnlyProperties estiver definido como
true .

Ignorar todas as propriedades de valor nulo


Para ignorar todas as propriedades de valor nulo, defina a propriedade
DefaultIgnoreCondition como WhenWritingNull, conforme mostrado no seguinte
exemplo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace IgnoreNullOnSerialize
{
public class Forecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new()
{
Date = DateTime.Now,
Summary = null,
TemperatureC = default
};

JsonSerializerOptions options = new()


{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};

string forecastJson =
JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine(forecastJson);
}
}
}
// Produces output like the following example:
//
//{"Date":"2020-10-30T10:11:40.2359135-07:00","TemperatureC":0}

Ignorar todas as propriedades de valor padrão


Para evitar a serialização de valores padrão em propriedades de tipo de valor, defina a
propriedade DefaultIgnoreCondition como WhenWritingDefault, conforme mostrado no
seguinte exemplo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace IgnoreValueDefaultOnSerialize
{
public class Forecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new()
{
Date = DateTime.Now,
Summary = null,
TemperatureC = default
};

JsonSerializerOptions options = new()


{
DefaultIgnoreCondition =
JsonIgnoreCondition.WhenWritingDefault
};

string forecastJson =
JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine(forecastJson);
}
}
}

// Produces output like the following example:


//
//{ "Date":"2020-10-21T15:40:06.8920138-07:00"}

A configuração WhenWritingDefault também impede a serialização de tipo de


referência de valor nulo e propriedades de tipo de valor anulável.

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Incluir campos
Artigo • 30/11/2023

Por padrão, os campos não são serializados. Use a configuração global


JsonSerializerOptions.IncludeFields ou o atributo [JsonInclude] para incluir campos ao
serializar ou desserializar, conforme mostrado no seguinte exemplo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Fields
{
public class Forecast
{
public DateTime Date;
public int TemperatureC;
public string? Summary;
}

public class Forecast2


{
[JsonInclude]
public DateTime Date;
[JsonInclude]
public int TemperatureC;
[JsonInclude]
public string? Summary;
}

public class Program


{
public static void Main()
{
var json =
@"{""Date"":""2020-09-
06T11:31:01.923395"",""TemperatureC"":-1,""Summary"":""Cold""} ";
Console.WriteLine($"Input JSON: {json}");

var options = new JsonSerializerOptions


{
IncludeFields = true,
};
var forecast = JsonSerializer.Deserialize<Forecast>(json,
options)!;

Console.WriteLine($"forecast.Date: {forecast.Date}");
Console.WriteLine($"forecast.TemperatureC:
{forecast.TemperatureC}");
Console.WriteLine($"forecast.Summary: {forecast.Summary}");
var roundTrippedJson =
JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine($"Output JSON: {roundTrippedJson}");

var forecast2 = JsonSerializer.Deserialize<Forecast2>(json)!;

Console.WriteLine($"forecast2.Date: {forecast2.Date}");
Console.WriteLine($"forecast2.TemperatureC:
{forecast2.TemperatureC}");
Console.WriteLine($"forecast2.Summary: {forecast2.Summary}");

roundTrippedJson = JsonSerializer.Serialize<Forecast2>
(forecast2);

Console.WriteLine($"Output JSON: {roundTrippedJson}");


}
}
}

// Produces output like the following example:


//
//Input JSON: { "Date":"2020-09-
06T11:31:01.923395","TemperatureC":-1,"Summary":"Cold"}
//forecast.Date: 9/6/2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "Date":"2020-09-
06T11:31:01.923395","TemperatureC":-1,"Summary":"Cold"}
//forecast2.Date: 9/6/2020 11:31:01 AM
//forecast2.TemperatureC: -1
//forecast2.Summary: Cold
//Output JSON: { "Date":"2020-09-
06T11:31:01.923395","TemperatureC":-1,"Summary":"Cold"}

Para ignorar campos somente leitura, use a configuração global


JsonSerializerOptions.IgnoreReadOnlyFields.

6 Colaborar conosco no .NET feedback


GitHub .NET is an open source project.
A fonte deste conteúdo pode Select a link to provide feedback:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
 Fornecer comentários sobre o
produto
informações, confira o nosso
guia para colaboradores.
Como ler JSON como objetos .NET
(desserializar)
Artigo • 30/11/2023

Este artigo mostra como usar o namespace System.Text.Json para serializar e


desserializar da JSON (JavaScript Object Notation). Se você estiver portando o código
existente de Newtonsoft.Json , confira Como migrar para System.Text.Json.

Uma maneira comum de desserializar JSON é ter (ou criar) uma classe .NET com
propriedades e campos que representam uma ou mais das propriedades JSON. Em
seguida, para desserializar de uma cadeia de caracteres ou um arquivo, chame o
método JsonSerializer.Deserialize. Para as sobrecargas genéricas, o parâmetro de tipo
genérico é a classe .NET. Para as sobrecargas não genéricas, você passa o tipo da classe
como um parâmetro de método. Você pode desserializar de maneira síncrona ou
assíncrona.

Todas as propriedades JSON que não são representadas na sua classe são ignoradas por
padrão. Além disso, se alguma propriedade no tipo for necessária, mas não estiver
presente no conteúdo JSON, a desserialização falhará.

O seguinte exemplo mostra como desserializar uma cadeia de caracteres JSON:

C#

using System.Text.Json;

namespace DeserializeExtra
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public string? SummaryField;
public IList<DateTimeOffset>? DatesAvailable { get; set; }
public Dictionary<string, HighLowTemps>? TemperatureRanges { get;
set; }
public string[]? SummaryWords { get; set; }
}

public class HighLowTemps


{
public int High { get; set; }
public int Low { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""TemperatureRanges"": {
""Cold"": {
""High"": 20,
""Low"": -10
},
""Hot"": {
""High"": 60,
""Low"": 20
}
},
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}
";

WeatherForecast? weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString);

Console.WriteLine($"Date: {weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius:
{weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary: {weatherForecast?.Summary}");
}
}
}
// output:
//Date: 8/1/2019 12:00:00 AM -07:00
//TemperatureCelsius: 25
//Summary: Hot

Para desserializar de um arquivo usando código síncrono, leia o arquivo em uma cadeia
de caracteres, conforme mostrado no seguinte exemplo:

C#

using System.Text.Json;
namespace DeserializeFromFile
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static void Main()
{
string fileName = "WeatherForecast.json";
string jsonString = File.ReadAllText(fileName);
WeatherForecast weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString)!;

Console.WriteLine($"Date: {weatherForecast.Date}");
Console.WriteLine($"TemperatureCelsius:
{weatherForecast.TemperatureCelsius}");
Console.WriteLine($"Summary: {weatherForecast.Summary}");
}
}
}
// output:
//Date: 8/1/2019 12:00:00 AM -07:00
//TemperatureCelsius: 25
//Summary: Hot

Para desserializar de um arquivo usando código assíncrono, chame o método


DeserializeAsync:

C#

using System.Text.Json;

namespace DeserializeFromFileAsync
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static async Task Main()
{
string fileName = "WeatherForecast.json";
using FileStream openStream = File.OpenRead(fileName);
WeatherForecast? weatherForecast =
await JsonSerializer.DeserializeAsync<WeatherForecast>
(openStream);
Console.WriteLine($"Date: {weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius:
{weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary: {weatherForecast?.Summary}");
}
}
}
// output:
//Date: 8/1/2019 12:00:00 AM -07:00
//TemperatureCelsius: 25
//Summary: Hot

Comportamento de desserialização
Os seguintes comportamentos se aplicam ao desserializar o JSON:

Por padrão, a correspondência de nome de propriedade diferencia maiúsculas de


minúsculas. Você pode especificar não diferenciar maiúsculas de minúsculas.
Construtores não públicos são ignorados pelo serializador.
A desserialização para objetos ou propriedades imutáveis que não têm
acessadores públicos set tem suporte, mas não está habilitada por padrão.
Confira Tipos imutáveis e registros.
Por padrão, há suporte para enumerações como números. Você pode desserializar
campos de enumeração de cadeia de caracteres.
Por padrão, os campos são ignorados. Você pode incluir campos.
Por padrão, comentários ou vírgulas à direita no JSON geram exceções. Você pode
permitir comentários e vírgulas à direita.
A profundidade máxima padrão é 64.

Quando você usa System.Text.Json indiretamente em um aplicativo ASP.NET Core,


alguns comportamentos padrão são diferentes. Para obter mais informações, confira
Padrões da Web para JsonSerializerOptions.

Você pode implementar conversores personalizados para fornecer funcionalidades que


não são compatíveis com os conversores internos.

Desserializar sem uma classe .NET


Se você tiver o JSON no qual deseja desserializar e não tiver a classe para desserializá-lo,
terá opções diferentes de criar manualmente a classe de que precisa:
Use o Utf8JsonReader diretamente.

Desserialize em um DOM JSON (modelo de objeto do documento) e extraia o que


você precisa do DOM.

O DOM permite que você navegue até uma subseção de um conteúdo JSON e
desserialize um só valor, um tipo personalizado ou uma matriz. Para informações
sobre o JsonNode DOM, confira Desserializar subseções de um conteúdo JSON.
Para informações sobre o DOM JsonDocument, confira Como pesquisar um
JsonDocument e JsonElement em busca de subconjuntos.

Use o Visual Studio 2022 para gerar automaticamente a classe necessária:


Copie o JSON de que você precisa para desserializar.
Crie um arquivo de classe e exclua o código do modelo.
Escolha Editar>Colar especial>Colar JSON como Classes.

O resultado é uma classe que você pode usar para seu destino de desserialização.

Desserializar de UTF-8
Para desserializar de UTF-8, chame uma sobrecarga JsonSerializer.Deserialize que usa
um ReadOnlySpan<byte> ou um Utf8JsonReader , conforme mostrado nos exemplos a
seguir. Os exemplos pressupõem que o JSON está em uma matriz de bytes chamada
jsonUtf8Bytes.

C#

var readOnlySpan = new ReadOnlySpan<byte>(jsonUtf8Bytes);


WeatherForecast deserializedWeatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(readOnlySpan)!;

C#

var utf8Reader = new Utf8JsonReader(jsonUtf8Bytes);


WeatherForecast deserializedWeatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(ref utf8Reader)!;

6 Colaborar conosco no .NET feedback


GitHub .NET is an open source project.
A fonte deste conteúdo pode Select a link to provide feedback:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores.
produto
Propriedades obrigatórias
Artigo • 08/06/2023

A partir do .NET 7, você pode marcar determinadas propriedades para significar que
elas devem estar presentes no conteúdo JSON para que a desserialização seja bem-
sucedida. Se uma ou mais dessas propriedades necessárias não estiverem presentes, os
métodos JsonSerializer.Deserialize lançarão uma JsonException.

Há três maneiras de marcar uma propriedade ou campo conforme necessário para


desserialização JSON:

Adicionando o modificador necessário, que é novo no C# 11.


Anotando-o com JsonRequiredAttribute, que é novo no .NET 7.
Modificando a propriedade JsonPropertyInfo.IsRequired do modelo de contrato,
que é nova no .NET 7.

Da perspectiva do serializador, essas duas demarcações são equivalentes e mapeiam


para a mesma parte dos metadados, que é JsonPropertyInfo.IsRequired. Na maioria dos
casos, você simplesmente usaria a palavra-chave integrada de C#. No entanto, você
deve usar JsonRequiredAttribute nos seguintes casos:

Se estiver usando uma linguagem de programação diferente de C# ou uma versão


de nível inferior de C#.
Se quiser apenas aplicar o requisito à desserialização JSON.
Se estiver usando a serialização System.Text.Json no modo de geração de origem.
Nesse caso, seu código não será compilado se você usar o modificador required ,
pois a geração de origem ocorrerá em tempo de compilação.

O snippet de código a seguir mostra um exemplo de uma propriedade modificada com


a palavra-chave required . Essa propriedade deve estar presente no conteúdo JSON
para que a desserialização seja bem-sucedida.

C#

using System.Text.Json;

// The following line throws a JsonException at run time.


Console.WriteLine(JsonSerializer.Deserialize<Person>("""{"Age": 42}"""));

public class Person


{
public required string Name { get; set; }
public int Age { get; set; }
}

Como alternativa, você pode usar JsonRequiredAttribute:

C#

using System.Text.Json;

// The following line throws a JsonException at run time.


Console.WriteLine(JsonSerializer.Deserialize<Person>("""{"Age": 42}"""));

public class Person


{
[JsonRequired]
public string Name { get; set; }
public int Age { get; set; }
}

Também é possível controlar se uma propriedade é necessária por meio do modelo de


contrato usando a propriedade JsonPropertyInfo.IsRequired:

C#

var options = new JsonSerializerOptions


{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
static typeInfo =>
{
if (typeInfo.Kind != JsonTypeInfoKind.Object)
return;

foreach (JsonPropertyInfo propertyInfo in


typeInfo.Properties)
{
// Strip IsRequired constraint from every property.
propertyInfo.IsRequired = false;
}
}
}
}
};

// Deserialization now succeeds even though the Name property isn't in the
JSON payload.
JsonSerializer.Deserialize<Person>("""{"Age": 42}""", options);
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como permitir alguns tipos de JSON
inválidos com System.Text.Json
Artigo • 26/05/2023

Neste artigo, você aprenderá a permitir comentários, vírgulas à direita e números entre
aspas no JSON e como escrever números como cadeias de caracteres.

Permitir comentários e vírgulas à direita


Por padrão, comentários e vírgulas à direita não são permitidos no JSON. Para permitir
comentários no JSON, defina a propriedade
JsonSerializerOptions.ReadCommentHandling como JsonCommentHandling.Skip . E para
permitir vírgulas à direita, defina a propriedade
JsonSerializerOptions.AllowTrailingCommas como true . O exemplo a seguir mostra
como permitir ambos:

C#

var options = new JsonSerializerOptions


{
ReadCommentHandling = JsonCommentHandling.Skip,
AllowTrailingCommas = true,
};
var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>
(jsonString, options)!;

Aqui está o exemplo JSON com comentários e uma vírgula à direita:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25, // Fahrenheit 77
"Summary": "Hot", /* Zharko */
// Comments on
/* separate lines */
}

Permitir ou gravar números entre aspas


Alguns serializadores codificam números como cadeias de caracteres JSON (entre
aspas).

Por exemplo:

JSON

{
"DegreesCelsius": "23"
}

Em vez de:

JSON

{
"DegreesCelsius": 23
}

Para serializar números entre aspas ou aceitar números entre aspas em todo o grafo de
objetos de entrada, defina JsonSerializerOptions.NumberHandling conforme mostrado
no exemplo a seguir:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace QuotedNumbers
{
public class Forecast
{
public DateTime Date { get; init; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new()
{
Date = DateTime.Now,
TemperatureC = 40,
Summary = "Hot"
};

JsonSerializerOptions options = new()


{
NumberHandling =
JsonNumberHandling.AllowReadingFromString |
JsonNumberHandling.WriteAsString,
WriteIndented = true
};

string forecastJson =
JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine($"Output JSON:\n{forecastJson}");

Forecast forecastDeserialized =
JsonSerializer.Deserialize<Forecast>(forecastJson,
options)!;

Console.WriteLine($"Date: {forecastDeserialized.Date}");
Console.WriteLine($"TemperatureC:
{forecastDeserialized.TemperatureC}");
Console.WriteLine($"Summary: {forecastDeserialized.Summary}");
}
}
}

// Produces output like the following example:


//
//Output JSON:
//{
// "Date": "2020-10-23T12:27:06.4017385-07:00",
// "TemperatureC": "40",
// "Summary": "Hot"
//}
//Date: 10/23/2020 12:27:06 PM
//TemperatureC: 40
//Summary: Hot

Quando você usa System.Text.Json indiretamente por meio do ASP.NET Core, os


números entre aspas são permitidos ao desserializar porque o ASP.NET Core especifica
opções padrão da Web.

Para permitir ou gravar números entre aspas para propriedades, campos ou tipos
específicos, use o atributo [JsonNumberHandling].

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Gerenciar membros ausentes durante a
desserialização
Artigo • 08/06/2023

Por padrão, se o conteúdo JSON que você está desserializando contiver propriedades
que não existem no tipo POCO (objeto CRL básico) simples desserializado, elas serão
simplesmente ignoradas. A partir do .NET 8, você pode especificar que todos os
membros precisam estar presentes no conteúdo. Se eles não estiverem, uma exceção
JsonException será gerada. Você pode configurar esse comportamento de uma das três
maneiras abaixo:

Anote seu tipo POCO com o atributo


[<xref:System.Text.Json.Serialization.JsonUnmappedMemberHandlingAttribute>] ,

especificando para os membros ausentes Skip ou Disallow.

C#

[JsonUnmappedMemberHandling(JsonUnmappedMemberHandling.Disallow)]
public class MyPoco
{
public int Id { get; set; }
}

JsonSerializer.Deserialize<MyPoco>("""{"Id" : 42, "AnotherId" : -1


}""");
// JsonException : The JSON property 'AnotherId' could not be mapped to
any .NET member contained in type 'MyPoco'.

Defina JsonSerializerOptions.UnmappedMemberHandling como Skip ou Disallow.

Personalize o contrato JsonTypeInfo para o tipo relevante. (Para obter informações


sobre como personalizar um contrato, confira Personalizar um contrato JSON.)

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
 Provide product feedback
more information, see our
contributor guide.
Como lidar com o JSON de estouro ou
usar JsonElement ou JsonNode em
System.Text.Json
Artigo • 05/03/2024

Este artigo mostra como lidar com o JSON de estouro com o namespace
System.Text.Json. Ele também mostra como desserializar em JsonElement ou JsonNode,
como uma alternativa a outros cenários em que o tipo de destino pode não
corresponder perfeitamente a todo o JSON que está sendo desserializado.

Manipular JSON de estouro


Ao desserializar, você pode receber dados no JSON que não são representados por
propriedades do tipo de destino. Por exemplo, suponha que seu tipo de destino seja o
seguinte:

C#

public class WeatherForecast


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

E o JSON a ser desserializado seja o seguinte:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"Summary": "Hot",
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}
Se você desserializar o JSON mostrado no tipo mostrado, as propriedades
DatesAvailable e SummaryWords não terão para onde ir e serão perdidas. Para capturar

dados extras, como essas propriedades, aplique o atributo [JsonExtensionData] a uma


propriedade do tipo Dictionary<string,object> ou Dictionary<string,JsonElement> :

C#

public class WeatherForecastWithExtensionData


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}

A tabela a seguir mostra o resultado da desserialização do JSON mostrado


anteriormente neste tipo de exemplo. Os dados extras se tornam pares chave-valor da
propriedade ExtensionData :

ノ Expandir a tabela

Propriedade Valor Observações

Date "8/1/2019 12:00:00 AM -07:00"

TemperatureCelsius 0 Incompatibilidade de diferenciação de


maiúsculas de minúsculas
( temperatureCelsius no JSON),
portanto, a propriedade não é
definida.

Summary "Hot"

ExtensionData "temperatureCelsius": 25, Como as maiúsculas e minúsculas não


"DatesAvailable": ["2019-08- eram iguais, temperatureCelsius é um
01T00:00:00-07:00","2019-08- extra e se torna um par chave-valor
02T00:00:00-07:00"], no dicionário.
"SummaryWords": Cada matriz extra do JSON se torna
["Cool","Windy","Humid"] um par chave-valor, com uma matriz
como o objeto de valor.

Quando o objeto de destino é serializado, os pares de valor da chave de dados de


extensão se tornam propriedades JSON exatamente como estavam no JSON de entrada:

JSON
{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 0,
"Summary": "Hot",
"temperatureCelsius": 25,
"DatesAvailable": [
"2019-08-01T00:00:00-07:00",
"2019-08-02T00:00:00-07:00"
],
"SummaryWords": [
"Cool",
"Windy",
"Humid"
]
}

Observe que o nome da propriedade ExtensionData não aparece no JSON. Esse


comportamento permite que o JSON faça uma viagem de ida e volta sem perder dados
extras que, de outra forma, não seriam desserializados.

O seguinte exemplo mostra uma viagem de ida e volta do JSON para um objeto
desserializado e de volta para JSON:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace RoundtripExtensionData
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
[JsonExtensionData]
public Dictionary<string, JsonElement>? ExtensionData { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""temperatureCelsius"": 25,
""Summary"": ""Hot"",
""SummaryField"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}";
WeatherForecast weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString)!;

var serializeOptions = new JsonSerializerOptions { WriteIndented


= true };
jsonString = JsonSerializer.Serialize(weatherForecast,
serializeOptions);
Console.WriteLine($"JSON output:\n{jsonString}\n");
}
}
}
// output:
//JSON output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 0,
// "Summary": "Hot",
// "temperatureCelsius": 25,
// "SummaryField": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}

Desserializar em JsonElement ou JsonNode


Se você quiser apenas ser flexível sobre qual JSON é aceitável para uma propriedade
específica, uma alternativa é desserializar em JsonElement ou JsonNode. Qualquer
propriedade JSON válida pode ser desserializada em JsonElement ou JsonNode . Escolha
JsonElement para criar um objeto imutável ou JsonNode para criar um objeto mutável.

O exemplo a seguir mostra uma viagem de ida e volta do JSON e de volta para JSON
para uma classe que inclui propriedades do tipo JsonElement e JsonNode .

C#
using System.Text.Json;
using System.Text.Json.Nodes;

namespace RoundtripJsonElementAndNode
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
public JsonElement DatesAvailable { get; set; }
public JsonNode? SummaryWords { get; set; }
}
public class Program
{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00-07:00"",
""2019-08-02T00:00:00-07:00""
],
""SummaryWords"": [
""Cool"",
""Windy"",
""Humid""
]
}";
WeatherForecast? weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString);

var serializeOptions = new JsonSerializerOptions { WriteIndented


= true };
jsonString = JsonSerializer.Serialize(weatherForecast,
serializeOptions);
Console.WriteLine(jsonString);
}
}
}
// output:
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00-07:00",
// "2019-08-02T00:00:00-07:00"
// ],
// "SummaryWords": [
// "Cool",
// "Windy",
// "Humid"
// ]
//}

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Usar tipos e propriedades imutáveis
Artigo • 30/11/2023

Um tipo imutável é aquele que impede que você altere quaisquer valores de
propriedade ou campo de um objeto depois de instanciado. O tipo pode ser um
registro, não ter propriedades ou campos públicos, ter propriedades somente leitura ou
ter propriedades com setters privados ou somente init. System.String é um exemplo de
um tipo imutável. System.Text.Json fornece diferentes maneiras de desserializar o JSON
para tipos imutáveis.

Construtores parametrizados
Por padrão, System.Text.Json usa o construtor público sem parâmetros padrão. No
entanto, você pode dizer a ele para usar um construtor parametrizado, o que possibilita
desserializar uma classe ou struct imutável.

Para uma classe, se o único construtor for parametrizado, esse construtor será
usado.

Para um struct ou uma classe com vários construtores, especifique aquele que será
utilizado aplicando o atributo [JsonConstructor]. Quando o atributo não for
utilizado, sempre será usado um construtor público sem parâmetros, se estiver
presente.

O exemplo a seguir usa o atributo [JsonConstructor] :

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace ImmutableTypes
{
public struct Forecast
{
public DateTime Date { get; }
public int TemperatureC { get; }
public string Summary { get; }

[JsonConstructor]
public Forecast(DateTime date, int temperatureC, string
summary) =>
(Date, TemperatureC, Summary) = (date, temperatureC,
summary);
}
public class Program
{
public static void Main()
{
var json = @"{""date"":""2020-09-06T11:31:01.923395-
07:00"",""temperatureC"":-1,""summary"":""Cold""} ";
Console.WriteLine($"Input JSON: {json}");

var options = new


JsonSerializerOptions(JsonSerializerDefaults.Web);

var forecast = JsonSerializer.Deserialize<Forecast>(json,


options);

Console.WriteLine($"forecast.Date: {forecast.Date}");
Console.WriteLine($"forecast.TemperatureC:
{forecast.TemperatureC}");
Console.WriteLine($"forecast.Summary: {forecast.Summary}");

var roundTrippedJson =
JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine($"Output JSON: {roundTrippedJson}");

}
}
}

// Produces output like the following example:


//
//Input JSON: { "date":"2020-09-06T11:31:01.923395-
07:00","temperatureC":-1,"summary":"Cold"}
//forecast.Date: 9 / 6 / 2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "date":"2020-09-06T11:31:01.923395-
07:00","temperatureC":-1,"summary":"Cold"}

No .NET 7 e em versões anteriores, o atributo [JsonConstructor] só pode ser


usado com construtores públicos.

Os nomes de parâmetro de um construtor parametrizado devem corresponder aos


nomes e tipos de propriedade. A correspondência não diferencia maiúsculas de
minúsculas e o parâmetro do construtor deverá corresponder ao nome da propriedade
real, mesmo que você use [JsonPropertyName] para renomear uma propriedade. No
exemplo a seguir, o nome da propriedade TemperatureC é alterado para celsius no
JSON, mas o parâmetro do construtor ainda é nomeado temperatureC :

C#
using System.Text.Json;
using System.Text.Json.Serialization;

namespace ImmutableTypesCtorParms
{
public struct Forecast
{
public DateTime Date { get; }
[JsonPropertyName("celsius")]
public int TemperatureC { get; }
public string Summary { get; }

[JsonConstructor]
public Forecast(DateTime date, int temperatureC, string summary) =>
(Date, TemperatureC, Summary) = (date, temperatureC, summary);
}

public class Program


{
public static void Main()
{
var json = @"{""date"":""2020-09-06T11:31:01.923395-
07:00"",""celsius"":-1,""summary"":""Cold""} ";
Console.WriteLine($"Input JSON: {json}");

var options = new


JsonSerializerOptions(JsonSerializerDefaults.Web);

var forecast = JsonSerializer.Deserialize<Forecast>(json,


options);

Console.WriteLine($"forecast.Date: {forecast.Date}");
Console.WriteLine($"forecast.TemperatureC:
{forecast.TemperatureC}");
Console.WriteLine($"forecast.Summary: {forecast.Summary}");

var roundTrippedJson =
JsonSerializer.Serialize<Forecast>(forecast, options);

Console.WriteLine($"Output JSON: {roundTrippedJson}");

}
}
}

// Produces output like the following example:


//
//Input JSON: { "date":"2020-09-06T11:31:01.923395-
07:00","celsius":-1,"summary":"Cold"}
//forecast.Date: 9 / 6 / 2020 11:31:01 AM
//forecast.TemperatureC: -1
//forecast.Summary: Cold
//Output JSON: { "date":"2020-09-06T11:31:01.923395-
07:00","celsius":-1,"summary":"Cold"}

Além de [JsonPropertyName] , os seguintes atributos dão suporte à desserialização com


construtores parametrizados:

[JsonConverter]
[JsonIgnore]
[JsonInclude]
[JsonNumberHandling]

Registros
Também há suporte para registros para serialização e desserialização, conforme
mostrado no exemplo a seguir:

C#

using System.Text.Json;

namespace Records
{
public record Forecast(DateTime Date, int TemperatureC)
{
public string? Summary { get; init; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new(DateTime.Now, 40)
{
Summary = "Hot!"
};

string forecastJson = JsonSerializer.Serialize<Forecast>


(forecast);
Console.WriteLine(forecastJson);
Forecast? forecastObj = JsonSerializer.Deserialize<Forecast>
(forecastJson);
Console.WriteLine(forecastObj);
}
}
}

// Produces output like the following example:


//
//{ "Date":"2020-10-21T15:26:10.5044594-
07:00","TemperatureC":40,"Summary":"Hot!"}
//Forecast { Date = 10 / 21 / 2020 3:26:10 PM, TemperatureC = 40, Summary =
Hot! }

É possível aplicar qualquer um dos atributos aos nomes de propriedade, usando o


destino property: no atributo. Para obter mais informações sobre registros posicionais,
consulte o artigo sobre registros na referência de linguagem C#.

Membros e acessadores de propriedade não


públicos
No entanto, você pode habilitar o uso de um acessador de propriedade não público
usando o atributo [JsonInclude], conforme mostrado no seguinte exemplo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace NonPublicAccessors
{
public class Forecast
{
public DateTime Date { get; init; }

[JsonInclude]
public int TemperatureC { get; private set; }

[JsonInclude]
public string? Summary { private get; set; }
};

public class Program


{
public static void Main()
{
string json = @"{""Date"":""2020-10-23T09:51:03.8702889-
07:00"",""TemperatureC"":40,""Summary"":""Hot""}";
Console.WriteLine($"Input JSON: {json}");

Forecast forecastDeserialized =
JsonSerializer.Deserialize<Forecast>(json)!;
Console.WriteLine($"Date: {forecastDeserialized.Date}");
Console.WriteLine($"TemperatureC:
{forecastDeserialized.TemperatureC}");

json = JsonSerializer.Serialize<Forecast>(forecastDeserialized);
Console.WriteLine($"Output JSON: {json}");
}
}
}

// Produces output like the following example:


//
//Input JSON: { "Date":"2020-10-23T09:51:03.8702889-
07:00","TemperatureC":40,"Summary":"Hot"}
//Date: 10 / 23 / 2020 9:51:03 AM
//TemperatureC: 40
//Output JSON: { "Date":"2020-10-23T09:51:03.8702889-
07:00","TemperatureC":40,"Summary":"Hot"}

Ao incluir uma propriedade com um setter privado, você ainda pode desserializar essa
propriedade.

No .NET 8 e versões posteriores, você também pode usar o atributo [JsonInclude] para
optar por membros não públicos no contrato de serialização para um determinado tipo.

7 Observação

No modo de geração de origem, você não pode serializar membros private ou


usar acessadores private anotando-os com o atributo [JsonInclude]. E você só
poderá serializar membros internal ou usar acessadores internal se eles
estiverem no mesmo assembly que o JsonSerializerContext gerado.

Propriedades somente leitura


No .NET 8 e versões posteriores, as propriedades somente leitura ou aquelas que não
têm nenhum setter, privadas ou públicas, também podem ser desserializadas. Embora
você não possa alterar a instância que a propriedade faz referência, se o tipo da
propriedade for mutável, você poderá modificá-la. Por exemplo, você pode adicionar
um elemento a uma lista. Para desserializar uma propriedade somente leitura, você
precisa definir seu comportamento de tratamento de criação de objeto para preencher
em vez de substituir. Por exemplo, você pode anotar a propriedade com o atributo
JsonObjectCreationHandlingAttribute.

C#

class A
{
[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
public List<int> Numbers1 { get; } = new List<int>() { 1, 2, 3 };
}
Para obter mais informações, consulte Popular propriedades inicializadas.

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON

6 Colaborar conosco no .NET feedback


GitHub .NET is an open source project.
A fonte deste conteúdo pode Select a link to provide feedback:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Preencher propriedades inicializadas
Artigo • 10/11/2023

A partir do .NET 8, você pode especificar uma preferência para substituir ou preencher
propriedades do .NET quando JSON for desserializado. A enumeração
JsonObjectCreationHandling fornece as opções de tratamento de criação de objeto:

JsonObjectCreationHandling.Replace
JsonObjectCreationHandling.Populate

Comportamento padrão (substituir)


O desserializador System.Text.Json sempre cria uma nova instância do tipo de destino.
No entanto, mesmo que uma nova instância seja criada, algumas propriedades e
campos já podem ser inicializados como parte da construção do objeto. Considere o
seguinte tipo :

C#

class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}

Quando você cria uma instância dessa classe, o valor da propriedade Numbers1 (e
Numbers2 ) é uma lista com três elementos (1, 2 e 3). Se você desserializar o JSON para

esse tipo, o comportamento de padrão será que os valores de propriedade serão


substituídos:

Para Numbers1 , como é somente leitura (sem setter), ele ainda tem os valores 1, 2 e
3 em sua lista.
Para Numbers2 , que é leitura-gravação, uma nova lista é alocada, e os valores do
JSON são adicionados.

Por exemplo, se você executar o código de desserialização a seguir, Numbers1 conterá os


valores 1, 2 e 3, e Numbers2 conterá os valores 4, 5 e 6.

C#

A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2":


[4,5,6]}""");
Comportamento de preenchimento
A partir do .NET 8, você pode alterar o comportamento de desserialização para
modificar (preencher) propriedades e campos em vez de substituí-los:

Para uma propriedade de tipo de coleção, o objeto é reutilizado sem limpar. Se a


coleção for pré-preenchida com elementos, eles serão exibidos no resultado final
desserializado junto com os valores do JSON. Para obter um exemplo, consulte
Exemplo de propriedade de coleção.

Para uma propriedade que é um objeto com propriedades, suas propriedades


mutáveis são atualizadas para os valores JSON, mas a referência de objeto em si
não é alterada.

Para uma propriedade do tipo struct, o comportamento efetivo é que, para suas
propriedades mutáveis, quaisquer valores existentes são mantidos e novos valores
do JSON são adicionados. No entanto, ao contrário de uma propriedade de
referência, o objeto em si não é reutilizado, pois é um tipo de valor. Em vez disso,
uma cópia de struct é modificada e, em seguida, reatribuída à propriedade. Para
obter um exemplo, consulte Exemplo de propriedade struct.

Uma propriedade struct deve ter um setter; caso contrário, um


InvalidOperationException é lançado em tempo de execução.

7 Observação

Atualmente, o comportamento de preenchimento não funciona para tipos que têm


um construtor parametrizado. Para obter mais informações, confira problema
92877 dotnet/runtime .

Propriedades somente leitura


Para preencher propriedades de referência que são mutáveis, uma vez que a instância a
que a propriedade faz referência não é substituída, a propriedade não precisa ter um
setter. Esse comportamento significa que a desserialização também pode preencher
propriedades somente leitura.

7 Observação

As propriedades struct ainda exigem setters, porque a instância é substituída por


uma cópia modificada.
Exemplo de propriedade de coleção
Considere a mesma classe A do exemplo de comportamento de substituição, mas desta
vez anotada com uma preferência para preencher propriedades em vez de substituí-las:

C#

[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class A
{
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}

Se você executar o seguinte código de desserialização, ambos Numbers1 e Numbers2


contêm os valores 1, 2, 3, 4, 5 e 6:

C#

A? a = JsonSerializer.Deserialize<A>("""{"Numbers1": [4,5,6], "Numbers2":


[4,5,6]}""");

Exemplo de propriedade struct


A classe a seguir contém uma propriedade struct, S1 , cujo comportamento de
desserialização é definido como Populate. Depois de executar esse código, c.S1.Value1
tem um valor de 10 (do construtor), e c.S1.Value2 tem um valor de 5 (do JSON).

C#

C? c = JsonSerializer.Deserialize<C>("""{"S1": {"Value2": 5}}""");

class C
{
public C()
{
_s1 = new S
{
Value1 = 10
};
}

private S _s1;

[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
public S S1
{
get { return _s1; }
set { _s1 = value; }
}
}

struct S
{
public int Value1 { get; set; }
public int Value2 { get; set; }
}

Se o comportamento Replace padrão fosse usado, c.S1.Value1 teria seu valor padrão 0
após a desserialização. Isso porque o construtor C() seria chamado, definindo
c.S1.Value1 como 10, mas então o valor de S1 seria substituído por uma nova instância.

( c.S1.Value2 ainda seria 5, já que o JSON substitui o valor padrão.)

Como especificar
Há várias maneiras de especificar uma preferência para substituir ou preencher:

Use o atributo JsonObjectCreationHandlingAttribute para anotar no nível de tipo


ou propriedade. Se você definir o atributo no nível do tipo e definir a propriedade
Handling como Populate, o comportamento só se aplicará às propriedades onde o
preenchimento for possível (por exemplo, os tipos de valor devem ter um setter).

Se desejar que a preferência de tipo seja Populate, mas quiser excluir uma ou mais
propriedades desse comportamento, você poderá adicionar o atributo no nível de
tipo e novamente no nível de propriedade para substituir o comportamento
herdado. Um exemplo é mostrado no código seguinte.

C#

// Type-level preference is Populate.


[JsonObjectCreationHandling(JsonObjectCreationHandling.Populate)]
class B
{
// For this property only, use Replace behavior.
[JsonObjectCreationHandling(JsonObjectCreationHandling.Replace)]
public List<int> Numbers1 { get; } = [1, 2, 3];
public List<int> Numbers2 { get; set; } = [1, 2, 3];
}

Defina JsonSerializerOptions.PreferredObjectCreationHandling (ou, para geração


de origem,
JsonSourceGenerationOptionsAttribute.PreferredObjectCreationHandling) para
especificar uma preferência global.

C#

var options = new JsonSerializerOptions


{
PreferredObjectCreationHandling =
JsonObjectCreationHandling.Populate
};

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Migrar de Newtonsoft.Json para
System.Text.Json
Artigo • 08/02/2024

Este artigo mostra como migrar de Newtonsoft.Json para System.Text.Json.

O namespace System.Text.Json apresenta funcionalidade para serialização e


desserialização do JSON (JavaScript Object Notation). A biblioteca System.Text.Json
está incluída no runtime do .NET Core 3.1 e versões posteriores. Para outras estruturas
de destino, instale o pacote NuGet System.Text.Json . O pacote é compatível com:

.NET Standard 2.0 e versões posteriores


.NET Framework 4.7.2 e versões posteriores
.NET Core 2.0, 2.1 e 2.2

System.Text.Json se concentra principalmente na conformidade de padrões, segurança

e desempenho. Ele tem algumas diferenças importantes no comportamento padrão e


não tem como objetivo ter paridade de recursos com Newtonsoft.Json . Para alguns
cenários, atualmente System.Text.Json não tem nenhuma funcionalidade interna, mas
há soluções alternativas recomendadas. Para outros cenários, soluções alternativas são
impraticáveis.

Estamos investindo na adição dos recursos que são mais solicitados. Se seu aplicativo
depende de um recurso em falta, considere arquivar um problema no repositório
GitHub do dotnet/runtime para saber se o suporte de seu cenário pode ser adicionado.

Este artigo trata principalmente de como usar a API JsonSerializer, mas também inclui
diretrizes de como usar o JsonDocument (que representa o Modelo de Objeto do
Documento ou DOM) tipos Utf8JsonReadere Utf8JsonWriter.

No Visual Basic, você não pode usar Utf8JsonReader, o que também significa que não
pode gravar conversores personalizados. A maioria das soluções alternativas
apresentadas aqui exige que você grave conversores personalizados. Você pode gravar
um conversor personalizado em C# e registrá-lo em um projeto do Visual Basic. Para
saber mais, confira Suporte para Visual Basic.

Tabela de diferenças
A tabela a seguir lista os recursos Newtonsoft.Json e equivalentes System.Text.Json . Os
equivalentes correspondem às seguintes categorias:
✔️Compatível com a funcionalidade integrada. Para obter um comportamento de
System.Text.Json semelhante, pode ser necessário o uso de um atributo ou de

uma opção global.


⚠ Não é compatível. É possível uma solução alternativa. As soluções alternativas
são conversores personalizados, que podem não oferecer paridade completa com
a funcionalidade Newtonsoft.Json . Para alguns, o código de exemplo é fornecido
como exemplos. Se você depender desses recursos Newtonsoft.Json , a migração
exige modificações em seus modelos de objeto .NET ou outras alterações de
código.
❌ Não é compatível. A solução alternativa não é prática ou possível. Se você
contar com esses recursos Newtonsoft.Json , a migração não pode ocorrer sem
alterações significativas.

ノ Expandir a tabela

Recurso do Newtonsoft.Json Equivalente System.Text.Json

Desserialização que não diferencia maiúsculas de ✔️Configuração global


minúsculas por padrão PropertyNameCaseInsensitive

Nomes de propriedade em minúsculas ✔️Configuração global


concatenadas PropertyNamingPolicy

Nomes de propriedade em snake-case ✔️Política de nomenclatura Snake case

Escape mínimo de caracteres ✔️Escape de caractere estrito e configurável

configuração global NullValueHandling.Ignore ✔️Opção global DefaultIgnoreCondition

Permitir comentários ✔️Configuração global


ReadCommentHandling

Permitir vírgulas à direita ✔️Configuração global


AllowTrailingCommas

Registro de conversor personalizado ✔️Diferença na ordem de precedência

Sem profundidade máxima por padrão ✔️Profundidade máxima padrão 64


configurável

configuração global PreserveReferencesHandling ✔️Configuração global ReferenceHandling

Serializar ou desserializar números entre aspas ✔️Configuração global NumberHandling,


atributo [JsonNumberHandling]

Desserializar para classes e structs imutáveis ✔️Registros JsonConstructor, C# 9


Recurso do Newtonsoft.Json Equivalente System.Text.Json

Suporte para campos ✔️Configuração global IncludeFields,


atributo [JsonInclude]

Configuração global DefaultValueHandling ✔️Configuração global


DefaultIgnoreCondition

Configuração NullValueHandling em ✔️Atributo JsonIgnore


[JsonProperty]

Configuração DefaultValueHandling em ✔️Atributo JsonIgnore


[JsonProperty]

Desserializar Dictionary com chave sem cadeia ✔️Com suporte


de caracteres

Suporte para setters e getters de propriedades ✔️Atributo JsonInclude


não públicas

Atributo [JsonConstructor] ✔️Atributo [JsonConstructor]

Configuração global ReferenceLoopHandling ✔️Configuração global ReferenceHandling

Retornos de chamada ✔️Retornos de chamada

NaN, Infinity, -Infinity ✔️Com suporte

Configuração Required no atributo ✔️Atributo [JsonRequired] e modificador


[JsonProperty] necessário do C#

DefaultContractResolver para ignorar ✔️DefaultJsonTypeInfoResolver class


propriedades

Serialização polimórfica ✔️[JsonDerivedType] attribute

Desserialização polimórfica ✔️Discriminador de tipo no atributo


[JsonDerivedType]

Desserializar o valor de enumeração da cadeia de ✔️Desserializar os valores de enumeração


caracteres da cadeia de caracteres

Configuração global MissingMemberHandling ✔️Lidar com os membros ausentes

Preencher propriedades sem setters ✔️Preencher propriedades sem setters

Configuração global ObjectCreationHandling ✔️Reutilizar em vez de substituir


propriedades

Suporte para uma ampla gama de tipos ⚠️Alguns tipos exigem conversores
personalizados
Recurso do Newtonsoft.Json Equivalente System.Text.Json

Desserializar o tipo inferido para as propriedades ⚠️Sem suporte, solução alternativa, amostra
object

Desserializar o literal JSON null para tipos de ⚠️Sem suporte, solução alternativa, amostra
valor não anuláveis

Configurações DateTimeZoneHandling , ⚠️Sem suporte, solução alternativa, amostra


DateFormatString

Método JsonConvert.PopulateObject ⚠️Sem suporte, solução alternativa

Suporte para atributos ⚠️Sem suporte, solução alternativa, amostra


System.Runtime.Serialization

JsonObjectAttribute ⚠️Sem suporte, solução alternativa

Permitir nomes de propriedade sem aspas ❌Não há suporte por design

Permitir aspas simples em torno de valores de ❌Não há suporte por design


cadeia de caracteres

Permitir valores JSON não string para ❌Não há suporte por design
propriedades string

Configuração global TypeNameHandling.All ❌Não há suporte por design

Suporte para consultas JsonPath ❌Sem suporte

Limites configuráveis ❌Sem suporte

Esta não é uma lista exaustiva de recursos Newtonsoft.Json . A lista inclui muitos dos
cenários que foram solicitados em problemas do GitHub ou postagens do
StackOverflow . Se implementar uma solução alternativa para um dos cenários
listados aqui que atualmente não tem código de exemplo e quiser compartilhar sua
solução, selecione Esta página na seção Comentários na parte inferior desta página.
Isso cria um problema no repositório GitHub desta documentação e também lista na
seção Comentários desta página.

Diferenças no comportamento padrão


System.Text.Json é estrito por padrão e evita qualquer detecção ou interpretação em
nome do chamador, enfatizando o comportamento determinístico. A biblioteca foi
projetada intencionalmente dessa forma por conta do desempenho e segurança.
Newtonsoft.Json é flexível por padrão. Essa diferença fundamental no design mostra

muitas das seguintes diferenças específicas no comportamento padrão.


Desserialização que não diferencia maiúsculas de
minúsculas
Durante a desserialização, Newtonsoft.Json gera um nome da propriedade que não
diferencia maiúsculas de minúsculas corresponde por padrão. O System.Text.Json por
padrão diferencia maiúsculas de minúsculas, o que oferece melhor desempenho, pois
tem uma correspondência exata. Para obter informações sobre como fazer
correspondência sem diferenciar maiúsculas de minúsculas, consulte correspondência
de propriedades que não diferencia maiúsculas de minúsculas.

Se você estiver usando System.Text.Json indiretamente pelo ASP.NET Core, não precisa
fazer nada para obter um comportamento como Newtonsoft.Json . O ASP.NET Core
especifica as configurações para nomes de propriedade em minúsculas concatenadas e
correspondências que não diferenciam maiúsculas de minúsculas ao usar
System.Text.Json .

Por padrão, o ASP.NET Core também permite desserializar os números entre aspas.

Escape mínimo de caracteres


Durante a serialização, Newtonsoft.Json é relativamente permitido deixar os caracteres
passarem sem escape. Ou seja, não os substitui por \uxxxx onde xxxx é o ponto de
código do caractere. Quando há escape, ele emite um \ antes que o caractere (por
exemplo, " se torne \" ). System.Text.Json escapa mais caracteres por padrão para
fornecer proteções de defesa em profundidade contra XSS (cross-site scripting) ou
ataques de divulgação de informações e, para isso, usa a sequência de seis caracteres.
Por padrão, System.Text.Json escapa todos os caracteres não ASCII e você não precisa
executar nada se estiver usando StringEscapeHandling.EscapeNonAscii no
Newtonsoft.Json . Por padrão, System.Text.Json também escapa caracteres sensíveis a

HTML. Para obter informações sobre como substituir o comportamento padrão


System.Text.Json , consulte Personalizar codificação de caracteres.

Comentários
Durante a desserialização, Newtonsoft.Json ignora os comentários no JSON por padrão.
O System.Text.Json padrão é lançar exceções para comentários porque a especificação
RFC 8259 não inclui exceções. Para obter informações sobre como permitir
comentários, consulte Permitir comentários e vírgulas à direita.

Vírgulas à direita
Durante a desserialização, Newtonsoft.Json ignora vírgulas à direita por padrão. O
código também ignora várias vírgulas à direita (por exemplo, [{"Color":"Red"},
{"Color":"Green"},,] ). O System.Text.Json padrão gera exceções para vírgulas à direita

porque a especificação RFC 8259 não permite exceções. Para obter informações sobre
como System.Text.Json aceita as exceções, consulte Permitir comentários e vírgulas à
direita. Não há como permitir várias vírgulas à direita.

Precedência de registro do conversor


A precedência de registro de Newtonsoft.Json para conversores personalizados é:

Atributo na propriedade
Atributo no tipo
Coleção Conversores

Essa ordem significa que um conversor personalizado na coleção Converters é


substituído por um conversor registrado aplicando um atributo no nível do tipo. Ambos
os registros são substituídos por um atributo no nível da propriedade.

A precedência de registro de System.Text.Json para conversores personalizados é


diferente:

Atributo na propriedade
Converters collection
Atributo no tipo

A diferença aqui é que um conversor personalizado na coleção Converters substitui um


atributo no nível do tipo. A intenção dessa ordem de precedência é fazer com que as
alterações no tempo de execução substituam as opções no tempo de design. Não há
como alterar a precedência.

Para obter mais informações sobre o registro de conversor personalizado, consulte


Registrar um conversor personalizado.

Profundidade máxima
Por padrão, a versão mais recente de Newtonsoft.Json tem um limite máximo de
profundidade de 64. System.Text.Json também tem um limite padrão de 64 e pode ser
configurado pela configuração JsonSerializerOptions.MaxDepth.

Se você estiver usando System.Text.Json indiretamente pelo ASP.NET Core, o limite


máximo de profundidade padrão será 32. O valor padrão é igual para model binding e é
definido na classe JsonOptions .

Cadeias de caracteres JSON (nomes de propriedade e


valores de cadeia de caracteres)
Durante a desserialização, Newtonsoft.Json aceita nomes de propriedade com aspas
duplas, aspas simples ou sem aspas. O código aceita valores de cadeia de caracteres
com aspas duplas ou aspas simples. Por exemplo, Newtonsoft.Json aceita o seguinte
JSON:

JSON

{
"name1": "value",
'name2': "value",
name3: 'value'
}

System.Text.Json aceita apenas nomes de propriedade e valores de cadeia de

caracteres em aspas duplas porque esse formato é exigido pela especificação RFC
8259 e é o único formato considerado JSON válido.

Um valor entre aspas simples resulta em um JsonException com a seguinte mensagem:

Saída

''' is an invalid start of a value.

Valores não cadeia de caracteres para propriedades de


cadeia de caracteres
Newtonsoft.Json aceita valores não cadeia de caracteres, como um número ou literais

true e false , para desserialização para propriedades do tipo string. Veja aqui um

exemplo de JSON que Newtonsoft.Json desserializa com êxito para a seguinte classe:

JSON

{
"String1": 1,
"String2": true,
"String3": false
}
C#

public class ExampleClass


{
public string String1 { get; set; }
public string String2 { get; set; }
public string String3 { get; set; }
}

System.Text.Json não desserializa valores não cadeia de caracteres em propriedades de

cadeia de caracteres. Um valor não cadeia de caracteres recebido para um campo de


cadeia de caracteres resulta em um JsonException com a seguinte mensagem:

Saída

The JSON value could not be converted to System.String.

Cenários usando JsonSerializer


Alguns dos cenários a seguir não são suportados pela funcionalidade integrada, mas
são possíveis soluções alternativas. As soluções alternativas são conversores
personalizados, que podem não oferecer paridade completa com a funcionalidade
Newtonsoft.Json . Para alguns, o código de exemplo é fornecido como exemplos. Se

você depender desses recursos Newtonsoft.Json , a migração exige modificações em


seus modelos de objeto .NET ou outras alterações de código.

Para alguns dos cenários a seguir, soluções alternativas não são práticas ou possíveis. Se
você contar com esses recursos Newtonsoft.Json , a migração não pode ocorrer sem
alterações significativas.

Permitir ou gravar números entre aspas


Newtonsoft.Json pode serializar ou desserializar números representados por cadeias de

caracteres JSON (com aspas). Por exemplo, ele pode aceitar: {"DegreesCelsius":"23"}
em vez de {"DegreesCelsius":23} . Para habilitar esse comportamento em
System.Text.Json, defina JsonSerializerOptions.NumberHandling para WriteAsString ou
AllowReadingFromString, ou use o atributo [JsonNumberHandling].

Se você estiver usando System.Text.Json indiretamente pelo ASP.NET Core, não precisa
fazer nada para obter um comportamento como Newtonsoft.Json . O ASP.NET Core
especifica os padrões da Web quando usa System.Text.Json , e os padrões da Web
permitem números entre aspas.

Para obter mais informações, consulte Permitir ou gravar números entre aspas.

Especificar construtor a ser usado na desserialização


O atributo Newtonsoft.Json [JsonConstructor] permite especificar qual construtor
chamar para desserializar para um POCO.

System.Text.Json também tem um atributo [JsonConstructor]. Para obter mais


informações, consulte Tipos imutáveis e Registros.

Ignorar condicionalmente uma propriedade


Newtonsoft.Json tem várias formas de ignorar condicionalmente uma propriedade em

serialização ou desserialização:

DefaultContractResolver permite selecionar propriedades para incluir ou ignorar

com base em critérios arbitrários.


As configurações NullValueHandling e DefaultValueHandling em
JsonSerializerSettings permitem especificar que todas as propriedades de valor

nulo ou valor padrão devem ser ignoradas.


As configurações NullValueHandling e DefaultValueHandling no atributo
[JsonProperty] permitem especificar propriedades individuais que devem ser

ignoradas quando definidas como valor nulo ou valor padrão.

System.Text.Json apresenta as seguintes formas de ignorar propriedades ou campos


durante a serialização:

O atributo [JsonIgnore] em uma propriedade permite que a propriedade seja


omitida do JSON durante a serialização.
A opção global IgnoreReadOnlyProperties permite ignorar todas as propriedades
somente leitura.
Se você estiver incluindo campos, a opção global
JsonSerializerOptions.IgnoreReadOnlyFields permitirá que você ignore todos os
campos somente leitura.
A opção global DefaultIgnoreCondition permite ignorar todas as propriedades de
tipo de valor que têm valores padrão ou ignorar todas as propriedades de tipo de
referência que têm valores nulos.
Além disso, no .NET 7 e em versões posteriores, você pode personalizar o contrato
JSON para ignorar propriedades com base em critérios arbitrários. Para obter mais
informações, consulte Contratos personalizados.

Campos públicos e não públicos


Newtonsoft.Json pode serializar e desserializar campos, além das propriedades.

No System.Text.Json, use a configuração global JsonSerializerOptions.IncludeFields ou o


atributo [JsonInclude] para incluir campos públicos na serialização ou desserialização.
Para obter um exemplo, consulte Incluir campos.

Preservar referências de objeto e loops de identificador


Por padrão, Newtonsoft.Json serializa por valor. Por exemplo, se um objeto contiver
duas propriedades com uma referência ao mesmo objeto Person , os valores das
propriedades do objeto Person serão duplicados no JSON.

Newtonsoft.Json tem uma configuração PreserveReferencesHandling no

JsonSerializerSettings que permite serializar por referência:

Um metadados de identificador é adicionado ao JSON criado para o primeiro


objeto Person .
O JSON foi criado para o segundo objeto Person que contém uma referência a
esse identificador em vez de valores da propriedade.

Newtonsoft.Json também tem uma configuração ReferenceLoopHandling que permite

ignorar referências circulares em vez de abrir uma exceção.

Para preservar referências e lidar com referências circulares em System.Text.Json, defina


JsonSerializerOptions.ReferenceHandler como Preserve. A configuração
ReferenceHandler.Preserve é equivalente a PreserveReferencesHandling =

PreserveReferencesHandling.All em Newtonsoft.Json .

A opção ReferenceHandler.IgnoreCycles tem um comportamento semelhante a


Newtonsoft.Json ReferenceLoopHandling.Ignore . Uma diferença é que a implementação
System.Text.Json substitui loops de referência pelo token JSON null em vez de ignorar
a referência de objeto. Para obter mais informações, consulte Ignorar referências
circulares.

Assim como o Newtonsoft.JsonReferenceResolver , a classe


System.Text.Json.Serialization.ReferenceResolver define o comportamento de preservar
referências sobre serialização e desserialização. Crie uma classe derivada para especificar
o comportamento personalizado. Para um exemplo, consulte GuidReferenceResolver .

Não há suporte para alguns recursos Newtonsoft.Json relacionados:

JsonPropertyAttribute.IsReference
JsonPropertyAttribute.ReferenceLoopHandling

Para obter mais informações, consulte Preservar referências e lidar com referências
circulares.

Dicionário sem chave de cadeia de caracteres


Ambos Newtonsoft.Json e System.Text.Json suportam coleções de tipo
Dictionary<TKey, TValue> . No entanto, em System.Text.Json , TKey deve ser um tipo

primitivo, não um tipo personalizado. Para obter mais informações, consulte Tipos de
chaves com suporte.

U Cuidado

Desserializar para um Dictionary<TKey, TValue> local em que TKey é digitado


como qualquer outra coisa que string não possa introduzir uma vulnerabilidade
de segurança no aplicativo de consumo. Para obter mais informações, confira
dotnet/runtime#4761 .

Tipos sem suporte interno


System.Text.Json não oferece suporte interno para os seguintes tipos:

DataTable e tipos relacionados (para obter mais informações, consulte Tipos de


coleção com suporte)
ExpandoObject
TimeZoneInfo
BigInteger
DBNull
Type
ValueTuple e seus tipos genéricos associados

Conversores personalizados podem ser implementados para tipos que não têm suporte
interno.
Serialização polimórfica
Newtonsoft.Json faz a serialização polimórfica automaticamente. A partir do .NET 7,
System.Text.Json oferece suporte à serialização polimórfica por meio do atributo
JsonDerivedTypeAttribute. Para obter mais informações, consulte Serializar propriedades
de classes derivadas.

Desserialização polimórfica
Newtonsoft.Json tem uma configuração TypeNameHandling que adiciona metadados de

nome de tipo ao JSON durante a serialização. Ele usa os metadados durante a


desserialização para fazer a desserialização polimórfica. A partir do .NET 7,
System.Text.Json depende de informações discriminadas de tipo para executar a
desserialização polimórfica. Esses metadados são emitidos no JSON e, em seguida,
usados durante a desserialização para determinar se deseja desserializar para o tipo
base ou um tipo derivado. Para obter mais informações, consulte Serializar propriedades
de classes derivadas.

Para oferecer suporte à desserialização polimórfica nas versões .NET anteriores, crie um
conversor como o exemplo Como gravar conversores personalizados.

Desserializar os valores de enumeração da cadeia de


caracteres
Por padrão, System.Text.Json não dá suporte para a desserialização de valores de
enumeração de cadeia de caracteres, enquanto Newtonsoft.Json dá. Por exemplo, o
código a seguir gera um JsonException:

C#

string json = "{ \"Text\": \"Hello\", \"Enum\": \"Two\" }";


var _ = JsonSerializer.Deserialize<MyObj>(json); // Throws exception.

class MyObj
{
public string Text { get; set; } = "";
public MyEnum Enum { get; set; }
}

enum MyEnum
{
One,
Two,
Three
}

Entretanto, você pode habilitar a desserialização de valores de enumeração de cadeia de


caracteres utilizando o conversor JsonStringEnumConverter. Para obter mais
informações, consulte Enumerações como cadeia de caracteres.

Desserialização das propriedades do objeto


Quando Newtonsoft.Json desserializa para Object:

Infere o tipo de valores primitivos no conteúdo JSON (diferente de null ) e retorna


o string , long , double , boolean ou DateTime armazenado como um objeto em
caixa. Valores primitivos são valores JSON únicos, como um número JSON, cadeia
de caracteres, true , false ou null .
Retorna um JObject ou JArray para valores complexos no conteúdo JSON.
Valores complexos são coleções de pares chave-valor JSON dentro de chaves ( {} )
ou listas de valores entre colchetes ( [] ). As propriedades e os valores dentro das
chaves ou colchetes podem ter propriedades ou valores adicionais.
Retorna uma referência nula quando o conteúdo tem o literal JSON null .

System.Text.Json armazena um box para valores primitivos e complexos JsonElement


sempre que desserializar para Object, por exemplo:

Uma propriedade object .


Um valor de dicionário object .
Um valor de matriz object .
Um object raiz.

No entanto, System.Text.Json trata null o mesmo que Newtonsoft.Json e retorna uma


referência nula quando o conteúdo tem o literal JSON null .

Para implementar a inferência de tipo para propriedades object , crie um conversor


como o exemplo em Como gravar conversores personalizados.

Desserializar o tipo nulo para não anulável


Newtonsoft.Json não gera uma exceção no seguinte cenário:

NullValueHandling é definido como Ignore , e


Durante a desserialização, o JSON contém um valor nulo para um tipo de valor
não anulável.

No mesmo cenário, System.Text.Json gera uma exceção. (A configuração


correspondente de tratamento nulo em System.Text.Json é
JsonSerializerOptions.IgnoreNullValues = true .)

Se você possui o tipo de destino, a melhor solução alternativa é tornar essa propriedade
anulável (por exemplo, alterar int para int? ).

Outra solução alternativa é criar um conversor para o tipo, como o exemplo a seguir
que lida com valores nulos para tipos DateTimeOffset :

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class DateTimeOffsetNullHandlingConverter :
JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
reader.TokenType == JsonTokenType.Null
? default
: reader.GetDateTimeOffset();

public override void Write(


Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue);
}
}

Registre esse conversor personalizado usando um atributo na propriedade ou


adicionando o conversor à coleção Converters.

Nota: o conversor anterior manipula valores nulos de forma diferente do que


Newtonsoft.Json para POCOs que especificam valores padrão. Por exemplo, suponha

que o código a seguir represente seu objeto de destino:

C#
public class WeatherForecastWithDefault
{
public WeatherForecastWithDefault()
{
Date = DateTimeOffset.Parse("2001-01-01");
Summary = "No summary";
}
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string Summary { get; set; }
}

E suponha que o JSON a seguir é desserializado usando o conversor anterior:

JSON

{
"Date": null,
"TemperatureCelsius": 25,
"Summary": null
}

Após a desserialização, a propriedade Date tem 1/1/0001 ( default(DateTimeOffset) ) ou


seja, o valor definido no construtor é substituído. Considerando o mesmo POCO e
JSON, a desserialização Newtonsoft.Json deixaria 1/1/2001 na propriedade Date .

Desserializar para classes e structs imutáveis


Newtonsoft.Json pode desserializar para classes e structs imutáveis porque pode usar

construtores que têm parâmetros.

No System.Text.Json, use o atributo [JsonConstructor] para especificar o uso de um


construtor parametrizado. Os registros no C# 9 também são imutáveis e têm suporte
como destinos de desserialização. Para obter mais informações, consulte Tipos
imutáveis e Registros.

Propriedades obrigatórias
No Newtonsoft.Json , você especifica que uma propriedade é necessária pela
configuração Required no atributo [JsonProperty] . Newtonsoft.Json gera uma exceção
se nenhum valor for recebido no JSON para uma propriedade marcada como
necessária.
A partir do .NET 7, você pode usar o modificador C# required ou o atributo
JsonRequiredAttribute em uma propriedade necessária. System.Text.Json gerará uma
exceção se o conteúdo JSON não contiver um valor para a propriedade marcada. Para
obter mais informações, consulte Propriedades necessárias.

Especificar formato de data


Newtonsoft.Json oferece várias formas de controlar como as propriedades de tipos

DateTime e DateTimeOffset são serializados e desserializados:

A configuração DateTimeZoneHandling pode ser usada para serializar todos os


valores DateTime como datas UTC.
A configuração DateFormatString e os conversores DateTime podem ser usados
para personalizar o formato das cadeias de caracteres de data.

System.Text.Json dá suporte à ISO 8601-1:2019, incluindo o perfil RFC 3339. Esse


formato é amplamente adotado, inequívoco e faz viagens de ida e volta com precisão.
Para usar qualquer outro formato, crie um conversor personalizado. Por exemplo, os
conversores a seguir serializam e desserializam o JSON que usa o formato Unix epoch
com ou sem um deslocamento de fuso horário (valores como /Date(1590863400000-
0700)/ ou /Date(1590863400000)/ ):

C#

sealed class UnixEpochDateTimeOffsetConverter :


JsonConverter<DateTimeOffset>
{
static readonly DateTimeOffset s_epoch = new DateTimeOffset(1970, 1, 1,
0, 0, 0, TimeSpan.Zero);
static readonly Regex s_regex = new Regex("^/Date\\(([+-]*\\d+)([+-])
(\\d{2})(\\d{2})\\)/$", RegexOptions.CultureInvariant);

public override DateTimeOffset Read(ref Utf8JsonReader reader, Type


typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);

if (
!match.Success
|| !long.TryParse(match.Groups[1].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
long unixTime)
|| !int.TryParse(match.Groups[3].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
int hours)
|| !int.TryParse(match.Groups[4].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
int minutes))
{
throw new JsonException();
}

int sign = match.Groups[2].Value[0] == '+' ? 1 : -1;


TimeSpan utcOffset = new TimeSpan(hours * sign, minutes * sign, 0);

return s_epoch.AddMilliseconds(unixTime).ToOffset(utcOffset);
}

public override void Write(Utf8JsonWriter writer, DateTimeOffset value,


JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value -
s_epoch).TotalMilliseconds);
TimeSpan utcOffset = value.Offset;

string formatted = string.Create(CultureInfo.InvariantCulture,


$"/Date({unixTime}{(utcOffset >= TimeSpan.Zero ? "+" : "-")}
{utcOffset:hhmm})/");

writer.WriteStringValue(formatted);
}
}

C#

sealed class UnixEpochDateTimeConverter : JsonConverter<DateTime>


{
static readonly DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0);
static readonly Regex s_regex = new Regex("^/Date\\(([+-]*\\d+)\\)/$",
RegexOptions.CultureInvariant);

public override DateTime Read(ref Utf8JsonReader reader, Type


typeToConvert, JsonSerializerOptions options)
{
string formatted = reader.GetString()!;
Match match = s_regex.Match(formatted);

if (
!match.Success
|| !long.TryParse(match.Groups[1].Value,
System.Globalization.NumberStyles.Integer, CultureInfo.InvariantCulture, out
long unixTime))
{
throw new JsonException();
}

return s_epoch.AddMilliseconds(unixTime);
}
public override void Write(Utf8JsonWriter writer, DateTime value,
JsonSerializerOptions options)
{
long unixTime = Convert.ToInt64((value -
s_epoch).TotalMilliseconds);

string formatted = string.Create(CultureInfo.InvariantCulture,


$"/Date({unixTime})/");
writer.WriteStringValue(formatted);
}
}

Para obter mais informações, confira Suporte para DateTime e DateTimeOffset em


System.Text.Json.

Retornos de chamada
Newtonsoft.Json permite executar código personalizado em vários pontos no processo

de serialização ou desserialização:

OnDeserializing (ao começar a desserializar um objeto)


OnDeserializing (ao terminar de desserializar um objeto)
OnSerializing (ao começar a serializar um objeto)
OnSerializing (ao terminar de serializar um objeto)

System.Text.Json expõe as mesmas notificações durante a serialização e desserialização.


Para usá-las, implemente uma ou mais das seguintes interfaces do namespace
System.Text.Json.Serialization:

IJsonOnDeserializing
IJsonOnDeserialized
IJsonOnSerializing
IJsonOnSerialized

Aqui está um exemplo que verifica uma propriedade nula e grava mensagens no início e
no final da serialização e desserialização:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace Callbacks
{
public class WeatherForecast :
IJsonOnDeserializing, IJsonOnDeserialized,
IJsonOnSerializing, IJsonOnSerialized
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }

void IJsonOnDeserializing.OnDeserializing() =>


Console.WriteLine("\nBegin deserializing");
void IJsonOnDeserialized.OnDeserialized()
{
Validate();
Console.WriteLine("Finished deserializing");
}
void IJsonOnSerializing.OnSerializing()
{
Console.WriteLine("Begin serializing");
Validate();
}
void IJsonOnSerialized.OnSerialized() => Console.WriteLine("Finished
serializing");

private void Validate()


{
if (Summary is null)
{
Console.WriteLine("The 'Summary' property is 'null'.");
}
}
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
};

string jsonString = JsonSerializer.Serialize(weatherForecast);


Console.WriteLine(jsonString);

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>
(jsonString);
Console.WriteLine($"Date={weatherForecast?.Date}");
Console.WriteLine($"TemperatureCelsius=
{weatherForecast?.TemperatureCelsius}");
Console.WriteLine($"Summary={weatherForecast?.Summary}");
}
}
}
// output:
//Begin serializing
//The 'Summary' property is 'null'.
//Finished serializing
//{"Date":"2019-08-01T00:00:00","TemperatureCelsius":25,"Summary":null}

//Begin deserializing
//The 'Summary' property is 'null'.
//Finished deserializing
//Date=8/1/2019 12:00:00 AM
//TemperatureCelsius = 25
//Summary=

O código OnDeserializing não tem acesso à nova instância POCO. Para lidar com a
nova instância POCO no início da desserialização, coloque esse código no construtor
POCO.

Suporte para setters e getters de propriedades não


públicas
Newtonsoft.Json pode usar setters e getters de propriedade privada e interna por meio

do atributo JsonProperty .

System.Text.Json suporta setters e getters de propriedade privada e interna por meio do


atributo [JsonInclude]. Para obter o código de exemplo, consulte Acessadores de
propriedade não pública.

Preencher objetos existentes


O método JsonConvert.PopulateObject no Newtonsoft.Json desserializa um documento
JSON para uma instância existente de uma classe, em vez de criar uma nova instância.
System.Text.Json sempre cria uma nova instância do tipo de destino usando o construtor
público sem parâmetros padrão. Conversores personalizados podem desserializar para
uma instância existente.

Reutilizar em vez de substituir propriedades


A partir do .NET 8, System.Text.Json dá suporte à reutilização de propriedades
inicializadas em vez de substituí-las. Há algumas diferenças de comportamento, sobre
as quais você pode ler na proposta de API .

Para obter mais informações, consulte Popular propriedades inicializadas.

Preencher propriedades sem setters


A partir do .NET 8, System.Text.Json dá suporte à população de propriedades, incluindo
aquelas que não têm um setter. Para obter mais informações, consulte Popular
propriedades inicializadas.

Política de nomenclatura Snake case


System.Text.Json inclui uma política de nomenclatura interna para snake case. No
entanto, há algumas diferenças de comportamento com Newtonsoft.Json para algumas
entradas. A tabela a seguir mostra algumas dessas diferenças ao converter a entrada
usando a política JsonNamingPolicy.SnakeCaseLower.

ノ Expandir a tabela

Entrada resultado Newtonsoft.Json resultado System.Text.Json

"AB1" "a_b1" "ab1"

"SHA512Managed" "sh_a512_managed" "sha512_managed"

"abc123DEF456" "abc123_de_f456" "abc123_def456"

"KEBAB-CASE" "keba_b-_case" "kebab-case"

Atributos System.Runtime.Serialization
System.Runtime.Serialization atributos como DataContractAttribute,
DataMemberAttributee IgnoreDataMemberAttribute permitem que você defina um
contrato de dados. Um contrato de dados é um contrato formal entre um serviço e um
cliente que descreve abstratamente os dados a serem trocados. O contrato de dados
define precisamente quais propriedades são serializadas para troca.

System.Text.Json não tem suporte interno para esses atributos. No entanto, a partir do
.NET 7, você pode usar um resolvedor de tipo personalizado para adicionar suporte.
Para obter um exemplo, confira ZCS. DataContractResolver .

Números octais
Newtonsoft.Json trata números com um zero à esquerda como números octais.

System.Text.Json não permite zeros à esquerda porque não é aceita pela especificação
RFC 8259 .

Manipular membros ausentes


Se o JSON que está sendo desserializado incluir propriedades ausentes no tipo de
destino, Newtonsoft.Json poderá ser configurado para gerar exceções. Por padrão,
System.Text.Json ignora as propriedades extras no JSON, exceto quando você usa o
atributo [JsonExtensionData].

No .NET 8 e em versões posteriores, você pode definir sua preferência para ignorar ou
não permitir propriedades JSON desmapeadas utilizando um dos seguintes meios:

Aplique o atributo JsonUnmappedMemberHandlingAttribute ao tipo para o qual


você está desserializando.
Para definir sua preferência globalmente, defina a propriedade
JsonSerializerOptions.UnmappedMemberHandling. Ou, para a geração de origens,
defina a propriedade
JsonSourceGenerationOptionsAttribute.UnmappedMemberHandling e aplique o
atributo à sua classe JsonSerializerContext.
Personalizar a propriedade JsonTypeInfo.UnmappedMemberHandling.

JsonObjectAttribute
Newtonsoft.Json tem um atributo, JsonObjectAttribute , que pode ser aplicado no nível

do tipo para controlar quais membros são serializados, como os valores null são
tratados e se todos os membros são necessários. System.Text.Json não tem nenhum
atributo equivalente que possa ser aplicado a um tipo. Para alguns comportamentos,
como manipulação de valor do null , você pode configurar o mesmo comportamento
no JsonSerializerOptions global ou individualmente em cada propriedade.

Considere o seguinte exemplo que usa Newtonsoft.Json.JsonObjectAttribute para


especificar que todas as propriedades null devem ser ignoradas:

C#

[JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)]
public class Person { ... }

No System.Text.Json, você pode definir o comportamento para todos os tipos e


propriedades:

C#

JsonSerializerOptions options = new()


{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string json = JsonSerializer.Serialize<Person>(person, options);

Ou você pode definir o comportamento em cada propriedade separadamente:

C#

public class Person


{
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public string? Name { get; set; }

[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public int? Age { get; set; }
}

Em seguida, considere o seguinte exemplo que usa


Newtonsoft.Json.JsonObjectAttribute para especificar que todas as propriedades de

membro devem estar presentes no JSON:

C#

[JsonObject(ItemRequired = Required.Always)]
public class Person { ... }

Você pode obter o mesmo comportamento em System.Text.Json adicionando o


modificador C# required ou o JsonRequiredAttributea cada propriedade. Para obter
mais informações, consulte Propriedades necessárias.

C#

public class Person


{
[JsonRequired]
public string? Name { get; set; }

public required int? Age { get; set; }


}

TraceWriter
Newtonsoft.Json permite que você depurar usando um TraceWriter para exibir logs

gerados por serialização ou desserialização. System.Text.Json não faz registro em log.


JsonDocument e JsonElement comparados ao
JToken (como JObject, JArray)
System.Text.Json.JsonDocument oferece a capacidade de analisar e criar um DOM
(Modelo de Objeto do Documento) somente leitura a partir de conteúdos JSON
existentes. O DOM oferece acesso aleatório aos dados em um conteúdo JSON. Os
elementos JSON que compõem o conteúdo podem ser acessados pelo tipo
JsonElement. O tipo JsonElement fornece APIs para converter texto JSON em tipos
comuns do .NET. JsonDocument mostra uma propriedade RootElement.

A partir do .NET 6, você pode analisar e criar um DOM mutável com conteúdos JSON
existentes usando o tipo JsonNode e outros tipos no namespace
System.Text.Json.Nodes. Para obter mais informações, consulte Usar JsonNode.

JsonDocument é IDisposable
JsonDocument cria uma exibição na memória dos dados em um buffer em pool. Portanto,

diferente de JObject ou JArray de Newtonsoft.Json , o tipo JsonDocument implementa


IDisposable e precisa ser usado dentro de um bloco de uso. Para obter mais

informações, consulte JsonDocument é IDisposable.

JsonDocument é somente leitura


O DOM deSystem.Text.Json não pode adicionar, remover ou modificar elementos JSON.
Ele foi projetado dessa forma para melhorar o desempenho e reduzir as alocações para
analisar tamanhos comuns de conteúdo JSON (ou seja, < 1 MB).

JsonElement é um struct de união


JsonDocument expõe a RootElement como uma propriedade do tipo JsonElement, que é

um tipo de struct de união que inclui qualquer elemento JSON. Newtonsoft.Json usa
tipos hierárquicos dedicados, como JObject , JArray , JToken e assim por diante.
JsonElement é o que você pode pesquisar e enumerar e pode usar JsonElement para

materializar elementos JSON em tipos .NET.

A partir do .NET 6, você pode usar tipo JsonNode e tipos no namespace


System.Text.Json.Nodes que correspondem a JObject , JArray e JToken . Para obter mais
informações, consulte Usar JsonNode.
Como pesquisar um JsonDocument e JsonElement como
subelementos
Pesquisa tokens JSON usando JObject ou JArray de Newtonsoft.Json costumam ser
relativamente rápidos porque são pesquisas em algum dicionário. Em comparação, as
pesquisas no JsonElement exigem uma pesquisa sequencial das propriedades e,
portanto, são relativamente lentas (por exemplo, ao usar TryGetProperty ).
System.Text.Json foi criado para minimizar o tempo inicial de análise em vez do tempo
de pesquisa. Para obter mais informações, consulte Como pesquisar um JsonDocument
e JsonElement em busca de subelementos.

Utf8JsonReader vs. JsonTextReader


System.Text.Json.Utf8JsonReader é um leitor de alto desempenho, baixa alocação e
somente para encaminhamento para texto JSON codificado em UTF-8, lido a partir de
um ReadOnlySpan<byte> ou ReadOnlySequence<byte>. O Utf8JsonReader é um tipo
de baixo nível que pode ser usado para criar analisadores e desserializadores
personalizados.

Utf8JsonReader é um ref struct


O JsonTextReader no Newtonsoft.Json é uma classe. O tipo Utf8JsonReader difere
porque é um ref struct. Para obter mais informações, confira limitações de struct ref para
Utf8JsonReader.

Ler valores nulos em tipos de valor anulável


Newtonsoft.Json fornece APIs que retornam Nullable<T>, como ReadAsBoolean , que

identifica um Null TokenType para você retornando um bool? . As APIs internas de


System.Text.Json retornam apenas tipos de valor não anuláveis. Para obter mais

informações, consulte Ler valores nulos em tipos de valor anulável.

Vários destinos para leitura de JSON


Se você precisa continuar usando o Newtonsoft.Json para determinadas estruturas de
destino, é possível ter vários destinos e duas implementações. No entanto, não é
comum e exigiria algumas #ifdefs e duplicação de origem. Uma forma de compartilhar
o máximo de código possível é criar um wrapper ref struct próximo a Utf8JsonReader
e Newtonsoft.Json.JsonTextReader . Esse wrapper unificaria a área de superfície pública
ao isolar as diferenças comportamentais. Assim, é possível isolar as alterações
principalmente na construção do tipo, junto com a aprovação do novo tipo por
referência. Esse é o padrão que a biblioteca Microsoft.Extensions.DependencyModel
segue:

UnifiedJsonReader.JsonTextReader.cs
UnifiedJsonReader.Utf8JsonReader.cs

Utf8JsonWriter vs. JsonTextWriter


System.Text.Json.Utf8JsonWriter é uma forma de alto desempenho para escrita de texto
JSON codificado em UTF-8 com base em tipos .NET comuns como String , Int32 e
DateTime . O gravador é um tipo de baixo nível que pode ser usado para criar

serializadores personalizados.

Gravar valores brutos


Newtonsoft.Json tem um método WriteRawValue que grava JSON bruto onde um valor é

esperado. System.Text.Json tem um equivalente direto: Utf8JsonWriter.WriteRawValue.


Para obter mais informações, consulte Gravar JSON bruto.

Personalizar o formato JSON


JsonTextWriter inclui as seguintes configurações, para as quais Utf8JsonWriter não tem

equivalente:

QuoteChar – especifica o caractere a ser usado para envolver os valores de


cadeia de caracteres. Utf8JsonWriter sempre usa aspas duplas.
QuoteName – especifica se deve ou não envolver nomes de propriedades com
aspas. Utf8JsonWriter sempre os cerca com aspas.

A partir do .NET 9, você pode personalizar o caractere e o tamanho de recuo


Utf8JsonWriter usando as opções expostas pelo struct JsonWriterOptions:

JsonWriterOptions.IndentCharacter

JsonWriterOptions.IndentSize

Não há soluções alternativas que permitem personalizar o JSON produzido pelo


Utf8JsonWriter dessa forma.

Gravar valores de Timespan, Uri ou char


JsonTextWriter fornece métodos WriteValue para valores de TimeSpan , Uri e
char . Utf8JsonWriter não tem métodos equivalentes. Em vez disso, formate esses
valores como cadeias de caracteres (com o nome ToString() , por exemplo) e chame
WriteStringValue.

Vários destinos para gravação JSON


Se você precisa continuar usando o Newtonsoft.Json para determinadas estruturas de
destino, é possível ter vários destinos e duas implementações. No entanto, não é
comum e exigiria algumas #ifdefs e duplicação de origem. Uma forma de compartilhar
o máximo de código possível é criar um wrapper circundando Utf8JsonWriter e
Newtonsoft.Json.JsonTextWriter . Esse wrapper unificaria a área de superfície pública ao

isolar as diferenças comportamentais. Isso permite isolar as alterações principalmente


na construção do tipo. A biblioteca Microsoft.Extensions.DependencyModel é:

UnifiedJsonWriter.JsonTextWriter.cs
UnifiedJsonWriter.Utf8JsonWriter.cs

TypeNameHandling.All não tem suporte


A decisão de excluir a funcionalidade equivalente TypeNameHandling.All de
System.Text.Json foi intencional. Permitir que um conteúdo JSON especifique suas

próprias informações de tipo é uma fonte comum de vulnerabilidades em aplicativos


Web. Em particular, a configuração de Newtonsoft.Json com TypeNameHandling.All
permite que o cliente remoto insira um aplicativo executável inteiro dentro do próprio
conteúdo JSON, de modo que, durante a desserialização, o aplicativo Web extraia e
executa o código inserido. Para obter mais informações, consulte Friday the 13th JSON
attacks PowerPoint e Friday the 13th JSON attacks details .

Consultas de caminho JSON sem suporte


O DOM de JsonDocument não dá suporte à consulta usando o Caminho JSON .

Em um DOM de JsonNode, cada instância JsonNode tem um método GetPath que


retorna um caminho para esse nó. Mas não há nenhuma API interna para lidar com
consultas com base em cadeias de consulta de caminho JSON.

Para obter mais informações, consulte o problema do GitHub dotnet/runtime #31068 .


Alguns limites não configuráveis
System.Text.Json define limites que não podem ser alterados para alguns valores, como
o tamanho máximo do token em caracteres (166 MB) e na base 64 (125 MB). Para obter
mais informações, consulte JsonConstants no código-fonte e o problema do GitHub
dotnet/runtime #39953 .

NaN, Infinity, -Infinity


Newtonsoft analisa tokens de string JSON NaN , Infinity e -Infinity . Com
System.Text.Json, use JsonNumberHandling.AllowNamedFloatingPointLiterals. Para obter
informações sobre como usar essa configuração, consulte Permitir ou gravar números
entre aspas.

Recursos adicionais
System.Text.Json overview
Como serializar e desserializar JSON

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como instanciar as instâncias de
JsonSerializerOptions com
System.Text.Json
Artigo • 23/02/2024

Este artigo explica como evitar problemas de desempenho ao usar


JsonSerializerOptions. Ele também mostra como usar os construtores parametrizados
disponíveis.

Reutilizar instâncias de JsonSerializerOptions


Se você usar JsonSerializerOptions repetidamente com as mesmas opções, não crie
uma instância JsonSerializerOptions sempre que a usar. Reutilize a mesma instância
para cada chamada. Essa orientação se aplica ao código que você escreve para
conversores personalizados e ao chamar JsonSerializer.Serialize ou
JsonSerializer.Deserialize. É seguro usar a mesma instância em vários threads. Os caches
de metadados na instância de opções são thread-safe, e a instância é imutável após a
primeira serialização ou desserialização.

A propriedade JsonSerializerOptions.Default
Se a instância de JsonSerializerOptions da qual você precisa usar for a instância padrão
(tem todas as configurações padrão e os conversores padrão), use a propriedade
JsonSerializerOptions.Default em vez de criar uma instância de opções. Para obter mais
informações, consulte Usar conversor de sistema padrão.

Copiar JsonSerializerOptions
Há um construtor JsonSerializerOptions que permite criar uma nova instância com as
mesmas opções de uma instância existente, conforme mostrado no exemplo a seguir:

C#

using System.Text.Json;

namespace CopyOptions
{
public class Forecast
{
public DateTime Date { get; init; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new()
{
Date = DateTime.Now,
TemperatureC = 40,
Summary = "Hot"
};

JsonSerializerOptions options = new()


{
WriteIndented = true
};

JsonSerializerOptions optionsCopy = new(options);


string forecastJson =
JsonSerializer.Serialize<Forecast>(forecast, optionsCopy);

Console.WriteLine($"Output JSON:\n{forecastJson}");
}
}
}

// Produces output like the following example:


//
//Output JSON:
//{
// "Date": "2020-10-21T15:40:06.8998502-07:00",
// "TemperatureC": 40,
// "Summary": "Hot"
//}

O cache de metadados da instância existente JsonSerializerOptions não é copiado


para a nova instância. Portanto, usar esse construtor não é o mesmo que reutilizar uma
instância existente de JsonSerializerOptions .

Padrões da Web para JsonSerializerOptions


As seguintes opções têm padrões diferentes para aplicativos Web:

PropertyNameCaseInsensitive = true
JsonNamingPolicy = CamelCase
NumberHandling = AllowReadingFromString
No .NET 9 e versões posteriores, você pode usar o singleton JsonSerializerOptions.Web
para serializar com as opções padrão que o ASP.NET Core usa para aplicativos Web. Em
versões anteriores, chame o construtor JsonSerializerOptions para criar uma nova
instância com os padrões da Web, conforme mostrado no exemplo a seguir:

C#

using System.Text.Json;

namespace OptionsDefaults
{
public class Forecast
{
public DateTime? Date { get; init; }
public int TemperatureC { get; set; }
public string? Summary { get; set; }
};

public class Program


{
public static void Main()
{
Forecast forecast = new()
{
Date = DateTime.Now,
TemperatureC = 40,
Summary = "Hot"
};

JsonSerializerOptions options = new(JsonSerializerDefaults.Web)


{
WriteIndented = true
};

Console.WriteLine(
$"PropertyNameCaseInsensitive:
{options.PropertyNameCaseInsensitive}");
Console.WriteLine(
$"JsonNamingPolicy: {options.PropertyNamingPolicy}");
Console.WriteLine(
$"NumberHandling: {options.NumberHandling}");

string forecastJson = JsonSerializer.Serialize<Forecast>


(forecast, options);
Console.WriteLine($"Output JSON:\n{forecastJson}");

Forecast? forecastDeserialized =
JsonSerializer.Deserialize<Forecast>(forecastJson, options);

Console.WriteLine($"Date: {forecastDeserialized?.Date}");
Console.WriteLine($"TemperatureC:
{forecastDeserialized?.TemperatureC}");
Console.WriteLine($"Summary: {forecastDeserialized?.Summary}");
}
}
}

// Produces output like the following example:


//
//PropertyNameCaseInsensitive: True
//JsonNamingPolicy: System.Text.Json.JsonCamelCaseNamingPolicy
//NumberHandling: AllowReadingFromString
//Output JSON:
//{
// "date": "2020-10-21T15:40:06.9040831-07:00",
// "temperatureC": 40,
// "summary": "Hot"
//}
//Date: 10 / 21 / 2020 3:40:06 PM
//TemperatureC: 40
//Summary: Hot

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como habilitar a correspondência de
nomes de propriedade que não
diferenciam maiúsculas de minúsculas
com System.Text.Json
Artigo • 26/05/2023

Neste artigo, você aprenderá a habilitar a correspondência de nome de propriedade


que não diferencia maiúsculas de minúsculas com o namespace System.Text.Json .

Correspondência de propriedades que não


diferencia maiúsculas de minúsculas
Por padrão, a desserialização procura correspondências de nome de propriedade que
diferenciam maiúsculas de minúsculas entre o JSON e as propriedades do objeto alvo.
Para alterar esse comportamento, defina
JsonSerializerOptions.PropertyNameCaseInsensitive como true :

7 Observação

O padrão da Web não diferencia maiúsculas de minúsculas.

C#

var options = new JsonSerializerOptions


{
PropertyNameCaseInsensitive = true
};
var weatherForecast = JsonSerializer.Deserialize<WeatherForecast>
(jsonString, options);

Aqui está o exemplo JSON com nomes de propriedade em minúsculas concatenadas.


Ele pode ser desserializado no tipo a seguir que tem nomes de propriedade em Pascal
case.

JSON

{
"date": "2019-08-01T00:00:00-07:00",
"temperatureCelsius": 25,
"summary": "Hot",
}

C#

public class WeatherForecast


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como preservar referências e manipular
ou ignorar referências circulares em
System.Text.Json
Artigo • 12/03/2024

Este artigo mostra como preservar referências e manipular ou ignorar referências


circulares ao usar System.Text.Json para serializar e desserializar JSON no .NET

Preservar referências e lidar com referências


circulares
Para preservar referências e lidar com referências circulares, defina ReferenceHandler
como Preserve. Essa configuração causa o seguinte comportamento:

Ao serializar:

Ao escrever tipos complexos, o serializador também grava propriedades de


metadados ( $id , $values e $ref ).

Ao desserializar:

Metadados são esperados (embora não obrigatórios) e o desserializador tenta


entendê-los.

O código a seguir ilustra o uso da configuração Preserve .

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace PreserveReferences
{
public class Employee
{
public string? Name { get; set; }
public Employee? Manager { get; set; }
public List<Employee>? DirectReports { get; set; }
}

public class Program


{
public static void Main()
{
Employee tyler = new()
{
Name = "Tyler Stein"
};

Employee adrian = new()


{
Name = "Adrian King"
};

tyler.DirectReports = new List<Employee> { adrian };


adrian.Manager = tyler;

JsonSerializerOptions options = new()


{
ReferenceHandler = ReferenceHandler.Preserve,
WriteIndented = true
};

string tylerJson = JsonSerializer.Serialize(tyler, options);


Console.WriteLine($"Tyler serialized:\n{tylerJson}");

Employee? tylerDeserialized =
JsonSerializer.Deserialize<Employee>(tylerJson, options);

Console.WriteLine(
"Tyler is manager of Tyler's first direct report: ");
Console.WriteLine(
tylerDeserialized?.DirectReports?[0].Manager ==
tylerDeserialized);
}
}
}

// Produces output like the following example:


//
//Tyler serialized:
//{
// "$id": "1",
// "Name": "Tyler Stein",
// "Manager": null,
// "DirectReports": {
// "$id": "2",
// "$values": [
// {
// "$id": "3",
// "Name": "Adrian King",
// "Manager": {
// "$ref": "1"
// },
// "DirectReports": null
// }
// ]
// }
//}
//Tyler is manager of Tyler's first direct report:
//True

Esse recurso não pode ser usado para preservar tipos de valor ou tipos imutáveis. Na
desserialização, a instância de um tipo imutável é criada depois que toda a carga útil é
lida. Portanto, seria impossível desserializar a mesma instância se uma referência a ela
aparecesse na carga JSON.

Para tipos de valor, tipos imutáveis e matrizes, nenhum metadados de referência é


serializado. Na desserialização, uma exceção é lançada se $ref ou $id for encontrado.
No entanto, os tipos de valor ignoram $id (e $values no caso de coleções) para
possibilitar desserializar cargas que foram serializadas usando Newtonsoft.Json.
Newtonsoft.Jsonserializa metadados para esses tipos.

Para determinar se os objetos são iguais, System.Text.Json usa


ReferenceEqualityComparer.Instance, que usa igualdade de referência
(Object.ReferenceEquals(Object, Object)) em vez de igualdade de valor
(Object.Equals(Object)) ao comparar duas instâncias de objeto.

Para obter mais informações sobre como as referências são serializadas e


desserializadas, consulte ReferenceHandler.Preserve.

A classe ReferenceResolver define o comportamento de preservação de referências na


serialização e desserialização. Crie uma classe derivada para especificar o
comportamento personalizado. Para obter um exemplo, consulte
GuidReferenceResolver .

Persistir metadados de referência em várias chamadas de


serialização e desserialização
Por padrão, os dados de referência só são armazenados em cache para cada chamada a
Serialize ou Deserialize. Para persistir as referências de uma chamada
Serialize / Deserialize para outra, enraíze a instância ReferenceResolver no site de

chamada de Serialize / Deserialize . O seguinte código mostra um exemplo para esse


cenário:

Você tem uma lista de objetos Employee e precisa serializar cada um


individualmente.
Você deseja aproveitar as referências salvas no resolvedor para o
ReferenceHandler .

Aqui está a classe Employee :


C#

public class Employee


{
public string? Name { get; set; }
public Employee? Manager { get; set; }
public List<Employee>? DirectReports { get; set; }
}

Uma classe derivada de ReferenceResolver armazena as referências em um dicionário:

C#

class MyReferenceResolver : ReferenceResolver


{
private uint _referenceCount;
private readonly Dictionary<string, object> _referenceIdToObjectMap =
new ();
private readonly Dictionary<object, string> _objectToReferenceIdMap =
new (ReferenceEqualityComparer.Instance);

public override void AddReference(string referenceId, object value)


{
if (!_referenceIdToObjectMap.TryAdd(referenceId, value))
{
throw new JsonException();
}
}

public override string GetReference(object value, out bool


alreadyExists)
{
if (_objectToReferenceIdMap.TryGetValue(value, out string?
referenceId))
{
alreadyExists = true;
}
else
{
_referenceCount++;
referenceId = _referenceCount.ToString();
_objectToReferenceIdMap.Add(value, referenceId);
alreadyExists = false;
}

return referenceId;
}

public override object ResolveReference(string referenceId)


{
if (!_referenceIdToObjectMap.TryGetValue(referenceId, out object?
value))
{
throw new JsonException();
}

return value;
}
}

Uma classe derivada de ReferenceHandler contém uma instância e MyReferenceResolver


cria uma nova instância somente quando necessário (em um método chamado Reset
neste exemplo):

C#

class MyReferenceHandler : ReferenceHandler


{
public MyReferenceHandler() => Reset();
private ReferenceResolver? _rootedResolver;
public override ReferenceResolver CreateResolver() => _rootedResolver!;
public void Reset() => _rootedResolver = new MyReferenceResolver();
}

Quando o código de exemplo chama o serializador, ele usa uma instância


JsonSerializerOptions na qual a propriedade ReferenceHandler é definida como uma
instância de MyReferenceHandler . Ao seguir esse padrão, redefina o dicionário
ReferenceResolver ao terminar de serializar para evitar que ele continue crescendo para

sempre.

C#

var options = new JsonSerializerOptions();


options.WriteIndented = true;
var myReferenceHandler = new MyReferenceHandler();
options.ReferenceHandler = myReferenceHandler;

string json;
foreach (Employee emp in employees)
{
json = JsonSerializer.Serialize(emp, options);
DoSomething(json);
}

// Reset after serializing to avoid out of bounds memory growth in the


resolver.
myReferenceHandler.Reset();

Ignorar referências circulares


Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Como serializar propriedades de classes
derivadas com System.Text.Json
Artigo • 02/06/2023

Neste artigo, você aprenderá a serializar propriedades de classes derivadas com o


namespace System.Text.Json .

Serializar propriedades de classes derivadas


Começando no .NET 7, System.Text.Json dá suporte à serialização e à desserialização
de hierarquia de tipo polimórfico com anotações de atributo.

Atributo Descrição

JsonDerivedTypeAttribute Quando colocado em uma declaração de tipo, indica que o subtipo


especificado deve ser aceito pela serialização polimórfica. Também
expõe a capacidade de especificar um descriminador de tipo.

JsonPolymorphicAttribute Quando colocado em uma declaração de tipo, indica que o tipo deve
ser serializado polimorficamente. Também expõe várias opções para
configurar a serialização e a desserialização polimórfica para esse
tipo.

Por exemplo, suponha que você tenha uma classe WeatherForecastBase e uma classe
derivada WeatherForecastWithCity :

C#

[JsonDerivedType(typeof(WeatherForecastWithCity))]
public class WeatherForecastBase
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

C#

public class WeatherForecastWithCity : WeatherForecastBase


{
public string? City { get; set; }
}
Suponha também que o argumento de tipo do método Serialize<TValue> em tempo
de compilação seja WeatherForecastBase :

C#

options = new JsonSerializerOptions


{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize<WeatherForecastBase>
(weatherForecastBase, options);

Nesse cenário, a propriedade City é serializada porque o objeto weatherForecastBase é,


na verdade, um objeto WeatherForecastWithCity . Essa configuração habilita a
serialização polimórfica para WeatherForecastBase , especificamente quando o tipo de
runtime é WeatherForecastWithCity :

JSON

{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}

Embora haja suporte para viagens de ia e volta do conteúdo como


WeatherForecastBase , isso não se materializará como um tipo de tempo de execução de
WeatherForecastWithCity . Em vez disso, se materializará como um tipo de tempo de

execução de WeatherForecastBase :

C#

WeatherForecastBase value = JsonSerializer.Deserialize<WeatherForecastBase>


("""
{
"City": "Milwaukee",
"Date": "2022-09-26T00:00:00-05:00",
"TemperatureCelsius": 15,
"Summary": "Cool"
}
""");

Console.WriteLine(value is WeatherForecastWithCity); // False


A seção a seguir descreve como adicionar metadados para habilitar viagens de ida e
volta do tipo derivado.

Discriminadores de tipo polimórfico


Para habilitar a desserialização polimórfica, você precisa especificar um discriminador de
tipo para a classe derivada:

C#

[JsonDerivedType(typeof(WeatherForecastBase), typeDiscriminator: "base")]


[JsonDerivedType(typeof(WeatherForecastWithCity), typeDiscriminator:
"withCity")]
public class WeatherForecastBase
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class WeatherForecastWithCity : WeatherForecastBase


{
public string? City { get; set; }
}

Com os metadados adicionados, especificamente o discriminador de tipo, o serializador


pode serializar e desserializar o conteúdo como o tipo WeatherForecastWithCity de seu
tipo base WeatherForecastBase . A serialização emitirá JSON com os metadados do
discriminador de tipo:

C#

WeatherForecastBase weather = new WeatherForecastWithCity


{
City = "Milwaukee",
Date = new DateTimeOffset(2022, 9, 26, 0, 0, 0, TimeSpan.FromHours(-5)),
TemperatureCelsius = 15,
Summary = "Cool"
}
var json = JsonSerializer.Serialize<WeatherForecastBase>(weather, options);
Console.WriteLine(json);
// Sample output:
// {
// "$type" : "withCity",
// "City": "Milwaukee",
// "Date": "2022-09-26T00:00:00-05:00",
// "TemperatureCelsius": 15,
// "Summary": "Cool"
// }

Com o discriminador de tipo, o serializador pode desserializar o conteúdo de maneira


polimórfica como WeatherForecastWithCity :

C#

WeatherForecastBase value = JsonSerializer.Deserialize<WeatherForecastBase>


(json);
Console.WriteLine(value is WeatherForecastWithCity); // True

7 Observação

O tipo discriminatório deve ser colocado no início do objeto JSON, agrupado com
outras propriedades de metadados, como $id e $ref .

VB

Dim value As WeatherForecastBase = JsonSerializer.Deserialize(json)


Console.WriteLine(value is WeatherForecastWithCity) // True

Combinação de formatos de discriminador de tipo


Identificadores de discriminador de tipo são válidos em formulários string ou int ,
portanto, o seguinte é válido:

C#

[JsonDerivedType(typeof(WeatherForecastWithCity), 0)]
[JsonDerivedType(typeof(WeatherForecastWithTimeSeries), 1)]
[JsonDerivedType(typeof(WeatherForecastWithLocalNews), 2)]
public class WeatherForecastBase { }

var json = JsonSerializer.Serialize<WeatherForecastBase>(new


WeatherForecastWithTimeSeries());
Console.WriteLine(json);
// Sample output:
// {
// "$type" : 1,
// Omitted for brevity...
// }
Embora a API dê suporte à combinação de configurações de discriminador de tipo, isso
não é recomendável. A recomendação geral é usar apenas discriminadores de tipo
string , apenas discriminadores de tipo int ou nenhum discriminador. O seguinte

exemplo mostra como combinar configurações de discriminador de tipo:

C#

[JsonDerivedType(typeof(ThreeDimensionalPoint), typeDiscriminator: 3)]


[JsonDerivedType(typeof(FourDimensionalPoint), typeDiscriminator: "4d")]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}

public class ThreeDimensionalPoint : BasePoint


{
public int Z { get; set; }
}

public sealed class FourDimensionalPoint : ThreeDimensionalPoint


{
public int W { get; set; }
}

No exemplo anterior, o tipo BasePoint não tem um discriminador de tipo, enquanto o


tipo ThreeDimensionalPoint tem um discriminador de tipo int e o
FourDimensionalPoint tem um discriminador de tipo string .

) Importante

Para que a serialização polimórfica funcione, o tipo do valor serializado deve ser de
base polimórfica. Isso inclui o uso do tipo base como o parâmetro de tipo genérico
ao serializar valores de nível raiz, como o tipo declarado de propriedades
serializadas ou como o elemento de coleção em coleções serializadas.

C#

using System.Text.Json;
using System.Text.Json.Serialization;

PerformRoundTrip<BasePoint>();
PerformRoundTrip<ThreeDimensionalPoint>();
PerformRoundTrip<FourDimensionalPoint>();

static void PerformRoundTrip<T>() where T : BasePoint, new()


{
var json = JsonSerializer.Serialize<BasePoint>(new T());
Console.WriteLine(json);

BasePoint? result = JsonSerializer.Deserialize<BasePoint>(json);


Console.WriteLine($"result is {typeof(T)}; // {result is T}");
Console.WriteLine();
}
// Sample output:
// { "X": 541, "Y": 503 }
// result is BasePoint; // True
//
// { "$type": 3, "Z": 399, "X": 835, "Y": 78 }
// result is ThreeDimensionalPoint; // True
//
// { "$type": "4d", "W": 993, "Z": 427, "X": 508, "Y": 741 }
// result is FourDimensionalPoint; // True

Personalizar o nome do discriminador de tipo


O nome da propriedade padrão do discriminador de tipo é $type . Para personalizar o
nome da propriedade, use JsonPolymorphicAttribute conforme mostrado no seguinte
exemplo:

C#

[JsonPolymorphic(TypeDiscriminatorPropertyName = "$discriminator")]
[JsonDerivedType(typeof(ThreeDimensionalPoint), typeDiscriminator: "3d")]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}

public sealed class ThreeDimensionalPoint : BasePoint


{
public int Z { get; set; }
}

No código anterior, o atributo JsonPolymorphic configura o


TypeDiscriminatorPropertyName para o valor "$discriminator" . Com o nome do

discriminador de tipo configurado, o seguinte exemplo mostra o tipo


ThreeDimensionalPoint serializado como JSON:

C#

BasePoint point = new ThreeDimensionalPoint { X = 1, Y = 2, Z = 3 };


var json = JsonSerializer.Serialize<BasePoint>(point);
Console.WriteLine(json);
// Sample output:
// { "$discriminator": "3d", "X": 1, "Y": 2, "Z": 3 }

 Dica

Evite usar um JsonPolymorphicAttribute.TypeDiscriminatorPropertyName que


entrar em conflito com uma propriedade em sua hierarquia de tipos.

Manipular tipos derivados desconhecidos


Para lidar com tipos derivados desconhecidos, você precisa aceitar esse suporte usando
uma anotação no tipo base. Considere a seguinte hierarquia de tipos:

C#

[JsonDerivedType(typeof(ThreeDimensionalPoint))]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}

public class ThreeDimensionalPoint : BasePoint


{
public int Z { get; set; }
}

public class FourDimensionalPoint : ThreeDimensionalPoint


{
public int W { get; set; }
}

Como a configuração não aceita explicitamente o suporte para FourDimensionalPoint ,


tentar serializar instâncias de FourDimensionalPoint como BasePoint resultará em uma
exceção em tempo de execução:

C#

JsonSerializer.Serialize<BasePoint>(new FourDimensionalPoint()); // throws


NotSupportedException

Você pode alterar o comportamento padrão usando a enumeração


JsonUnknownDerivedTypeHandling, que pode ser especificada da seguinte maneira:

C#
[JsonPolymorphic(
UnknownDerivedTypeHandling =
JsonUnknownDerivedTypeHandling.FallBackToBaseType)]
[JsonDerivedType(typeof(ThreeDimensionalPoint))]
public class BasePoint
{
public int X { get; set; }
public int Y { get; set; }
}

public class ThreeDimensionalPoint : BasePoint


{
public int Z { get; set; }
}

public class FourDimensionalPoint : ThreeDimensionalPoint


{
public int W { get; set; }
}

Em vez fazer fallback para o tipo base, você pode usar a configuração
FallBackToNearestAncestor para fazer fallback para o contrato do tipo derivado

declarado mais próximo:

C#

[JsonPolymorphic(
UnknownDerivedTypeHandling =
JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
[JsonDerivedType(typeof(BasePoint)]
public interface IPoint { }

public class BasePoint : IPoint { }

public class ThreeDimensionalPoint : BasePoint { }

Com uma configuração como o exemplo anterior, o tipo ThreeDimensionalPoint será


serializado como BasePoint :

C#

// Serializes using the contract for BasePoint


JsonSerializer.Serialize<IPoint>(new ThreeDimensionalPoint());

No entanto, fazer fallback para o ancestral mais próximo admite a possibilidade de uma
ambiguidade "diamante". Considere a seguinte hierarquia de tipos como exemplo:
C#

[JsonPolymorphic(
UnknownDerivedTypeHandling =
JsonUnknownDerivedTypeHandling.FallBackToNearestAncestor)]
[JsonDerivedType(typeof(BasePoint))]
[JsonDerivedType(typeof(IPointWithTimeSeries))]
public interface IPoint { }

public interface IPointWithTimeSeries : IPoint { }

public class BasePoint : IPoint { }

public class BasePointWithTimeSeries : BasePoint, IPointWithTimeSeries { }

Nesse caso, o tipo BasePointWithTimeSeries pode ser serializado como BasePoint ou


IPointWithTimeSeries , pois ambos são ancestrais diretos. Essa ambiguidade fará com

que NotSupportedException seja gerado ao tentar serializar uma instância de


BasePointWithTimeSeries como IPoint .

C#

// throws NotSupportedException
JsonSerializer.Serialize<IPoint>(new BasePointWithTimeSeries());

Configurar o polimorfismo com o modelo de


contrato
Para casos de uso em que anotações de atributo são impraticáveis ou impossíveis
(como modelos de domínio grandes, hierarquias entre assemblies ou hierarquias em
dependências de terceiros) use o modelo de contrato para configurar o polimorfismo. O
modelo de contrato é um conjunto de APIs que podem ser usadas para configurar o
polimorfismo em uma hierarquia de tipos criando uma subclasse personalizada
DefaultJsonTypeInfoResolver que fornece dinamicamente a configuração polimórfica
por tipo, conforme mostrado no seguinte exemplo:

C#

public class PolymorphicTypeResolver : DefaultJsonTypeInfoResolver


{
public override JsonTypeInfo GetTypeInfo(Type type,
JsonSerializerOptions options)
{
JsonTypeInfo jsonTypeInfo = base.GetTypeInfo(type, options);
Type basePointType = typeof(BasePoint);
if (jsonTypeInfo.Type == basePointType)
{
jsonTypeInfo.PolymorphismOptions = new JsonPolymorphismOptions
{
TypeDiscriminatorPropertyName = "$point-type",
IgnoreUnrecognizedTypeDiscriminators = true,
UnknownDerivedTypeHandling =
JsonUnknownDerivedTypeHandling.FailSerialization,
DerivedTypes =
{
new JsonDerivedType(typeof(ThreeDimensionalPoint),
"3d"),
new JsonDerivedType(typeof(FourDimensionalPoint), "4d")
}
};
}

return jsonTypeInfo;
}
}

Detalhes adicionais da serialização polimórfica


A serialização polimórfica dá suporte a tipos derivados que foram aceitos
explicitamente por meio do JsonDerivedTypeAttribute. Tipos não declarados
resultarão em uma exceção em tempo de execução. O comportamento pode ser
alterado configurando a propriedade
JsonPolymorphicAttribute.UnknownDerivedTypeHandling.
A configuração polimórfica especificada nos tipos derivados não é herdada pela
configuração polimórfica nos tipos base. O tipo base deve ser configurado
independentemente.
As hierarquias polimórficas têm suporte para os tipos interface e class .
O polimorfismo usando discriminadores de tipo só tem suporte para hierarquias
de tipos que usam conversores padrão para objetos, coleções e tipos de
dicionário.
O polimorfismo tem suporte na geração de origem baseada em metadados, mas
não na geração de origem de caminho rápido.

Confira também
Visão geral de System.Text.Json
Como serializar e desserializar JSON
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como usar um modelo de objeto de
documento do JSON em
System.Text.Json
Artigo • 02/06/2023

Este artigo mostra como usar um DOM (modelo de objeto de documento) do JSON
para acesso aleatório a dados em um conteúdo do JSON.

Opções de DOM JSON


Trabalhar com um DOM é uma alternativa à desserialização com JsonSerializer quando:

Você não tem um tipo para desserializar.


O JSON que você recebe não tem um esquema fixo e deve ser inspecionado para
saber o que ele contém.

System.Text.Json fornece duas maneiras de criar um DOM JSON:

JsonDocument fornece a capacidade de criar um DOM somente leitura usando


Utf8JsonReader . Os elementos JSON que compõem o conteúdo podem ser

acessados pelo tipo JsonElement. O tipo JsonElement fornece os enumeradores de


objeto e de matriz JSON junto com as APIs para converter o texto JSON em tipos
.NET comuns. JsonDocument expõe uma propriedade RootElement. Para obter mais
informações, consulte Usar JsonDocument posteriormente neste artigo.

JsonNode e as classes que derivam dele no namespace System.Text.Json.Nodes


fornecem a capacidade de criar um DOM mutável. Os elementos JSON que
compõem o conteúdo podem ser acessados ​por meio dos tipos JsonNode,
JsonObject, JsonArray, JsonValue e JsonElement. Para obter mais informações,
consulte Usar JsonNode posteriormente neste artigo.

Considere os seguintes fatores ao escolher entre JsonDocument e JsonNode :

O DOM JsonNode pode ser alterado depois de criado. O DOM JsonDocument é


imutável.
O DOM JsonDocument fornece acesso mais rápido aos seus dados.

Use JsonNode .
O exemplo a seguir mostra como usar JsonNode e os outros tipos no namespace
System.Text.Json.Nodes para:

Criar um DOM com base em uma cadeia de caracteres JSON


Escreva JSON de um DOM.
Obtenha um valor, objeto ou matriz de um DOM.

C#

using System.Text.Json;
using System.Text.Json.Nodes;

namespace JsonNodeFromStringExample;

public class Program


{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00"",
""Temperature"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00"",
""2019-08-02T00:00:00""
],
""TemperatureRanges"": {
""Cold"": {
""High"": 20,
""Low"": -10
},
""Hot"": {
""High"": 60,
""Low"": 20
}
}
}
";
// Create a JsonNode DOM from a JSON string.
JsonNode forecastNode = JsonNode.Parse(jsonString)!;

// Write JSON from a JsonNode


var options = new JsonSerializerOptions { WriteIndented = true };
Console.WriteLine(forecastNode!.ToJsonString(options));
// output:
//{
// "Date": "2019-08-01T00:00:00",
// "Temperature": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00",
// "2019-08-02T00:00:00"
// ],
// "TemperatureRanges": {
// "Cold": {
// "High": 20,
// "Low": -10
// },
// "Hot": {
// "High": 60,
// "Low": 20
// }
// }
//}

// Get value from a JsonNode.


JsonNode temperatureNode = forecastNode!["Temperature"]!;
Console.WriteLine($"Type={temperatureNode.GetType()}");
Console.WriteLine($"JSON={temperatureNode.ToJsonString()}");
//output:
//Type =
System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement]
//JSON = 25

// Get a typed value from a JsonNode.


int temperatureInt = (int)forecastNode!["Temperature"]!;
Console.WriteLine($"Value={temperatureInt}");
//output:
//Value=25

// Get a typed value from a JsonNode by using GetValue<T>.


temperatureInt = forecastNode!["Temperature"]!.GetValue<int>();
Console.WriteLine($"TemperatureInt={temperatureInt}");
//output:
//Value=25

// Get a JSON object from a JsonNode.


JsonNode temperatureRanges = forecastNode!["TemperatureRanges"]!;
Console.WriteLine($"Type={temperatureRanges.GetType()}");
Console.WriteLine($"JSON={temperatureRanges.ToJsonString()}");
//output:
//Type = System.Text.Json.Nodes.JsonObject
//JSON = { "Cold":{ "High":20,"Low":-10},"Hot":{ "High":60,"Low":20}
}

// Get a JSON array from a JsonNode.


JsonNode datesAvailable = forecastNode!["DatesAvailable"]!;
Console.WriteLine($"Type={datesAvailable.GetType()}");
Console.WriteLine($"JSON={datesAvailable.ToJsonString()}");
//output:
//datesAvailable Type = System.Text.Json.Nodes.JsonArray
//datesAvailable JSON =["2019-08-01T00:00:00", "2019-08-
02T00:00:00"]

// Get an array element value from a JsonArray.


JsonNode firstDateAvailable = datesAvailable[0]!;
Console.WriteLine($"Type={firstDateAvailable.GetType()}");
Console.WriteLine($"JSON={firstDateAvailable.ToJsonString()}");
//output:
//Type =
System.Text.Json.Nodes.JsonValue`1[System.Text.Json.JsonElement]
//JSON = "2019-08-01T00:00:00"

// Get a typed value by chaining references.


int coldHighTemperature = (int)forecastNode["TemperatureRanges"]!
["Cold"]!["High"]!;
Console.WriteLine($"TemperatureRanges.Cold.High=
{coldHighTemperature}");
//output:
//TemperatureRanges.Cold.High = 20

// Parse a JSON array


var datesNode = JsonNode.Parse(@"[""2019-08-01T00:00:00"",""2019-08-
02T00:00:00""]");
JsonNode firstDate = datesNode![0]!.GetValue<DateTime>();
Console.WriteLine($"firstDate={ firstDate}");
//output:
//firstDate = "2019-08-01T00:00:00"
}
}

Criar um DOM JsonNode com inicializadores de objeto e


fazer alterações
O exemplo a seguir mostra como:

Crie um DOM usando inicializadores de objeto.


Faça alterações em um DOM.

C#

using System.Text.Json;
using System.Text.Json.Nodes;

namespace JsonNodeFromObjectExample;

public class Program


{
public static void Main()
{
// Create a new JsonObject using object initializers.
var forecastObject = new JsonObject
{
["Date"] = new DateTime(2019, 8, 1),
["Temperature"] = 25,
["Summary"] = "Hot",
["DatesAvailable"] = new JsonArray(
new DateTime(2019, 8, 1), new DateTime(2019, 8, 2)),
["TemperatureRanges"] = new JsonObject
{
["Cold"] = new JsonObject
{
["High"] = 20,
["Low"] = -10
}
},
["SummaryWords"] = new JsonArray("Cool", "Windy", "Humid")
};

// Add an object.
forecastObject!["TemperatureRanges"]!["Hot"] =
new JsonObject { ["High"] = 60, ["Low"] = 20 };

// Remove a property.
forecastObject.Remove("SummaryWords");

// Change the value of a property.


forecastObject["Date"] = new DateTime(2019, 8, 3);

var options = new JsonSerializerOptions { WriteIndented = true };


Console.WriteLine(forecastObject.ToJsonString(options));
//output:
//{
// "Date": "2019-08-03T00:00:00",
// "Temperature": 25,
// "Summary": "Hot",
// "DatesAvailable": [
// "2019-08-01T00:00:00",
// "2019-08-02T00:00:00"
// ],
// "TemperatureRanges": {
// "Cold": {
// "High": 20,
// "Low": -10
// },
// "Hot": {
// "High": 60,
// "Low": 20
// }
// }
//}
}
}

Desserializar subseções de um conteúdo JSON


O exemplo a seguir mostra como usar jsonNode para navegar até uma subseção de
uma árvore JSON e desserializar um único valor, um tipo personalizado ou uma matriz
dessa subseção.
C#

using System.Text.Json;
using System.Text.Json.Nodes;

namespace JsonNodePOCOExample;

public class TemperatureRanges : Dictionary<string, HighLowTemps>


{
}

public class HighLowTemps


{
public int High { get; set; }
public int Low { get; set; }
}

public class Program


{
public static DateTime[]? DatesAvailable { get; set; }

public static void Main()


{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00"",
""Temperature"": 25,
""Summary"": ""Hot"",
""DatesAvailable"": [
""2019-08-01T00:00:00"",
""2019-08-02T00:00:00""
],
""TemperatureRanges"": {
""Cold"": {
""High"": 20,
""Low"": -10
},
""Hot"": {
""High"": 60,
""Low"": 20
}
}
}
";
// Parse all of the JSON.
JsonNode forecastNode = JsonNode.Parse(jsonString)!;

// Get a single value


int hotHigh = forecastNode["TemperatureRanges"]!["Hot"]!
["High"]!.GetValue<int>();
Console.WriteLine($"Hot.High={hotHigh}");
// output:
//Hot.High=60
// Get a subsection and deserialize it into a custom type.
JsonObject temperatureRangesObject = forecastNode!
["TemperatureRanges"]!.AsObject();
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
temperatureRangesObject.WriteTo(writer);
writer.Flush();
TemperatureRanges? temperatureRanges =
JsonSerializer.Deserialize<TemperatureRanges>(stream.ToArray());
Console.WriteLine($"Cold.Low={temperatureRanges!["Cold"].Low},
Hot.High={temperatureRanges["Hot"].High}");
// output:
//Cold.Low=-10, Hot.High=60

// Get a subsection and deserialize it into an array.


JsonArray datesAvailable = forecastNode!
["DatesAvailable"]!.AsArray()!;
Console.WriteLine($"DatesAvailable[0]={datesAvailable[0]}");
// output:
//DatesAvailable[0]=8/1/2019 12:00:00 AM
}
}

Exemplo de nota média JsonNode


O exemplo a seguir seleciona uma matriz JSON que tem valores inteiros e calcula um
valor médio:

C#

using System.Text.Json.Nodes;

namespace JsonNodeAverageGradeExample;

public class Program


{
public static void Main()
{
string jsonString =
@"{
""Class Name"": ""Science"",
""Teacher\u0027s Name"": ""Jane"",
""Semester"": ""2019-01-01"",
""Students"": [
{
""Name"": ""John"",
""Grade"": 94.3
},
{
""Name"": ""James"",
""Grade"": 81.0
},
{
""Name"": ""Julia"",
""Grade"": 91.9
},
{
""Name"": ""Jessica"",
""Grade"": 72.4
},
{
""Name"": ""Johnathan""
}
],
""Final"": true
}
";
double sum = 0;
int count = 0;

JsonNode document = JsonNode.Parse(jsonString)!;

JsonNode root = document.Root;


JsonArray studentsArray = root["Students"]!.AsArray();

count = studentsArray.Count;

foreach (JsonNode? student in studentsArray)


{
if (student?["Grade"] is JsonNode gradeNode)
{
sum += (double)gradeNode;
}
else
{
sum += 70;
}
}

double average = sum / count;


Console.WriteLine($"Average grade : {average}");
}
}
// output:
//Average grade : 81.92

O código anterior:

Calcula uma nota média para objetos em uma matriz Students que tem uma
propriedade Grade .
Atribui uma nota padrão de 70 para alunos sem nota.
Obtém o número de alunos da propriedade Count de JsonArray .
JsonNode com JsonSerializerOptions

Você pode usar JsonSerializer para serializar e desserializar uma instância de


JsonNode . No entanto, se você usar uma sobrecarga que leva JsonSerializerOptions , a

instância de opções só será usada para obter conversores personalizados. Outros


recursos da instância de opções não são usados. Por exemplo, se você definir
JsonSerializerOptions.DefaultIgnoreCondition como WhenWritingNull e chamar
JsonSerializer com uma sobrecarga que leva JsonSerializerOptions , as propriedades

nulas não serão ignoradas.

A mesma limitação se aplica aos métodos JsonNode que usam um parâmetro


JsonSerializerOptions : WriteTo(Utf8JsonWriter, JsonSerializerOptions) e

ToJsonString(JsonSerializerOptions). Essas APIs usam JsonSerializerOptions apenas


para obter conversores personalizados.

O exemplo a seguir ilustra o resultado do uso de métodos que usam um parâmetro


JsonSerializerOptions e serializam uma instância JsonNode :

C#

using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace JsonNodeWithJsonSerializerOptions;

public class Program


{
public static void Main()
{
Person person = new Person { Name = "Nancy" };

// Default serialization - Address property included with null


token.
// Output: {"Name":"Nancy","Address":null}
string personJsonWithNull = JsonSerializer.Serialize(person);
Console.WriteLine(personJsonWithNull);

// Serialize and ignore null properties - null Address property is


omitted
// Output: {"Name":"Nancy"}
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string personJsonWithoutNull = JsonSerializer.Serialize(person,
options);
Console.WriteLine(personJsonWithoutNull);
// Ignore null properties doesn't work when serializing JsonNode
instance
// by using JsonSerializer.
// Output: {"Name":"Nancy","Address":null}
var personJsonNode = JsonSerializer.Deserialize<JsonNode>
(personJsonWithNull);
personJsonWithNull = JsonSerializer.Serialize(personJsonNode,
options);
Console.WriteLine(personJsonWithNull);

// Ignore null properties doesn't work when serializing JsonNode


instance
// by using JsonNode.ToJsonString method.
// Output: {"Name":"Nancy","Address":null}
personJsonWithNull = personJsonNode!.ToJsonString(options);
Console.WriteLine(personJsonWithNull);

// Ignore null properties doesn't work when serializing JsonNode


instance
// by using JsonNode.WriteTo method.
// Output: {"Name":"Nancy","Address":null}
using var stream = new MemoryStream();
using var writer = new Utf8JsonWriter(stream);
personJsonNode!.WriteTo(writer, options);
writer.Flush();
personJsonWithNull = Encoding.UTF8.GetString(stream.ToArray());
Console.WriteLine(personJsonWithNull);
}
}
public class Person
{
public string? Name { get; set; }
public string? Address { get; set; }
}

Se você precisar de recursos JsonSerializerOptions diferentes de conversores


personalizados, use JsonSerializer com destinos fortemente tipados (como a classe
Person neste exemplo) em vez de JsonNode .

Use JsonDocument .
O exemplo a seguir mostra como usar a classe JsonDocument para acesso aleatório a
dados em uma cadeia de caracteres JSON:

C#

double sum = 0;
int count = 0;
using (JsonDocument document = JsonDocument.Parse(jsonString))
{
JsonElement root = document.RootElement;
JsonElement studentsElement = root.GetProperty("Students");
foreach (JsonElement student in studentsElement.EnumerateArray())
{
if (student.TryGetProperty("Grade", out JsonElement gradeElement))
{
sum += gradeElement.GetDouble();
}
else
{
sum += 70;
}
count++;
}
}

double average = sum / count;


Console.WriteLine($"Average grade : {average}");

O código anterior:

Pressupõe que o JSON a ser analisado esteja em uma cadeia de caracteres


chamada jsonString .
Calcula uma nota média para objetos em uma matriz Students que tem uma
propriedade Grade .
Atribui uma nota padrão de 70 para alunos sem nota.
Cria a instância JsonDocument em uma using instrução porque JsonDocument
implementa IDisposable . Depois que uma instância JsonDocument é descartada,
você perde o acesso a todas as suas instâncias JsonElement também. Para manter
o acesso a uma instância JsonElement , faça uma cópia dela antes que a instância
pai JsonDocument seja descartada. Para fazer uma cópia, chame JsonElement.Clone.
Para obter mais informações, consulte JsonDocument é IDisposable.

O código de exemplo anterior conta os alunos incrementando uma variável count com
cada iteração. Uma alternativa é chamar GetArrayLength, conforme mostrado no
exemplo a seguir:

C#

double sum = 0;
int count = 0;

using (JsonDocument document = JsonDocument.Parse(jsonString))


{
JsonElement root = document.RootElement;
JsonElement studentsElement = root.GetProperty("Students");
count = studentsElement.GetArrayLength();

foreach (JsonElement student in studentsElement.EnumerateArray())


{
if (student.TryGetProperty("Grade", out JsonElement gradeElement))
{
sum += gradeElement.GetDouble();
}
else
{
sum += 70;
}
}
}

double average = sum / count;


Console.WriteLine($"Average grade : {average}");

Aqui está um exemplo do JSON que este código processa:

JSON

{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}
Para um exemplo semelhante que usa JsonNode em vez de JsonDocument , consulte
Exemplo de nota média de JsonNode.

Como pesquisar um JsonDocument e JsonElement como


subelementos
As pesquisas no JsonElement exigem uma pesquisa sequencial das propriedades e,
portanto, são relativamente lentas (por exemplo, ao usar TryGetProperty ).
System.Text.Json foi criado para minimizar o tempo inicial de análise em vez do tempo
de pesquisa. Portanto, use as seguintes abordagens para otimizar o desempenho ao
pesquisar por meio de um objeto JsonDocument :

Use os enumeradores internos (EnumerateArray e EnumerateObject) em vez de


fazer sua própria indexação ou loops.
Não faça uma pesquisa sequencial no todo JsonDocument por meio de cada
propriedade usando RootElement . Em vez disso, pesquise objetos JSON aninhados
com base na estrutura conhecida dos dados JSON. Por exemplo, os exemplos de
código anteriores procuram uma propriedade Grade em objetos Student
percorrendo os objetos Student e obtendo o valor de Grade para cada um, em vez
de pesquisar em todos os objetos JsonElement procurando por propriedades
Grade . Fazer o último resultaria em passagens desnecessárias sobre os mesmos

dados.

Usar JsonDocument para gravar JSON


O exemplo a seguir mostra como escrever JSON de um JsonDocument:

C#

string jsonString = File.ReadAllText(inputFileName);

var writerOptions = new JsonWriterOptions


{
Indented = true
};

var documentOptions = new JsonDocumentOptions


{
CommentHandling = JsonCommentHandling.Skip
};

using FileStream fs = File.Create(outputFileName);


using var writer = new Utf8JsonWriter(fs, options: writerOptions);
using JsonDocument document = JsonDocument.Parse(jsonString,
documentOptions);

JsonElement root = document.RootElement;

if (root.ValueKind == JsonValueKind.Object)
{
writer.WriteStartObject();
}
else
{
return;
}

foreach (JsonProperty property in root.EnumerateObject())


{
property.WriteTo(writer);
}

writer.WriteEndObject();

writer.Flush();

O código anterior:

Lê um arquivo JSON, carrega os dados em um JsonDocument e grava JSON


formatado (bem impresso) em um arquivo.
Usa JsonDocumentOptions para especificar que os comentários no JSON de
entrada são permitidos, mas ignorados.
Quando terminar, chama o gravador Flush. Uma alternativa é deixar o gravador
liberar automaticamente quando ele for descartado.

Aqui está um exemplo de entrada JSON a ser processada pelo código de exemplo:

JSON

{"Class Name": "Science","Teacher's Name": "Jane","Semester": "2019-01-


01","Students": [{"Name": "John","Grade": 94.3},{"Name": "James","Grade":
81.0},{"Name": "Julia","Grade": 91.9},{"Name": "Jessica","Grade": 72.4},
{"Name": "Johnathan"}],"Final": true}

O resultado é a seguinte saída JSON bastante impressa:

JSON

{
"Class Name": "Science",
"Teacher\u0027s Name": "Jane",
"Semester": "2019-01-01",
"Students": [
{
"Name": "John",
"Grade": 94.3
},
{
"Name": "James",
"Grade": 81.0
},
{
"Name": "Julia",
"Grade": 91.9
},
{
"Name": "Jessica",
"Grade": 72.4
},
{
"Name": "Johnathan"
}
],
"Final": true
}

JsonDocument é IDisposable
JsonDocument cria uma exibição na memória dos dados em um buffer em pool. Portanto,

o tipo JsonDocument implementa IDisposable e precisa ser usado dentro de um bloco


using .

Retorne apenas JsonDocument de sua API se você quiser transferir a propriedade de


tempo de vida e descartar a responsabilidade para o chamador. Na maioria dos
cenários, isso não é necessário. Se o chamador precisar trabalhar com todo o
documento JSON, retorne o Clone do RootElement, que é um JsonElement. Se o
chamador precisar trabalhar com um elemento específico no documento JSON, retorne
o Clone desse JsonElement. Se você retornar o RootElement ou um subconjunto
diretamente sem fazer um Clone , o chamador não poderá acessar o retornado
JsonElement depois que o proprietário JsonDocument for descartado.

Aqui está um exemplo que exige que você faça um Clone :

C#

public JsonElement LookAndLoad(JsonElement source)


{
string json =
File.ReadAllText(source.GetProperty("fileName").GetString());

using (JsonDocument doc = JsonDocument.Parse(json))


{
return doc.RootElement.Clone();
}
}

O código anterior espera um JsonElement que contenha uma propriedade fileName . Ele
abre um arquivo JSON e cria JsonDocument . O método assume que o chamador deseja
trabalhar com todo o documento, então ele retorna Clone de RootElement .

Se você receber JsonElement e estiver retornando um subelemento, não será necessário


retornar um subconjunto Clone . O chamador é responsável por manter ativo o
JsonDocument pertencente ao JsonElement passado. Por exemplo:

C#

public JsonElement ReturnFileName(JsonElement source)


{
return source.GetProperty("fileName");
}

JsonDocument com JsonSerializerOptions

Você pode usar JsonSerializer para serializar e desserializar uma instância de


JsonDocument . No entanto, a implementação para leitura e gravação de instâncias

JsonDocument usando JsonSerializer é um wrapper por


JsonDocument.ParseValue(Utf8JsonReader) e JsonDocument.WriteTo(Utf8JsonWriter).
Esse wrapper não encaminha nenhum JsonSerializerOptions (recursos de serializador)
para Utf8JsonReader ou Utf8JsonWriter . Por exemplo, se você definir
JsonSerializerOptions.DefaultIgnoreCondition como WhenWritingNull e chamar
JsonSerializer com uma sobrecarga que leva JsonSerializerOptions , as propriedades

nulas não serão ignoradas.

O exemplo a seguir ilustra o resultado do uso de métodos que usam um parâmetro


JsonSerializerOptions e serializam uma instância JsonDocument :

C#

using System.Text;
using System.Text.Json;
using System.Text.Json.Nodes;
using System.Text.Json.Serialization;

namespace JsonDocumentWithJsonSerializerOptions;
public class Program
{
public static void Main()
{
Person person = new Person { Name = "Nancy" };

// Default serialization - Address property included with null


token.
// Output: {"Name":"Nancy","Address":null}
string personJsonWithNull = JsonSerializer.Serialize(person);
Console.WriteLine(personJsonWithNull);

// Serialize and ignore null properties - null Address property is


omitted
// Output: {"Name":"Nancy"}
JsonSerializerOptions options = new()
{
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
};
string personJsonWithoutNull = JsonSerializer.Serialize(person,
options);
Console.WriteLine(personJsonWithoutNull);

// Ignore null properties doesn't work when serializing JsonDocument


instance
// by using JsonSerializer.
// Output: {"Name":"Nancy","Address":null}
var personJsonDocument = JsonSerializer.Deserialize<JsonDocument>
(personJsonWithNull);
personJsonWithNull = JsonSerializer.Serialize(personJsonDocument,
options);
Console.WriteLine(personJsonWithNull);
}
}
public class Person
{
public string? Name { get; set; }

public string? Address { get; set; }


}

Se você precisar de recursos JsonSerializerOptions , use JsonSerializer com destinos


fortemente tipados (como a classe Person neste exemplo) em vez de JsonDocument .

Confira também
Visão geral de System.Text.Json
Referência da API System.Text.Json
System.Text.JsonReferência da API .Serialization
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como usar Utf8JsonWriter no
System.Text.Json
Artigo • 02/06/2023

Este artigo mostra como usar o tipo Utf8JsonWriter para criar serializadores
personalizados.

Utf8JsonWriter é uma forma de alto desempenho para escrita de texto JSON codificado
em UTF-8 com base em tipos .NET comuns como String , Int32 e DateTime . O gravador
é um tipo de baixo nível que pode ser usado para criar serializadores personalizados. O
método JsonSerializer.Serialize é usado sob as coberturas Utf8JsonWriter .

O exemplo a seguir mostra como usar a classe Utf8JsonWriter:

C#

var options = new JsonWriterOptions


{
Indented = true
};

using var stream = new MemoryStream();


using var writer = new Utf8JsonWriter(stream, options);

writer.WriteStartObject();
writer.WriteString("date", DateTimeOffset.UtcNow);
writer.WriteNumber("temp", 42);
writer.WriteEndObject();
writer.Flush();

string json = Encoding.UTF8.GetString(stream.ToArray());


Console.WriteLine(json);

Gravar com texto UTF-8


Para obter o melhor desempenho possível ao usar Utf8JsonWriter , o conteúdo JSON de
gravação já codificado como texto UTF-8 em vez de como cadeias de caracteres UTF-16.
Use JsonEncodedText para armazenar em cache e pré-codificar nomes e valores de
propriedade de cadeia de caracteres conhecidos como estáticos e passá-los para o
gravador, em vez de usar literais de cadeia de caracteres UTF-16. Isso é mais rápido do
que armazenar em cache e usar matrizes de bytes UTF-8.
Essa abordagem também funcionará se você precisar fazer escape personalizado.
System.Text.Json não permite desabilitar a fuga ao escrever uma cadeia de caracteres.

No entanto, você pode passar seu próprio JavaScriptEncoder personalizado como uma
opção para o gravador ou criar o seu próprio JsonEncodedText que usa o
JavascriptEncoder para fazer o escape e, em seguida, gravar JsonEncodedText em vez da

cadeia de caracteres. Para obter mais informações, consulte Personalizar a codificação


de caracteres.

Gravar JSON bruto


Em alguns cenários, convém gravar JSON "bruto" em um conteúdo JSON que você está
criando com Utf8JsonWriter . Você pode usar Utf8JsonWriter.WriteRawValue para fazer
isso. Aqui estão cenários típicos:

Você tem um conteúdo JSON existente que deseja incluir no novo JSON.

Você deseja formatar valores de forma diferente da formatação padrão


Utf8JsonWriter .

Por exemplo, talvez você queira personalizar a formatação numérica. Por padrão,
System.Text.Json omite o ponto decimal para números inteiros, escrevendo 1 em
vez de 1.0 , por exemplo. A lógica é que escrever menos bytes é bom para o
desempenho. Mas suponha que o consumidor de seu JSON trate números com
decimais como duplos e números sem decimais como inteiros. Talvez você queira
garantir que os números em uma matriz sejam todos reconhecidos como duplos,
escrevendo um ponto decimal e zero para números inteiros. O seguinte exemplo
mostra como fazer isso:

C#

using System.Text;
using System.Text.Json;

namespace WriteRawJson;

public class Program


{
public static void Main()
{
JsonWriterOptions writerOptions = new() { Indented = true, };

using MemoryStream stream = new();


using Utf8JsonWriter writer = new(stream, writerOptions);

writer.WriteStartObject();
writer.WriteStartArray("defaultJsonFormatting");
foreach (double number in new double[] { 50.4, 51 })
{
writer.WriteStartObject();
writer.WritePropertyName("value");
writer.WriteNumberValue(number);
writer.WriteEndObject();
}
writer.WriteEndArray();

writer.WriteStartArray("customJsonFormatting");
foreach (double result in new double[] { 50.4, 51 })
{
writer.WriteStartObject();
writer.WritePropertyName("value");
writer.WriteRawValue(
FormatNumberValue(result), skipInputValidation: true);
writer.WriteEndObject();
}
writer.WriteEndArray();

writer.WriteEndObject();
writer.Flush();

string json = Encoding.UTF8.GetString(stream.ToArray());


Console.WriteLine(json);
}
static string FormatNumberValue(double numberValue)
{
return numberValue == Convert.ToInt32(numberValue) ?
numberValue.ToString() + ".0" : numberValue.ToString();
}
}
// output:
//{
// "defaultJsonFormatting": [
// {
// "value": 50.4
// },
// {
// "value": 51
// }
// ],
// "customJsonFormatting": [
// {
// "value": 50.4
// },
// {
// "value": 51.0
// }
// ]
//}
Personalizar codificação de caracteres
A configuração StringEscapeHandling de JsonTextWriter oferece opções para escapar
de todos os caracteres não ASCII ou caracteres HTML. Por padrão, Utf8JsonWriter
escapa todos os caracteres não ASCII e HTML. Esse escape é feita por razões de
segurança detalhadas. Para especificar uma política de escape diferente, crie
JavaScriptEncoder e defina JsonWriterOptions.Encoder. Para obter mais informações,
consulte Personalizar a codificação de caracteres.

Grave valores nulos


Para gravar valores nulos usando Utf8JsonWriter , chame:

WriteNull para gravar um par de chaves-valor com nulo como o valor.


WriteNullValue para escrever null como um elemento de uma matriz JSON.

Para uma propriedade de cadeia de caracteres, se a cadeia de caracteres for nula,


WriteString e WriteStringValue for equivalente a WriteNull e WriteNullValue .

Gravar valores de Timespan, Uri ou char


Para gravar valores Timespan , Uri ou char , formatá-los como cadeias de caracteres
(chamando ToString() , por exemplo) e chamar WriteStringValue.

Confira também
Como usar Utf8JsonReader no System.Text.Json

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
be found on GitHub, where you source. Provide feedback here.
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como usar Utf8JsonReader no
System.Text.Json
Artigo • 26/06/2023

Este artigo mostra como você pode usar o tipo Utf8JsonReader para criar analisadores e
desserializadores personalizados.

Utf8JsonReader é um leitor de alto desempenho, baixa alocação e somente para


encaminhamento para texto JSON codificado em UTF-8, lido de um ReadOnlySpan<byte>
ou ReadOnlySequence<byte> . O Utf8JsonReader é um tipo de baixo nível que pode ser
usado para criar analisadores e desserializadores personalizados. Os métodos
JsonSerializer.Deserialize usam Utf8JsonReader .

O Utf8JsonReader não pode ser usado diretamente a partir do código do Visual


Basic. Para saber mais, confira Suporte para Visual Basic.

O exemplo a seguir mostra como usar a classe Utf8JsonReader:

C#

var options = new JsonReaderOptions


{
AllowTrailingCommas = true,
CommentHandling = JsonCommentHandling.Skip
};
var reader = new Utf8JsonReader(jsonUtf8Bytes, options);

while (reader.Read())
{
Console.Write(reader.TokenType);

switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
case JsonTokenType.String:
{
string? text = reader.GetString();
Console.Write(" ");
Console.Write(text);
break;
}

case JsonTokenType.Number:
{
int intValue = reader.GetInt32();
Console.Write(" ");
Console.Write(intValue);
break;
}

// Other token types elided for brevity


}
Console.WriteLine();
}

O código anterior pressupõe que a variável jsonUtf8 seja uma matriz de bytes que
contém JSON válido, codificado como UTF-8.

Filtrar dados usando Utf8JsonReader


O exemplo a seguir mostra como ler um arquivo de forma síncrona e pesquisar um
valor.

C#

using System.Text;
using System.Text.Json;

namespace SystemTextJsonSamples
{
public class Utf8ReaderFromFile
{
private static readonly byte[] s_nameUtf8 =
Encoding.UTF8.GetBytes("name");
private static ReadOnlySpan<byte> Utf8Bom => new byte[] { 0xEF,
0xBB, 0xBF };

public static void Run()


{
// ReadAllBytes if the file encoding is UTF-8:
string fileName = "UniversitiesUtf8.json";
ReadOnlySpan<byte> jsonReadOnlySpan =
File.ReadAllBytes(fileName);

// Read past the UTF-8 BOM bytes if a BOM exists.


if (jsonReadOnlySpan.StartsWith(Utf8Bom))
{
jsonReadOnlySpan = jsonReadOnlySpan.Slice(Utf8Bom.Length);
}

// Or read as UTF-16 and transcode to UTF-8 to convert to a


ReadOnlySpan<byte>
//string fileName = "Universities.json";
//string jsonString = File.ReadAllText(fileName);
//ReadOnlySpan<byte> jsonReadOnlySpan =
Encoding.UTF8.GetBytes(jsonString);
int count = 0;
int total = 0;

var reader = new Utf8JsonReader(jsonReadOnlySpan);

while (reader.Read())
{
JsonTokenType tokenType = reader.TokenType;

switch (tokenType)
{
case JsonTokenType.StartObject:
total++;
break;
case JsonTokenType.PropertyName:
if (reader.ValueTextEquals(s_nameUtf8))
{
// Assume valid JSON, known schema
reader.Read();
if (reader.GetString()!.EndsWith("University"))
{
count++;
}
}
break;
}
}
Console.WriteLine($"{count} out of {total} have names that end
with 'University'");
}
}
}

Para obter uma versão assíncrona deste exemplo, consulte o projeto JSON de exemplos
do .NET .

O código anterior:

Pressupõe que o JSON contém uma matriz de objetos e cada objeto pode conter
uma propriedade "name" da cadeia de caracteres de tipo.

Conta objetos e valores de propriedade "name" que terminam com "University".

Pressupõe que o arquivo seja codificado como UTF-16 e transcodifica-o para UTF-
8. Um arquivo codificado como UTF-8 pode ser lido diretamente em um
ReadOnlySpan<byte> , usando o seguinte código:

C#

ReadOnlySpan<byte> jsonReadOnlySpan = File.ReadAllBytes(fileName);


Se o arquivo contiver uma marca de ordem de bytes UTF-8 (BOM), remova-a antes
de passar os bytes para o Utf8JsonReader , já que o leitor espera texto. Caso
contrário, o BOM é considerado JSON inválido e o leitor gera uma exceção.

Aqui está um exemplo JSON que o código anterior pode ler. A mensagem de resumo
resultante é "2 em 4 têm nomes que terminam com 'University'":

JSON

[
{
"web_pages": [ "https://contoso.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "contoso.edu" ],
"name": "Contoso Community College"
},
{
"web_pages": [ "http://fabrikam.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "fabrikam.edu" ],
"name": "Fabrikam Community College"
},
{
"web_pages": [ "http://www.contosouniversity.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "contosouniversity.edu" ],
"name": "Contoso University"
},
{
"web_pages": [ "http://www.fabrikamuniversity.edu/" ],
"alpha_two_code": "US",
"state-province": null,
"country": "United States",
"domains": [ "fabrikamuniversity.edu" ],
"name": "Fabrikam University"
}
]

Ler de um fluxo usando Utf8JsonReader


Ao ler um arquivo grande (um gigabyte ou mais em tamanho, por exemplo), convém
evitar a necessidade de carregar todo o arquivo na memória de uma vez. Para este
cenário, é possível usar FileStream.
Ao usar o Utf8JsonReader para ler de um fluxo, as seguintes regras se aplicam:

O buffer que contém um conteúdo JSON parcial deve ser pelo menos tão grande
quanto o maior token JSON dentro dele para que o leitor possa avançar.
O buffer deve ser pelo menos tão grande quanto a maior sequência de espaço em
branco dentro do JSON.
O leitor não controla os dados que leu até ler completamente o próximo
TokenType no conteúdo JSON. Portanto, quando há bytes restantes no buffer, você
precisa passá-los para o leitor novamente. Você pode usar BytesConsumed para
determinar quantos bytes sobraram.

O código a seguir ilustra como ler de um fluxo. O exemplo mostra um MemoryStream.


Código semelhante funcionará com um FileStream, exceto quando o FileStream
contém um BOM UTF-8 no início. Nesse caso, você precisa remover esses três bytes do
buffer antes de passar os bytes restantes para Utf8JsonReader . Caso contrário, o leitor
lançaria uma exceção, já que o BOM não é considerado uma parte válida do JSON.

O código de exemplo começa com um buffer de 4KB e dobra o tamanho do buffer


sempre que ele descobre que o tamanho não é grande o suficiente para se ajustar a um
token JSON completo, o que é necessário para que o leitor faça progresso no conteúdo
JSON. O exemplo JSON fornecido no snippet dispara um aumento de tamanho de
buffer somente se você definir um tamanho de buffer inicial muito pequeno, por
exemplo, 10 bytes. Se você definir o tamanho inicial do buffer como 10, as instruções
Console.WriteLine ilustram a causa e o efeito dos aumentos de tamanho do buffer. No
tamanho inicial do buffer de 4KB, todo o JSON de exemplo é mostrado por cada
Console.WriteLine , e o tamanho do buffer nunca precisa ser aumentado.

C#

using System.Text;
using System.Text.Json;

namespace SystemTextJsonSamples
{
public class Utf8ReaderPartialRead
{
public static void Run()
{
var jsonString = @"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""Temperature"": 25,
""TemperatureRanges"": {
""Cold"": { ""High"": 20, ""Low"": -10 },
""Hot"": { ""High"": 60, ""Low"": 20 }
},
""Summary"": ""Hot"",
}";

byte[] bytes = Encoding.UTF8.GetBytes(jsonString);


var stream = new MemoryStream(bytes);

var buffer = new byte[4096];

// Fill the buffer.


// For this snippet, we're assuming the stream is open and has
data.
// If it might be closed or empty, check if the return value is
0.
stream.Read(buffer);

// We set isFinalBlock to false since we expect more data in a


subsequent read from the stream.
var reader = new Utf8JsonReader(buffer, isFinalBlock: false,
state: default);
Console.WriteLine($"String in buffer is:
{Encoding.UTF8.GetString(buffer)}");

// Search for "Summary" property name


while (reader.TokenType != JsonTokenType.PropertyName ||
!reader.ValueTextEquals("Summary"))
{
if (!reader.Read())
{
// Not enough of the JSON is in the buffer to complete a
read.
GetMoreBytesFromStream(stream, ref buffer, ref reader);
}
}

// Found the "Summary" property name.


Console.WriteLine($"String in buffer is:
{Encoding.UTF8.GetString(buffer)}");
while (!reader.Read())
{
// Not enough of the JSON is in the buffer to complete a
read.
GetMoreBytesFromStream(stream, ref buffer, ref reader);
}
// Display value of Summary property, that is, "Hot".
Console.WriteLine($"Got property value: {reader.GetString()}");
}

private static void GetMoreBytesFromStream(


MemoryStream stream, ref byte[] buffer, ref Utf8JsonReader
reader)
{
int bytesRead;
if (reader.BytesConsumed < buffer.Length)
{
ReadOnlySpan<byte> leftover =
buffer.AsSpan((int)reader.BytesConsumed);
if (leftover.Length == buffer.Length)
{
Array.Resize(ref buffer, buffer.Length * 2);
Console.WriteLine($"Increased buffer size to
{buffer.Length}");
}

leftover.CopyTo(buffer);
bytesRead = stream.Read(buffer.AsSpan(leftover.Length));
}
else
{
bytesRead = stream.Read(buffer);
}
Console.WriteLine($"String in buffer is:
{Encoding.UTF8.GetString(buffer)}");
reader = new Utf8JsonReader(buffer, isFinalBlock: bytesRead ==
0, reader.CurrentState);
}
}
}

O exemplo anterior não define nenhum limite para o tamanho do buffer. Se o tamanho
do token for muito grande, o código poderá falhar com uma exceção
OutOfMemoryException. Isso pode acontecer se o JSON contiver um token com cerca
de 1 GB ou mais de tamanho, pois dobrar o tamanho de 1 GB resultará em um tamanho
muito grande para caber em um buffer int32 .

Limitações da estrutura ref


Como o tipo Utf8JsonReader é um ref struct, ele tem certas limitações. Por exemplo, ele
não pode ser armazenado como um campo em uma classe ou struct diferente de um ref
struct.

Para obter alto desempenho, esse tipo deve ser um ref struct , pois precisa armazenar
em cache o byte< ReadOnlySpan> de entrada, que é um ref struct. Além disso, o tipo
Utf8JsonReader é mutável, pois contém o estado. Portanto, passe-o por referência e

não por valor. Passá-lo por valor resultaria em uma cópia de struct e as alterações de
estado não seriam visíveis para o chamador.

Para obter mais informações sobre como usar structs de referência, confira Evitar
alocações.

Ler texto UTF-8


Para obter o melhor desempenho possível ao usar Utf8JsonReader , o conteúdo JSON de
gravação já codificado como texto UTF-8 em vez de como cadeias de caracteres UTF-16.
Para obter um exemplo de código, consulte Filtrar dados usando Utf8JsonReader.

Ler com ReadOnlySequence de vários


segmentos
Se sua entrada JSON for um ReadOnlySpan<byte>, cada elemento JSON poderá ser
acessado a partir da propriedade ValueSpan no leitor conforme você percorre o loop de
leitura. No entanto, se sua entrada for um ReadOnlySequence<byte> (que é o resultado
da leitura de PipeReader), alguns elementos JSON poderão abranger vários segmentos
do ReadOnlySequence<byte> objeto. Esses elementos não seriam acessíveis de ValueSpan
em um bloco de memória contíguo. Em vez disso, sempre que você tiver um
multissegmento ReadOnlySequence<byte> como entrada, sondar a propriedade
HasValueSequence no leitor para descobrir como acessar o elemento JSON atual. Aqui
está um padrão recomendado:

C#

while (reader.Read())
{
switch (reader.TokenType)
{
// ...
ReadOnlySpan<byte> jsonElement = reader.HasValueSequence ?
reader.ValueSequence.ToArray() :
reader.ValueSpan;
// ...
}
}

Usar ValueTextEquals para pesquisas de nome


de propriedade
Não use ValueSpan para fazer comparações byte por byte chamando SequenceEqual
para pesquisas de nome de propriedade. Em vez disso, chame ValueTextEquals porque
esse método desescala todos os caracteres que são escapados no JSON. Aqui está um
exemplo que mostra como pesquisar uma propriedade chamada "name":

C#
private static readonly byte[] s_nameUtf8 = Encoding.UTF8.GetBytes("name");

C#

while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.StartObject:
total++;
break;
case JsonTokenType.PropertyName:
if (reader.ValueTextEquals(s_nameUtf8))
{
count++;
}
break;
}
}

Ler valores nulos em tipos de valor anulável


As APIs internas de System.Text.Json retornam apenas tipos de valor não anuláveis. Por
exemplo, Utf8JsonReader.GetBoolean retorna bool . Ele gerará uma exceção se
encontrar Null no JSON. Os exemplos a seguir mostram duas maneiras de lidar com
nulos, uma retornando um tipo de valor anulável e outra retornando o valor padrão:

C#

public bool? ReadAsNullableBoolean()


{
_reader.Read();
if (_reader.TokenType == JsonTokenType.Null)
{
return null;
}
if (_reader.TokenType != JsonTokenType.True && _reader.TokenType !=
JsonTokenType.False)
{
throw new JsonException();
}
return _reader.GetBoolean();
}

C#
public bool ReadAsBoolean(bool defaultValue)
{
_reader.Read();
if (_reader.TokenType == JsonTokenType.Null)
{
return defaultValue;
}
if (_reader.TokenType != JsonTokenType.True && _reader.TokenType !=
JsonTokenType.False)
{
throw new JsonException();
}
return _reader.GetBoolean();
}

Ignorar filhos do token


Use o método Utf8JsonReader.Skip() para ignorar os filhos do token JSON atual. Se o
tipo de token for JsonTokenType.PropertyName, o leitor passará para o valor da
propriedade. O snippet de código a seguir mostra um exemplo de como usar
Utf8JsonReader.Skip() para mover o leitor para o valor de uma propriedade.

C#

var weatherForecast = new WeatherForecast


{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

byte[] jsonUtf8Bytes = JsonSerializer.SerializeToUtf8Bytes(weatherForecast);

var reader = new Utf8JsonReader(jsonUtf8Bytes);

int temp;
while (reader.Read())
{
switch (reader.TokenType)
{
case JsonTokenType.PropertyName:
{
if (reader.ValueTextEquals("TemperatureCelsius"))
{
reader.Skip();
temp = reader.GetInt32();

Console.WriteLine($"Temperature is {temp} degrees.");


}
continue;
}
default:
continue;
}
}

Consumir cadeias de caracteres JSON


decodificadas
A partir do .NET 7, você pode usar o método Utf8JsonReader.CopyString, em vez de
Utf8JsonReader.GetString() para consumir uma cadeia de caracteres JSON decodificada.
Ao contrário de GetString(), que sempre aloca uma nova cadeia de caracteres,
CopyString permite copiar a cadeia de caracteres sem escape para um buffer que você
possui. O snippet de código a seguir mostra um exemplo de consumo de uma cadeia de
caracteres UTF-16 usando CopyString.

C#

var reader = new Utf8JsonReader( /* jsonReadOnlySpan */ );

int valueLength = reader.HasValueSequence


? checked((int)reader.ValueSequence.Length)
: reader.ValueSpan.Length;

char[] buffer = ArrayPool<char>.Shared.Rent(valueLength);


int charsRead = reader.CopyString(buffer);
ReadOnlySpan<char> source = buffer.AsSpan(0, charsRead);

// Handle the unescaped JSON string.


ParseUnescapedString(source);
ArrayPool<char>.Shared.Return(buffer, clearArray: true);

void ParseUnescapedString(ReadOnlySpan<char> source)


{
// ...
}

APIs relacionadas
Para desserializar um tipo personalizado de uma instância Utf8JsonReader , chame
JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonSerializerOptions) ou
JsonSerializer.Deserialize<TValue>(Utf8JsonReader, JsonTypeInfo<TValue>). Para
obter um exemplo, confira Desserializar de UTF-8.
JsonNode e as classes que derivam dele fornecem a capacidade de criar um DOM
mutável. Você pode converter uma instância Utf8JsonReader em JsonNode
chamando JsonNode.Parse(Utf8JsonReader, Nullable<JsonNodeOptions>). O
snippet de código a seguir mostra um exemplo.

C#

using System.Text.Json;
using System.Text.Json.Nodes;

namespace Utf8ReaderToJsonNode
{
public class WeatherForecast
{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

public class Program


{
public static void Main()
{
var weatherForecast = new WeatherForecast
{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Summary = "Hot"
};

byte[] jsonUtf8Bytes =
JsonSerializer.SerializeToUtf8Bytes(weatherForecast);

var utf8Reader = new Utf8JsonReader(jsonUtf8Bytes);


JsonNode? node = JsonNode.Parse(ref utf8Reader);
Console.WriteLine(node);
}
}
}

JsonDocument fornece a capacidade de criar um DOM somente leitura usando


Utf8JsonReader . Chame o método JsonDocument.ParseValue(Utf8JsonReader) para

analisar um JsonDocument de uma instância Utf8JsonReader . Você pode acessar os


elementos JSON que compõem a carga por meio do tipo JsonElement. Por
exemplo, código que usa JsonDocument.ParseValue(Utf8JsonReader), confira
RoundtripDataTable.cs e o snippet de código em Desserializar tipos inferidos
para propriedades de objeto.
Você também pode analisar uma instância Utf8JsonReader para JsonElement, que
representa um valor JSON específico, chamando
JsonElement.ParseValue(Utf8JsonReader).

Confira também
Como usar Utf8JsonWriter no System.Text.Json
Suporte ao Visual Basic
Artigo • 28/02/2024

Partes de System.Text.Json usam structs ref, que não têm suporte no Visual Basic. Se
você tentar usar APIs de struct ref System.Text.Json com o Visual Basic, receberá erros
do compilador BC40000. A mensagem de erro indica que o problema é uma API
obsoleta, mas o problema real é a falta de suporte para o struct ref no compilador. As
seguintes partes de System.Text.Json não são utilizáveis do Visual Basic:

A Utf8JsonReader estrutura. Como o método JsonConverter<T>.Read usa um


parâmetro Utf8JsonReader , essa limitação significa que você não pode usar o
Visual Basic para gravar conversores personalizados. Uma solução alternativa para
isso é implementar conversores personalizados em um assembly de biblioteca C#
e fazer referência a esse assembly do seu projeto do VB. Isso pressupõe que tudo
o que você faz no Visual Basic é registrar os conversores no serializador. Você não
pode chamar os métodos Read dos conversores do código do Visual Basic.
Sobrecargas de outras APIs que incluem um tipo ReadOnlySpan<T>. A maioria dos
métodos inclui sobrecargas que usam String em vez de ReadOnlySpan .

Essas restrições estão em vigor porque os structs de ref não podem ser usados com
segurança sem suporte vocal, mesmo apenas ao "passar dados". Subverter esse erro
resultará em um código do Visual Basic que pode corromper a memória e não deve ser
feito.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Tipos de coleção compatíveis em
System.Text.Json
Artigo • 07/12/2023

Este artigo fornece uma visão geral de quais coleções têm suporte para serialização e
desserialização. System.Text.Json.JsonSerializer dá suporte a um tipo de coleção para
serialização se ele:

Deriva de IEnumerable ou IAsyncEnumerable<T>


Contém elementos serializáveis.

O serializador chama o método GetEnumerator() e grava os elementos.

A desserialização é mais complicada e não tem suporte para alguns tipos de coleção.

As seções a seguir são organizadas pelo namespace e mostram quais tipos têm suporte
para serialização e desserialização.

Namespace System.Array
ノ Expand table

Tipo Serialização Desserialização

Matrizes unidimensionais ✔️ ✔️

Matrizes multidimensionais ❌ ❌

Matrizes denteadas ✔️ ✔️

Namespace System.Collections
ノ Expand table

Tipo Serialização Desserialização

ArrayList ✔️ ✔️

BitArray ✔️ ❌

DictionaryEntry ✔️ ✔️
Tipo Serialização Desserialização

Hashtable ✔️ ✔️

ICollection ✔️ ✔️

IDictionary ✔️ ✔️

IEnumerable ✔️ ✔️

IList ✔️ ✔️

Queue ✔️ ✔️

SortedList ✔️ ✔️

Stack * ✔️ ✔️

* Consulte Suporte para ida e volta de Stack tipos.

Namespace System.Collections.Generic
ノ Expand table

Tipo Serialização Desserialização

Dictionary<TKey,TValue> * ✔️ ✔️

HashSet<T> ✔️ ✔️

IAsyncEnumerable<T> † ✔️ ✔️

ICollection<T> ✔️ ✔️

IDictionary<TKey,TValue> * ✔️ ✔️

IEnumerable<T> ✔️ ✔️

IList<T> ✔️ ✔️

IReadOnlyCollection<T> ✔️ ✔️

IReadOnlyDictionary<TKey,TValue> * ✔️ ✔️

IReadOnlyList<T> ✔️ ✔️

ISet<T> ✔️ ✔️

KeyValuePair<TKey,TValue> ✔️ ✔️
Tipo Serialização Desserialização

LinkedList<T> ✔️ ✔️

LinkedListNode<T> ✔️ ❌

List<T> ✔️ ✔️

Queue<T> ✔️ ✔️

SortedDictionary<TKey,TValue> * ✔️ ✔️

SortedList<TKey,TValue> * ✔️ ✔️

SortedSet<T> ✔️ ✔️

Stack<T> ‡ ✔️ ✔️

* Consulte tipos de chave com suporte.

† Consulte a seção a seguir sobre IAsyncEnumerable<T> .

‡ Consulte Suporte de ida e volta para Stack tipos.

IAsyncEnumerable<T>
Os exemplos a seguir usam fluxos como uma representação de qualquer fonte
assíncrona de dados. A origem pode ser arquivos em um computador local ou
resultados de uma consulta de banco de dados ou chamada à API do serviço Web.

Serialização de fluxo

System.Text.Json dá suporte à serialização de valores IAsyncEnumerable<T> como

matrizes JSON, conforme mostrado no exemplo a seguir:

C#

using System.Text.Json;

namespace IAsyncEnumerableSerialize;

public class Program


{
public static async Task Main()
{
using Stream stream = Console.OpenStandardOutput();
var data = new { Data = PrintNumbers(3) };
await JsonSerializer.SerializeAsync(stream, data);
}

static async IAsyncEnumerable<int> PrintNumbers(int n)


{
for (int i = 0; i < n; i++)
{
await Task.Delay(1000);
yield return i;
}
}
}
// output:
// {"Data":[0,1,2]}

IAsyncEnumerable<T> os valores têm suporte apenas pelos métodos de serialização

assíncronos, como JsonSerializer.SerializeAsync.

Desserialização de fluxo
O método DeserializeAsyncEnumerable dá suporte à desserialização de streaming,
conforme mostrado no exemplo a seguir:

C#

using System.Text;
using System.Text.Json;

namespace IAsyncEnumerableDeserialize;

public class Program


{
public static async Task Main()
{
using var stream = new MemoryStream(Encoding.UTF8.GetBytes("
[0,1,2,3,4]"));
await foreach (int item in
JsonSerializer.DeserializeAsyncEnumerable<int>(stream))
{
Console.WriteLine(item);
}
}
}
// output:
//0
//1
//2
//3
//4
O método DeserializeAsyncEnumerable só dá suporte à leitura de matrizes JSON no
nível raiz.

O método DeserializeAsync dá suporte a IAsyncEnumerable<T> , mas sua assinatura não


permite streaming. Ele retorna o resultado final como um único valor, conforme
mostrado no exemplo a seguir.

C#

using System.Text;
using System.Text.Json;

namespace IAsyncEnumerableDeserializeNonStreaming;

public class MyPoco


{
public IAsyncEnumerable<int>? Data { get; set; }
}

public class Program


{
public static async Task Main()
{
using var stream = new MemoryStream(Encoding.UTF8.GetBytes(@"
{""Data"":[0,1,2,3,4]}"));
MyPoco? result = await JsonSerializer.DeserializeAsync<MyPoco>
(stream)!;
await foreach (int item in result!.Data!)
{
Console.WriteLine(item);
}
}
}
// output:
//0
//1
//2
//3
//4

Neste exemplo, o desserializador armazena em buffer todo o conteúdo


IAsyncEnumerable<T> na memória antes de retornar o objeto desserializado. Esse

comportamento é necessário porque o desserializador precisa ler todo o conteúdo


JSON antes de retornar um resultado.

Namespace System.Collections.Immutable
ノ Expand table

Tipo Serialização Desserialização

IImmutableDictionary<TKey,TValue> † ✔️ ✔️

IImmutableList<T> ✔️ ✔️

IImmutableQueue<T> ✔️ ✔️

IImmutableSet<T> ✔️ ✔️

IImmutableStack<T> * ✔️ ✔️

ImmutableArray<T> ✔️ ✔️

ImmutableDictionary<TKey,TValue> † ✔️ ✔️

ImmutableHashSet<T> ✔️ ✔️

ImmutableQueue<T> ✔️ ✔️

ImmutableSortedDictionary<TKey,TValue> † ✔️ ✔️

ImmutableSortedSet<T> ✔️ ✔️

ImmutableStack<T> * ✔️ ✔️

* Consulte Suporte para ida e volta de Stack tipos.

† Consulte Tipos de chave com suporte.

Namespace System.Collections.Specialized
ノ Expand table

Tipo Serialização Desserialização

BitVector32 ✔️ ❌*

HybridDictionary ✔️ ✔️

IOrderedDictionary ✔️ ❌

ListDictionary ✔️ ✔️

NameValueCollection ✔️ ❌

StringCollection ✔️ ❌
Tipo Serialização Desserialização

StringDictionary ✔️ ❌

* Quando BitVector32 está desserializada, a propriedade Data é ignorada porque não


tem um setter público. Nenhuma exceção é gerada.

Namespace System.Collections.Concurrent
ノ Expand table

Tipo Serialização Desserialização

BlockingCollection<T> ✔️ ❌

ConcurrentBag<T> ✔️ ❌

ConcurrentDictionary<TKey,TValue> † ✔️ ✔️

ConcurrentQueue<T> ✔️ ✔️

ConcurrentStack<T> * ✔️ ✔️

* Consulte Suporte para ida e volta de Stack tipos.

† Consulte Tipos de chave com suporte.

Namespace System.Collections.ObjectModel
ノ Expand table

Tipo Serialização Desserialização

Collection<T> ✔️ ✔️

Cadeia de caracteres KeyedCollection<, TValue> * ✔️ ❌

ObservableCollection<T> ✔️ ✔️

ReadOnlyCollection<T> ✔️ ❌

ReadOnlyDictionary<TKey,TValue> ✔️ ❌

ReadOnlyObservableCollection<T> ✔️ ❌

* Não há suporte para chaves não string .


Coleções personalizadas
Qualquer tipo de coleção que não esteja em um dos namespaces anteriores é
considerado uma coleção personalizada. Esses tipos incluem tipos definidos pelo
usuário e tipos definidos por ASP.NET Core. Por exemplo,
Microsoft.Extensions.Primitives está nesse grupo.

Todas as coleções personalizadas (tudo o que deriva de IEnumerable ) têm suporte para
serialização, desde que seus tipos de elementos sejam compatíveis.

Coleções personalizadas com suporte à desserialização


Uma coleção personalizada terá suporte à desserialização se ela:

Não for uma interface ou abstrata.

Tiver um construtor sem parâmetros.

Contiver tipos de elementos compatíveis com JsonSerializer.

Implementar ou herdar uma ou mais das seguintes interfaces ou classes:


ConcurrentQueue<T>
ConcurrentStack<T> *
ICollection<T>
IDictionary
IDictionary<TKey,TValue> †
IList
IList<T>
Queue
Queue<T>
Stack *
Stack<T> *

* Consulte Suporte para ida e volta de Stack tipos.

† Consulte Tipos de chave com suporte.

Coleções personalizadas com problemas conhecidos


Há problemas conhecidos com as seguintes coleções personalizadas:

ExpandoObject: consulte dotnet/runtime#29690 .


DynamicObject: consulte dotnet/runtime#1808 .
DataTable: consulte dotnet/docs#21366 .
Microsoft.AspNetCore.Http.FormFile: consulte dotnet/runtime#1559 .
Microsoft.AspNetCore.Http.IFormCollection: consulte dotnet/runtime#1559 .

Para obter mais informações sobre problemas conhecidos, consulte problemas em


aberto em System.Text.Json .

Tipos de chave com suporte


Os tipos com suporte para as chaves Dictionary e SortedList tipos incluem o seguinte:

Boolean
Byte

DateTime

DateTimeOffset
Decimal

Double
Enum

Guid
Int16

Int32

Int64
Object (Somente na serialização e se o tipo de runtime for um dos tipos com

suporte nesta lista.)


SByte

Single

String
UInt16

UInt32
UInt64

Namespace System.Data
Não há conversores internos para DataSet,DataTable e tipos relacionados no namespace
System.Data. Desserializar esses tipos a partir de entrada não confiável não é seguro,
conforme explicado nas diretrizes de segurança. No entanto, você pode escrever um
conversor personalizado para dar suporte a esses tipos. Para obter um código de
conversor personalizado de exemplo que serializa e desserializa um DataTable , consulte
RoundtripDataTable.cs .
Confira também
Preencher propriedades inicializadas
Visão geral de System.Text.Json
Referência da API System.Text.Json
System.Text.JsonReferência da API .Serialization

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Reflexão x geração de código-fonte em
System.Text.Json
Artigo • 12/11/2023

Este artigo explica as diferenças entre a reflexão e a geração de código-fonte no que diz
respeito à serialização do System.Text.Json . Também fornece diretrizes sobre como
escolher a melhor abordagem para a sua conjuntura.

Coleção de metadados
Para serializar ou desserializar um tipo, JsonSerializer precisa de informações sobre
como acessar os membros do tipo. JsonSerializer precisa das seguintes informações:

Como acessar getters e campos de propriedade para serialização.


Como acessar um construtor, setters de propriedades e campos para
desserialização.
Informações sobre quais atributos foram usados para customizar a serialização ou
desserialização.
Configuração de tempo de execução de JsonSerializerOptions.

Essas informações são chamadas de metadados.

Reflexão
Por padrão, JsonSerializer coleta metadados em tempo de execução usando reflexão.
Sempre que JsonSerializer tiver que serializar ou desserializar um tipo pela primeira
vez, ele coleta e armazena em cache esses metadados. O processo de coleta de
metadados leva tempo e usa memória.

Geração de origem
Como alternativa, System.Text.Json pode usar o recurso de geração de origem C# para
melhorar o desempenho, reduzir o uso de memória privada e facilitar o corte do
assembly, o que reduz o tamanho do aplicativo. Além disso, determinadas APIs de
reflexão não podem ser usadas em aplicativos nativos de AOT e, portanto, você precisa
usar geração de código-fonte para esses aplicativos.

A geração de código-fonte pode ser usada em dois modos:


Modo baseado em metadados

Durante a compilação, o System.Text.Json coleta as informações necessárias para


a serialização e gera arquivos de código-fonte que preenchem os metadados de
contrato JSON para os tipos solicitados.

Modo serialização-otimização (expressa)

Os recursos de JsonSerializer que personalizam a saída da serialização, como


políticas de nomenclatura e preservação de referências, implicam uma sobrecarga
de desempenho. No modo serialização-otimização, o System.Text.Json gera um
código de serialização otimizado que usa o Utf8JsonWriter diretamente. Esse
código expresso ou otimizado aumenta a taxa de transferência da serialização.

A desserialização expressa não está disponível no momento. Para obter mais


informações, confira problema de dotnet/runtime 55043 .

A geração de código-fonte para o System.Text.Json requer o C# versão 9.0 ou


posterior.

Comparação de recursos
Escolha os modos de reflexão ou geração de código-fonte com base nos seguintes
benefícios que cada um oferece:

Benefício Reflexão Geração de origem Geração de origem


(Modo baseado em (Modo serialização-
metadados) otimização)

Mais simples para codificar. ✔️ ❌ ❌

Mais simples para depurar. ❌ ❌ ✔️

Suporta acessadores não públicos. ✔️ ❌ ❌

Dá suporte às propriedades exigidas. ✔️ ❌ ❌

Suporta propriedades somente de ✔️ ❌ ❌


inicialização.

Reduz o tempo de inicialização. ❌ ✔️ ✔️

Reduz o uso de memória privada. ❌ ✔️ ✔️

Elimina a reflexão em tempo de ❌ ✔️ ✔️


execução.
Benefício Reflexão Geração de origem Geração de origem
(Modo baseado em (Modo serialização-
metadados) otimização)

Facilita a redução do tamanho do ❌ ✔️ ✔️


aplicativo com segurança de corte.

Aumenta a taxa de transferência de ❌ ❌ ✔️


serialização.

6 Colaborar conosco no .NET feedback


GitHub The .NET documentation is open
A fonte deste conteúdo pode source. Provide feedback here.
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Modos de geração de origem em
System.Text.Json
Artigo • 30/11/2023

A geração de origem pode ser usada em dois modos: otimização baseada em


metadados e serialização. Este artigo descreve os diferentes modos.

Para obter informações sobre como usar os modos de geração de origem, consulte
Como usar a geração de origem em System.Text.Json.

Modo baseado em metadados


Você pode usar a geração de origem para mover o processo de coleta de metadados do
tempo de execução para o tempo de compilação. Durante a compilação, os metadados
são coletados e os arquivos de código-fonte são gerados. Os arquivos de código-fonte
gerados são compilados automaticamente como parte integrante do aplicativo. Essa
técnica elimina a coleta de metadados em tempo de execução, o que melhora o
desempenho da serialização e desserialização.

As melhorias de desempenho fornecidas pela geração de fonte podem ser substanciais.


Por exemplo, os resultados do teste mostraram uma redução de até 40% ou mais no
tempo de inicialização, redução da memória privada, aumento da taxa de transferência
(no modo de otimização de serialização) e redução do tamanho do aplicativo.

Problemas conhecidos
Somente propriedades e campos public têm suporte por padrão em qualquer modo de
serialização. No entanto, o modo de reflexão dá suporte para uso de membros private
e acessadores private , mas o modo de geração de fonte não. Por exemplo, se você
aplicar o atributo JsonInclude a uma propriedade private ou a uma propriedade que
tenha um setter ou getter private , irá serializar no modo de reflexão. O modo de
geração de fonte dá suporte apenas a membros public ou internal e acessadores
public ou internal de propriedades public . Se você definir [JsonInclude] em

membros ou acessadores private e escolher o modo de geração de fonte, uma


NotSupportedException será lançada em tempo de execução.

Para obter informações sobre outros problemas conhecidos com geração de origem,
consulte os problemas do GitHub rotulados como "gerador de origem" no repositório
dotnet/runtime.
Modo serialização-otimização (caminho rápido)
JsonSerializer possui muitos recursos que personalizam a saída da serialização, como

políticas de nomenclatura e preservação de referências. O suporte para todos esses


recursos causa alguma sobrecarga de desempenho. A geração de origem pode
melhorar o desempenho de serialização gerando código otimizado que usa
Utf8JsonWriter diretamente.

O código otimizado não oferece suporte a todos os recursos de serialização compatíveis


com JsonSerializer . O serializador detecta se o código otimizado pode ser usado e
retorna ao código de serialização padrão se as opções sem suporte forem especificadas.
Por exemplo, JsonNumberHandling.AllowReadingFromString não é aplicável à gravação,
portanto, especificar essa opção não causa um fallback para o código padrão.

A tabela a seguir mostra para quais opções em JsonSerializerOptions há suporte para


serialização de caminho rápido:

Opção de serialização Com suporte para caminho rápido

AllowTrailingCommas ✔️

Converters ❌

DefaultBufferSize ✔️

DefaultIgnoreCondition ✔️

DictionaryKeyPolicy ❌

Encoder ❌

IgnoreNullValues ❌

IgnoreReadOnlyFields ✔️

IgnoreReadOnlyProperties ✔️

IncludeFields ✔️

MaxDepth ✔️

NumberHandling ❌

PropertyNamingPolicy ✔️

ReferenceHandler ❌

TypeInfoResolver ✔️
Opção de serialização Com suporte para caminho rápido

WriteIndented ✔️

(Não há suporte para as seguintes opções porque elas se aplicam apenas à


desserialização: PropertyNameCaseInsensitive, ReadCommentHandling e
UnknownTypeHandling.)

A tabela a seguir mostra quais atributos são compatíveis com a serialização de caminho
rápido:

Atributo Com suporte para caminho rápido

JsonConstructorAttribute ❌

JsonConverterAttribute ❌

JsonDerivedTypeAttribute ✔️

JsonExtensionDataAttribute ❌

JsonIgnoreAttribute ✔️

JsonIncludeAttribute ✔️

JsonNumberHandlingAttribute ❌

JsonPolymorphicAttribute ✔️

JsonPropertyNameAttribute ✔️

JsonPropertyOrderAttribute ❌

JsonRequiredAttribute ✔️

Se uma opção ou atributo sem suporte for especificado para um tipo, o serializador
retornará ao modo de metadados, supondo que o gerador de origem tenha sido
configurado para gerar metadados. Nesse caso, o código otimizado não é usado ao
serializar esse tipo, mas pode ser usado para outros tipos. Portanto, é importante fazer
testes de desempenho com suas opções e cargas de trabalho para determinar quanto
benefício você pode realmente obter do modo de otimização de serialização. Além
disso, a capacidade de retornar ao código JsonSerializer requer o modo de coleta de
metadados. Se você selecionar apenas o modo de otimização de serialização, a
serialização poderá falhar para tipos ou opções que precisam retornar ao código
JsonSerializer .
Confira também
Serialização e desserialização de JSON em .NET - visão geral
Como usar a biblioteca

6 Colaborar conosco no .NET feedback


GitHub .NET is an open source project.
A fonte deste conteúdo pode Select a link to provide feedback:
ser encontrada no GitHub, onde
você também pode criar e  Abrir um problema de
revisar problemas e solicitações documentação
de pull. Para obter mais
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores. produto
Como usar a geração de origem em
System.Text.Json
Artigo • 18/12/2023

A geração de fonte no System.Text.Json está disponível no .NET 6 e em versões


posteriores. Quando usado em um aplicativo, a versão do idioma do aplicativo deve ser
C# 9.0 ou posterior. Este artigo mostra como usar a serialização baseada na geração de
origem em seus aplicativos.

Para obter informações sobre os diferentes modos de geração de origem, confira os


Modos de geração de origem.

Usar padrões de geração de origem


Para usar a geração de origem com todos os padrões (ambos os modos, opções
padrão):

1. Crie uma classe parcial que deriva de JsonSerializerContext.

2. Especifique o tipo para serializar ou desserializar aplicando


JsonSerializableAttribute à classe de contexto.

3. Chame um método JsonSerializer que:

Usa uma instância JsonTypeInfo<T> ou


Usa uma instância JsonSerializerContext ou
Usa uma instância JsonSerializerOptions e você definiu sua propriedade
JsonSerializerOptions.TypeInfoResolver como a propriedade Default do tipo
de contexto (somente .NET 7 e versão posterior).

Por padrão, ambos os modos de geração de origem são usados se você não especificar
um. Para obter informações sobre como especificar o modo a ser usado, consulte
Especificar modo de geração de origem posteriormente neste artigo.

Aqui está o tipo que é usado nos exemplos a seguir:

C#

public class WeatherForecast


{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

Aqui está a classe de contexto configurada para gerar a fonte para a classe
WeatherForecast anterior:

C#

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

Os tipos de membros WeatherForecast não precisam ser especificados explicitamente


com atributos [JsonSerializable] . Membros declarados como object são uma exceção
a esta regra. O tipo de runtime para um membro declarado como object precisa ser
especificado. Por exemplo, suponha que você tenha a seguinte classe:

C#

public class WeatherForecast


{
public object? Data { get; set; }
public List<object>? DataList { get; set; }
}

E você sabe que em runtime pode ter objetos boolean e int :

C#

WeatherForecast wf = new() { Data = true, DataList = new List<object> {


true, 1 } };

Então boolean e int devem ser declarados como [JsonSerializable] :

C#

[JsonSerializable(typeof(WeatherForecast))]
[JsonSerializable(typeof(bool))]
[JsonSerializable(typeof(int))]
public partial class WeatherForecastContext : JsonSerializerContext
{
}
Para especificar a geração de origem para uma coleção, use [JsonSerializable] com o
tipo de coleção. Por exemplo: [JsonSerializable(typeof(List<WeatherForecast>))] .

JsonSerializer métodos que usam geração de origem

Nos exemplos a seguir, a propriedade estática Default do tipo de contexto fornece uma
instância do tipo de contexto com opções padrão. A instância de contexto fornece uma
propriedade WeatherForecast que retorna uma instância
JsonTypeInfo<WeatherForecast> . Você pode especificar um nome diferente para esta

propriedade usando a propriedade TypeInfoPropertyName do atributo


[JsonSerializable] .

Exemplos de serialização
Usando JsonTypeInfo<T>:

C#

jsonString = JsonSerializer.Serialize(
weatherForecast!, SourceGenerationContext.Default.WeatherForecast);

Usando JsonSerializerContext:

C#

jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast),
SourceGenerationContext.Default);

Usando JsonSerializerOptions:

C#

sourceGenOptions = new JsonSerializerOptions


{
TypeInfoResolver = SourceGenerationContext.Default
};

jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), sourceGenOptions);

Exemplos de desserialização
Usando JsonTypeInfo<T>:

C#

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString, SourceGenerationContext.Default.WeatherForecast);

Usando JsonSerializerContext:

C#

weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), SourceGenerationContext.Default)
as WeatherForecast;

Usando JsonSerializerOptions:

C#

var sourceGenOptions = new JsonSerializerOptions


{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), sourceGenOptions)
as WeatherForecast;

Exemplo de programa completo


Aqui estão os exemplos anteriores em um programa completo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace BothModesNoOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SourceGenerationContext : JsonSerializerContext
{
}

public class Program


{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot""
}
";
WeatherForecast? weatherForecast;

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString,
SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM

weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast),
SourceGenerationContext.Default)
as WeatherForecast;
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM

var sourceGenOptions = new JsonSerializerOptions


{
TypeInfoResolver = SourceGenerationContext.Default
};
weatherForecast = JsonSerializer.Deserialize(
jsonString, typeof(WeatherForecast), sourceGenOptions)
as WeatherForecast;
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM

jsonString = JsonSerializer.Serialize(
weatherForecast!,
SourceGenerationContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}

jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast),
SourceGenerationContext.Default);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}

sourceGenOptions = new JsonSerializerOptions


{
TypeInfoResolver = SourceGenerationContext.Default
};

jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast), sourceGenOptions);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
}
}
}

Especifique o modo de geração de origem


Você pode especificar o modo baseado em metadados ou o modo de otimização de
serialização para um contexto inteiro, que pode incluir vários tipos. Ou você pode
especificar o modo para um tipo individual. Se você fizer as duas coisas, a especificação
de modo para um tipo prevalece.

Para um contexto inteiro, use a propriedade


JsonSourceGenerationOptionsAttribute.GenerationMode.
Para um tipo individual, use a propriedade
JsonSerializableAttribute.GenerationMode.

Exemplo do modo serialização-otimização (caminho


rápido)
Para um contexto inteiro:

C#

[JsonSourceGenerationOptions(GenerationMode =
JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializeOnlyContext : JsonSerializerContext
{
}

Para um tipo individual:


C#

[JsonSerializable(typeof(WeatherForecast), GenerationMode =
JsonSourceGenerationMode.Serialization)]
internal partial class SerializeOnlyWeatherForecastOnlyContext :
JsonSerializerContext
{
}

Exemplo de programa completo

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SerializeOnlyNoOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

[JsonSourceGenerationOptions(GenerationMode =
JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializeOnlyContext : JsonSerializerContext
{
}

[JsonSerializable(typeof(WeatherForecast), GenerationMode =
JsonSourceGenerationMode.Serialization)]
internal partial class SerializeOnlyWeatherForecastOnlyContext :
JsonSerializerContext
{
}

public class Program


{
public static void Main()
{
string jsonString;
WeatherForecast weatherForecast = new()
{ Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25, Summary = "Hot" };

// Use context that selects Serialization mode only for


WeatherForecast.
jsonString = JsonSerializer.Serialize(weatherForecast,

SerializeOnlyWeatherForecastOnlyContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}

// Use a context that selects Serialization mode.


jsonString = JsonSerializer.Serialize(weatherForecast,
SerializeOnlyContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}
}
}
}

Exemplo do modo baseado em metadados


Para um contexto inteiro:

C#

[JsonSourceGenerationOptions(GenerationMode =
JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class MetadataOnlyContext : JsonSerializerContext
{
}

C#

jsonString = JsonSerializer.Serialize(
weatherForecast!, MetadataOnlyContext.Default.WeatherForecast);

C#

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString, MetadataOnlyContext.Default.WeatherForecast);

Para um tipo individual:

C#

[JsonSerializable(typeof(WeatherForecast), GenerationMode =
JsonSourceGenerationMode.Metadata)]
internal partial class MetadataOnlyWeatherForecastOnlyContext :
JsonSerializerContext
{
}

C#

jsonString = JsonSerializer.Serialize(
weatherForecast!,
MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);

C#

weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(
jsonString,
MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);

Exemplo de programa completo

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace MetadataOnlyNoOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

[JsonSerializable(typeof(WeatherForecast), GenerationMode =
JsonSourceGenerationMode.Metadata)]
internal partial class MetadataOnlyWeatherForecastOnlyContext :
JsonSerializerContext
{
}

[JsonSourceGenerationOptions(GenerationMode =
JsonSourceGenerationMode.Metadata)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class MetadataOnlyContext : JsonSerializerContext
{
}

public class Program


{
public static void Main()
{
string jsonString =
@"{
""Date"": ""2019-08-01T00:00:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot""
}
";
WeatherForecast? weatherForecast;

// Deserialize with context that selects metadata mode only


for WeatherForecast only.
weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(
jsonString,
MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM

// Serialize with context that selects metadata mode only


for WeatherForecast only.
jsonString = JsonSerializer.Serialize(
weatherForecast!,

MetadataOnlyWeatherForecastOnlyContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":25,"Summary":"Hot"}

// Deserialize with context that selects metadata mode


only.
weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(
jsonString,
MetadataOnlyContext.Default.WeatherForecast);
Console.WriteLine($"Date={weatherForecast?.Date}");
// output:
//Date=8/1/2019 12:00:00 AM

// Serialize with context that selects metadata mode only.


jsonString = JsonSerializer.Serialize(
weatherForecast!,
MetadataOnlyContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{"Date":"2019-08-
01T00:00:00","TemperatureCelsius":0,"Summary":"Hot"}
}
}
}

Suporte à geração de origem no ASP.NET Core


Em aplicativos Blazor, use sobrecargas e métodos de extensão
HttpClientJsonExtensions.GetFromJsonAsync e
HttpClientJsonExtensions.PostAsJsonAsync que usam um contexto de geração de
origem ou TypeInfo<TValue> .

A partir do .NET 8, você também pode usar sobrecargas de métodos de extensão


HttpClientJsonExtensions.GetFromJsonAsAsyncEnumerable que aceitam um contexto de
geração de origem ou TypeInfo<TValue> .

Em aplicativos do Razor Pages, MVC, SignalR e de API Web, use a propriedade


JsonSerializerOptions.TypeInfoResolver para especificar o contexto.

C#

[JsonSerializable(typeof(WeatherForecast[]))]
internal partial class MyJsonContext : JsonSerializerContext { }

C#

var serializerOptions = new JsonSerializerOptions


{
TypeInfoResolver = MyJsonContext.Default;
};

services.AddControllers().AddJsonOptions(
static options =>

options.JsonSerializerOptions.TypeInfoResolverChain.Add(MyJsonContext.Defaul
t));

7 Observação

JsonSourceGenerationMode.Serialization ou serialização de caminho rápido, não


tem suporte para serialização assíncrona.

No .NET 7 e versões anteriores, essa limitação também se aplica a sobrecargas


síncronas de JsonSerializer.Serialize que aceitam um arquivo Stream. A partir do
.NET 8, mesmo que a serialização de streaming exija modelos baseados em
metadados, ela voltará ao caminho rápido se os conteúdos forem conhecidos por
serem pequenos o suficiente para se ajustarem ao tamanho do buffer
predeterminado. Para obter mais informações, consulte
https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-
8/#json .
Desabilitar padrões de reflexão
Como o System.Text.Json usa reflexão por padrão, chamar um método básico de
serialização pode interromper aplicativos Native AOT, que não dão suporte a todas as
APIs de reflexão necessárias. Essas interrupções podem ser desafiadoras para
diagnosticar, pois podem ser imprevisíveis, e os aplicativos muitas vezes são depurados
usando o runtime CoreCLR, onde a reflexão funciona. Em vez disso, se você desabilitar
explicitamente a serialização baseada em reflexão, as interrupções serão mais fáceis de
diagnosticar. O código que usa serialização baseada em reflexão fará com que uma
InvalidOperationException com uma mensagem descritiva seja gerada durante o tempo
de execução.

Para desabilitar a reflexão padrão em seu aplicativo, defina a propriedade MSBuild


JsonSerializerIsReflectionEnabledByDefault como false em seu arquivo de projeto:

XML

<PropertyGroup>

<JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectio
nEnabledByDefault>
</PropertyGroup>

O comportamento dessa propriedade é consistente independentemente do


runtime, seja CoreCLR ou Native AOT.
Se você não especificar essa propriedade e PublishTrimmed estiver habilitado, a
serialização baseada em reflexão será desabilitada automaticamente.

Você pode verificar programaticamente se a reflexão está desabilitada usando a


propriedade JsonSerializer.IsReflectionEnabledByDefault. O snippet de código a seguir
mostra como você pode configurar seu serializador dependendo se a reflexão está
habilitada:

C#

static JsonSerializerOptions CreateDefaultOptions()


{
return new()
{
TypeInfoResolver = JsonSerializer.IsReflectionEnabledByDefault
? new DefaultJsonTypeInfoResolver()
: MyContext.Default
};
}
Como a propriedade é tratada como uma constante de tempo de link, o método
anterior não enraíza o resolvedor baseado em reflexão em aplicativos executados no
Native AOT.

Especificar opções
No .NET 8 e versões posteriores, a maioria das opções que você pode definir usando
JsonSerializerOptions também pode ser definida usando o atributo
JsonSourceGenerationOptionsAttribute. A vantagem de definir opções por meio do
atributo é que a configuração é especificada em tempo de compilação, garantindo que
a propriedade MyContext.Default gerada seja pré-configurada com todas as opções
relevantes definidas.

O código a seguir mostra como definir opções usando o atributo


JsonSourceGenerationOptionsAttribute.

C#

[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext :
JsonSerializerContext
{
}

Ao usar JsonSourceGenerationOptionsAttribute para especificar opções de serialização,


chame um dos seguintes métodos de serialização:

Um método JsonSerializer.Serialize que usa um TypeInfo<TValue> . Passe a


propriedade Default.<TypeName> da classe de contexto:

C#

jsonString = JsonSerializer.Serialize(
weatherForecast,
SerializationModeOptionsContext.Default.WeatherForecast);

Um método JsonSerializer.Serialize que usa um contexto. Passe a propriedade


estática Default de sua classe de contexto.

C#
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast),
SerializationModeOptionsContext.Default);

Se você chamar um método que permite passar em sua própria instância de


Utf8JsonWriter , a configuração Indented do gravador é respeitada em vez da opção

JsonSourceGenerationOptionsAttribute.WriteIndented .

Se você criar e usar uma instância de contexto chamando o construtor que usa uma
instância JsonSerializerOptions , a instância fornecida será usada em vez das opções
especificadas por JsonSourceGenerationOptionsAttribute .

Aqui estão os exemplos anteriores em um programa completo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SerializeOnlyWithOptions
{
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

[JsonSourceGenerationOptions(
WriteIndented = true,
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
GenerationMode = JsonSourceGenerationMode.Serialization)]
[JsonSerializable(typeof(WeatherForecast))]
internal partial class SerializationModeOptionsContext :
JsonSerializerContext
{
}
public class Program
{
public static void Main()
{
string jsonString;
WeatherForecast weatherForecast = new()
{ Date = DateTime.Parse("2019-08-01"), TemperatureCelsius =
25, Summary = "Hot" };

// Serialize using TypeInfo<TValue> provided by the context


// and options specified by [JsonSourceGenerationOptions].
jsonString = JsonSerializer.Serialize(
weatherForecast,
SerializationModeOptionsContext.Default.WeatherForecast);
Console.WriteLine(jsonString);
// output:
//{
// "date": "2019-08-01T00:00:00",
// "temperatureCelsius": 0,
// "summary": "Hot"
//}

// Serialize using Default context


// and options specified by [JsonSourceGenerationOptions].
jsonString = JsonSerializer.Serialize(
weatherForecast, typeof(WeatherForecast),
SerializationModeOptionsContext.Default);
Console.WriteLine(jsonString);
// output:
//{
// "date": "2019-08-01T00:00:00",
// "temperatureCelsius": 0,
// "summary": "Hot"
//}
}
}
}

Combinar geradores de origem


Você pode combinar contratos de vários contextos gerados pela origem dentro de uma
única instância de JsonSerializerOptions. Use a propriedade
JsonSerializerOptions.TypeInfoResolver para encadear vários contextos que foram
combinados usando o método JsonTypeInfoResolver.Combine(IJsonTypeInfoResolver[]).

C#

var options = new JsonSerializerOptions


{
TypeInfoResolver = JsonTypeInfoResolver.Combine(ContextA.Default,
ContextB.Default, ContextC.Default);
};

A partir do .NET 8, se você quiser preceder ou acrescentar outro contexto


posteriormente, poderá fazê-lo usando a propriedade
JsonSerializerOptions.TypeInfoResolverChain. A ordenação da cadeia é significativa:
JsonSerializerOptions consulta cada um dos resolvedores em sua ordem especificada e
retorna o primeiro resultado que não é nulo.

C#
options.TypeInfoResolverChain.Add(ContextD.Default); // Append to the end of
the list.
options.TypeInfoResolverChain.Insert(0, ContextE.Default); // Insert at the
beginning of the list.

Qualquer alteração feita na propriedade TypeInfoResolverChain é refletida por


TypeInfoResolver e vice-versa.

Serializar campos de enumeração como cadeias


de caracteres
Por padrão, as enumerações são serializadas como números. Para serializar os campos
específicos de uma enumeração como cadeias de caracteres ao usar a geração de
origem, anote-os com o conversor JsonStringEnumConverter<TEnum>. Ou para definir
uma política global para todas as enumerações, use o atributo
JsonSourceGenerationOptionsAttribute.

Conversor JsonStringEnumConverter<T>
Para serializar nomes de enumeração como cadeias de caracteres utilizando a geração
de fonte, use o conversor JsonStringEnumConverter<TEnum>. (Não há suporte para o
tipo JsonStringEnumConverter não genérico no runtime de AOT Nativa)

Anote o tipo de enumeração com o conversor JsonStringEnumConverter<TEnum>


usando o atributo JsonConverterAttribute:

C#

public class WeatherForecastWithPrecipEnum


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation? Precipitation { get; set; }
}

[JsonConverter(typeof(JsonStringEnumConverter<Precipitation>))]
public enum Precipitation
{
Drizzle, Rain, Sleet, Hail, Snow
}

Crie uma classe JsonSerializerContext e anote-a com o atributo


JsonSerializableAttribute:
C#

[JsonSerializable(typeof(WeatherForecastWithPrecipEnum))]
public partial class Context1 : JsonSerializerContext { }

O código a seguir serializa os nomes de enumeração em vez dos valores numéricos:

C#

var weatherForecast = new WeatherForecastWithPrecipEnum


{
Date = DateTime.Parse("2019-08-01"),
TemperatureCelsius = 25,
Precipitation = Precipitation.Sleet
};

var options = new JsonSerializerOptions


{
WriteIndented = true,
TypeInfoResolver = Context1.Default,
};
string? jsonString = JsonSerializer.Serialize(weatherForecast, options);

O JSON resultante é semelhante ao seguinte exemplo:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Precipitation": "Sleet"
}

Política ampla
Em vez de usar o tipo JsonStringEnumConverter<TEnum>, você poderá aplicar uma
política ampla para serializar enumerações como cadeias de caracteres usando o
JsonSourceGenerationOptionsAttribute. Crie uma classe JsonSerializerContext e anote-a
com os atributos JsonSerializableAttributee JsonSourceGenerationOptionsAttribute:

C#

[JsonSourceGenerationOptions(UseStringEnumConverter = true)]
[JsonSerializable(typeof(WeatherForecast2WithPrecipEnum))]
public partial class Context2 : JsonSerializerContext { }
Observe que a enumeração não possui o JsonConverterAttribute:

C#

public class WeatherForecast2WithPrecipEnum


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public Precipitation2? Precipitation { get; set; }
}

public enum Precipitation2


{
Drizzle, Rain, Sleet, Hail, Snow
}

Confira também
Serialização e desserialização de JSON em .NET - visão geral
Como usar a biblioteca

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como personalizar a codificação de
caracteres com System.Text.Json
Artigo • 26/05/2023

Por padrão, o serializador escapa de todos os caracteres não ASCII. Ou seja, ele os
substitui por \uxxxx , onde xxxx é o código Unicode do caractere. Por exemplo, se a
propriedade Summary no JSON a seguir estiver definida como Cirílico жарко , o objeto
WeatherForecast será serializado conforme mostrado neste exemplo:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "\u0436\u0430\u0440\u043A\u043E"
}

Serializar conjuntos de caracteres de linguagem


Para serializar os conjuntos de caracteres de uma ou mais linguagens sem escapar,
especifique intervalo(s) Unicode ao criar uma instância de
System.Text.Encodings.Web.JavaScriptEncoder, conforme mostrado no exemplo a seguir:

C#

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

C#

var options1 = new JsonSerializerOptions


{
Encoder = JavaScriptEncoder.Create(UnicodeRanges.BasicLatin,
UnicodeRanges.Cyrillic),
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options1);

Este código não escapa de caracteres cirílicos ou gregos. Se a propriedade Summary for
definida como Cirílico жарко , o objeto WeatherForecast será serializado conforme
mostrado neste exemplo:
JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "жарко"
}

Por padrão, o codificador é inicializado com o intervalo BasicLatin.

Para serializar todos os conjuntos de idiomas sem escapar, use UnicodeRanges.All.

Serializar caracteres específicos


Uma alternativa é especificar caracteres individuais que você deseja permitir a passagem
sem ser escapado. O exemplo a seguir serializa apenas os dois primeiros caracteres de
жарко :

C#

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;

C#

var encoderSettings = new TextEncoderSettings();


encoderSettings.AllowCharacters('\u0436', '\u0430');
encoderSettings.AllowRange(UnicodeRanges.BasicLatin);
var options2 = new JsonSerializerOptions
{
Encoder = JavaScriptEncoder.Create(encoderSettings),
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options2);

Aqui está um exemplo de JSON produzido pelo código anterior:

JSON

{
"Date": "2019-08-01T00:00:00-07:00",
"TemperatureCelsius": 25,
"Summary": "жа\u0440\u043A\u043E"
}
Listas de bloqueios
As seções anteriores mostram como especificar listas de permissões de pontos de
código ou intervalos que você não deseja que escapem. No entanto, há listas de
bloqueios globais e específicas do codificador que podem substituir determinados
pontos de código na lista de permissões. Os pontos de código em uma lista de
bloqueios sempre são escapados, mesmo que estejam incluídos em sua lista de
permissões.

Lista de bloqueio global


A lista de bloqueio global inclui coisas como caracteres de uso privado, caracteres de
controle, pontos de código indefinidos e determinadas categorias Unicode, como a
categoria Space_Separator , excluindo U+0020 SPACE . Por exemplo, U+3000 IDEOGRAPHIC
SPACE é escapado mesmo se você especificar símbolos CJK do intervalo Unicode e

Pontuação (U+3000-U+303F) como sua lista de permissões.

A lista de bloqueio global é um detalhe de implementação que foi alterado em cada


versão do .NET Core e no .NET 5. Não assuma uma dependência de um caractere sendo
membro (ou não sendo membro) da lista de bloqueio global.

Listas de bloqueios específicas do codificador


Exemplos de pontos de código bloqueados específicos do codificador incluem '<' e
'&' para o codificador HTML, '\' para o codificador JSON e '%' para o codificador de

URL. Por exemplo, o codificador HTML sempre escapa de E comerciais ( '&' ), mesmo
que o E comercial no intervalo BasicLatin e todos os codificadores sejam inicializados
com BasicLatin por padrão.

Serializar todos os caracteres


Para minimizar o escape, é possível usar o operador
JavaScriptEncoder.UnsafeRelaxedJsonEscaping, conforme mostrado no exemplo a
seguir:

C#

using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Unicode;
C#

var options3 = new JsonSerializerOptions


{
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, options3);

U Cuidado

Em comparação com o codificador padrão, o codificador


UnsafeRelaxedJsonEscaping é mais permissivo em permitir que os caracteres

passem sem escape:

Ele não faz escape de caracteres sensíveis a HTML, como < , > , & e ' .
Ele não oferece proteções adicionais de defesa em profundidade contra XSS
ou ataques de divulgação não autorizada de informação, como aqueles que
podem resultar da discordância entre o cliente e o servidor sobre o conjunto
de caracteres.

Use o codificador não seguro somente quando souber que o cliente interpretará o
conteúdo resultante como JSON codificado em UTF-8. Por exemplo, é possível usá-
lo se o servidor estiver enviando o cabeçalho de resposta Content-Type:
application/json; charset=utf-8 . Nunca permita que a saída bruta
UnsafeRelaxedJsonEscaping seja emitida em uma página HTML ou em um elemento

<script> .

Confira também
System.Text.Json overview
Como serializar e desserializar JSON

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our  Provide product feedback
contributor guide.
Como gravar conversores
personalizados para serialização JSON
(marshalling) no .NET
Artigo • 04/10/2023

Este artigo mostra como criar conversores personalizados para as classes de serialização
JSON fornecidas no namespace System.Text.Json. Para obter uma introdução a
System.Text.Json , confira Como serializar e desserializar o JSON no .NET.

Um conversor é uma classe que converte um objeto ou um valor de e para JSON. O


namespace System.Text.Json tem conversores internos para a maioria dos tipos
primitivos que são mapeados para primitivos JavaScript. Você pode escrever
conversores personalizados:

Para substituir o comportamento padrão de um conversor interno. Por exemplo,


talvez você queira que os valores DateTime sejam representados pelo formato
mm/dd/aaaa. Por padrão, há suporte para ISO 8601-1:2019, incluindo o perfil RFC
3339. Para obter mais informações, confira Suporte para DateTime e
DateTimeOffset em System.Text.Json.
Para dar suporte a um tipo de valor personalizado. Por exemplo, um struct
PhoneNumber .

Você também pode escrever conversores personalizados ou estender System.Text.Json


com novas funcionalidades. Os seguintes cenários são abordados posteriormente neste
artigo:

Desserializar tipos inferidos para propriedades de objeto.


Dar suporte para desserialização polimórfica.
Dar suporte para viagem de ida e volta para Stack<T>.
Usar o conversor de sistema padrão.

O Visual Basic não pode ser usado para gravar conversores personalizados, mas pode
chamar conversores implementados em bibliotecas C#. Para saber mais, confira Suporte
para Visual Basic.

Padrões de conversor personalizado


Há dois padrões para criar um conversor personalizado: o padrão básico e o padrão de
fábrica. O padrão de fábrica é para conversores que lidam com tipos Enum ou genéricos
abertos. O padrão básico é para tipos genéricos não genéricos e fechados. Por exemplo,
os conversores para os seguintes tipos exigem o padrão de fábrica:

Dictionary<TKey,TValue>
Enum
List<T>

Alguns exemplos de tipos que podem ser tratados pelo padrão básico incluem:

Dictionary<int, string>

WeekdaysEnum
List<DateTimeOffset>

DateTime
Int32

O padrão básico cria uma classe que pode lidar com um tipo. O padrão de fábrica cria
uma classe que determina, em tempo de execução, qual tipo específico é necessário e
cria dinamicamente o conversor apropriado.

Conversor básico de exemplo


O exemplo a seguir é um conversor que substitui a serialização padrão para um tipo de
dados. O conversor usa o formato mm/dd/aaaa para propriedades DateTimeOffset .

C#

using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class DateTimeOffsetJsonConverter : JsonConverter<DateTimeOffset>
{
public override DateTimeOffset Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
DateTimeOffset.ParseExact(reader.GetString()!,
"MM/dd/yyyy", CultureInfo.InvariantCulture);

public override void Write(


Utf8JsonWriter writer,
DateTimeOffset dateTimeValue,
JsonSerializerOptions options) =>
writer.WriteStringValue(dateTimeValue.ToString(
"MM/dd/yyyy", CultureInfo.InvariantCulture));
}
}

Conversor de padrões de fábrica de exemplo


O código a seguir mostra um conversor personalizado que funciona com
Dictionary<Enum,TValue> . O código segue o padrão de fábrica porque o primeiro

parâmetro de tipo genérico é Enum e o segundo está aberto. O método CanConvert


retorna true apenas para um Dictionary com dois parâmetros genéricos, o primeiro
deles é um tipo Enum . O conversor interno obtém um conversor para lidar com qualquer
tipo fornecido em tempo de execução para TValue .

C#

using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class DictionaryTKeyEnumTValueConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}

if (typeToConvert.GetGenericTypeDefinition() !=
typeof(Dictionary<,>))
{
return false;
}

return typeToConvert.GetGenericArguments()[0].IsEnum;
}

public override JsonConverter CreateConverter(


Type type,
JsonSerializerOptions options)
{
Type keyType = type.GetGenericArguments()[0];
Type valueType = type.GetGenericArguments()[1];

JsonConverter converter =
(JsonConverter)Activator.CreateInstance(
typeof(DictionaryEnumConverterInner<,>).MakeGenericType(
new Type[] { keyType, valueType }),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object[] { options },
culture: null)!;

return converter;
}

private class DictionaryEnumConverterInner<TKey, TValue> :


JsonConverter<Dictionary<TKey, TValue>> where TKey : struct,
Enum
{
private readonly JsonConverter<TValue> _valueConverter;
private readonly Type _keyType;
private readonly Type _valueType;

public DictionaryEnumConverterInner(JsonSerializerOptions
options)
{
// For performance, use the existing converter.
_valueConverter = (JsonConverter<TValue>)options
.GetConverter(typeof(TValue));

// Cache the key and value types.


_keyType = typeof(TKey);
_valueType = typeof(TValue);
}

public override Dictionary<TKey, TValue> Read(


ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}

var dictionary = new Dictionary<TKey, TValue>();

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return dictionary;
}

// Get the key.


if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}

string? propertyName = reader.GetString();


// For performance, parse with ignoreCase:false first.
if (!Enum.TryParse(propertyName, ignoreCase: false, out
TKey key) &&
!Enum.TryParse(propertyName, ignoreCase: true, out
key))
{
throw new JsonException(
$"Unable to convert \"{propertyName}\" to Enum
\"{_keyType}\".");
}

// Get the value.


reader.Read();
TValue value = _valueConverter.Read(ref reader,
_valueType, options)!;

// Add to dictionary.
dictionary.Add(key, value);
}

throw new JsonException();


}

public override void Write(


Utf8JsonWriter writer,
Dictionary<TKey, TValue> dictionary,
JsonSerializerOptions options)
{
writer.WriteStartObject();

foreach ((TKey key, TValue value) in dictionary)


{
var propertyName = key.ToString();
writer.WritePropertyName

(options.PropertyNamingPolicy?.ConvertName(propertyName) ?? propertyName);

_valueConverter.Write(writer, value, options);


}

writer.WriteEndObject();
}
}
}
}

Etapas para seguir o padrão básico


As seguintes etapas explicam como criar um conversor seguindo o padrão básico:
Crie uma classe que deriva de JsonConverter<T> em que T é o tipo a ser
serializado e desserializado.
Substitua o método Read para desserializar o JSON de entrada e convertê-lo no
tipo T . Use o Utf8JsonReader que é passado para o método para ler o JSON. Você
não precisa se preocupar em lidar com os dados parciais, pois o serializador passa
todos os dados para o escopo JSON atual. Portanto, não é necessário chamar Skip
ou TrySkip para validar que Read retorna true .
Substitua o método Write para serializar o objeto de entrada do tipo T . Use o
Utf8JsonWriter que é passado para o método para escrever o JSON.
Substitua o método CanConvert somente se necessário. A implementação padrão
retorna true quando o tipo a ser convertido é do tipo T . Portanto, os conversores
que dão suporte apenas ao tipo T não precisam substituir esse método. Para
obter um exemplo de um conversor que precisa substituir esse método, confira a
seção desserialização polimórfica mais adiante neste artigo.

Você pode consultar código-fonte dos conversores internos como implementações


de referência para escrever conversores personalizados.

Etapas para seguir o padrão de fábrica


As seguintes etapas explicam como criar um conversor seguindo o padrão de fábrica:

Crie uma classe que deriva de JsonConverterFactory.


Substitua o método CanConvert para retornar true quando o tipo a ser convertido
é aquele que o conversor pode manipular. Por exemplo, se o conversor for para
List<T> , ele só poderá lidar com List<int> , List<string> e List<DateTime> .

Substitua o método CreateConverter para retornar uma instância de uma classe de


conversor que lidará com o tipo a converter fornecido no tempo de execução.
Crie a classe de conversor que o método CreateConverter instancia.

O padrão de fábrica é necessário para genéricos abertos porque o código para


converter um objeto de e para uma cadeia de caracteres não é o mesmo para todos os
tipos. Um conversor para um tipo genérico aberto ( List<T> , por exemplo) precisa criar
um conversor para um tipo genérico fechado ( List<DateTime> , por exemplo) nos
bastidores. O código deve ser gravado para lidar com cada tipo genérico fechado com
que o conversor pode lidar.

O tipo Enum é semelhante a um tipo genérico aberto: um conversor para Enum precisa
criar um conversor para um Enum específico ( WeekdaysEnum , por exemplo) nos
bastidores.
O uso do Utf8JsonReader no método Read
Se o conversor estiver convertendo um objeto JSON, o Utf8JsonReader será posicionado
no token de objeto inicial quando o método Read for iniciado. Em seguida, você deve
ler todos os tokens nesse objeto e sair do método com o leitor posicionado no token
de objeto final correspondente. Se você ler além do final do objeto ou parar antes de
chegar ao token final correspondente, receberá uma exceção JsonException indicando
que:

O conversor 'ConverterName' leu demais ou não o suficiente.

Para obter um exemplo, confira o conversor de exemplo de padrão de fábrica anterior.


O método Read começa verificando se o leitor está posicionado em um token de objeto
inicial. Ele lê até descobrir que está posicionado no próximo token de objeto final. Ele
para no token de objeto de extremidade seguinte porque não há tokens de objeto de
início intervindo que indicariam um objeto dentro do objeto. A mesma regra sobre
token de início e token final se aplicará se você estiver convertendo uma matriz. Para
obter um exemplo, confira o conversor de exemplo Stack<T> mais adiante neste artigo.

Tratamento de erros
O serializador fornece tratamento especial para tipos de exceção JsonException e
NotSupportedException.

JsonException
Se você gerar um JsonException sem mensagem, o serializador criará uma mensagem
incluindo o caminho para a parte do JSON que causou o erro. Por exemplo, a instrução
throw new JsonException() produz uma mensagem de erro como o seguinte exemplo:

Saída

Unhandled exception. System.Text.Json.JsonException:


The JSON value could not be converted to System.Object.
Path: $.Date | LineNumber: 1 | BytePositionInLine: 37.

Se você fornecer uma mensagem (por exemplo), throw new JsonException("Error


occurred") o serializador ainda definirá as propriedades e Path, LineNumber e

BytePositionInLine.
NotSupportedException
Se você gerar um NotSupportedException , sempre obterá as informações do caminho na
mensagem. Se você fornecer uma mensagem, as informações do caminho serão
acrescentadas a ela. Por exemplo, a instrução throw new NotSupportedException("Error
occurred.") produz uma mensagem de erro como o seguinte exemplo:

Saída

Error occurred. The unsupported member type is located on type


'System.Collections.Generic.Dictionary`2[Samples.SummaryWords,System.Int32]'
.
Path: $.TemperatureRanges | LineNumber: 4 | BytePositionInLine: 24

Quando gerar qual tipo de exceção


Quando o conteúdo JSON contiver tokens que não são válidos para o tipo que está
sendo desserializado, gere um JsonException .

Quando você quiser desabilitar determinados tipos, gere um NotSupportedException .


Essa exceção é o que o serializador gera automaticamente para tipos que não têm
suporte. Por exemplo, System.Type não tem suporte por motivos de segurança,
portanto, uma tentativa de desserializá-la resulta em uma NotSupportedException .

Você pode gerar outras exceções conforme necessário, mas elas não incluem
automaticamente informações de caminho JSON.

Registrar um conversor personalizado


Registre um conversor personalizado para fazer com que os métodos Serialize e
Deserialize o usem. Escolha uma das seguintes abordagens:

Adiciona uma instância da classe do conversor à coleção


JsonSerializerOptions.Converters.
Aplique o atributo [JsonConverter] às propriedades que exigem o conversor
personalizado.
Aplique o atributo [JsonConverter] a uma classe ou a um struct que representa um
tipo de valor personalizado.

Exemplo de registro – coleção Converters


Aqui está um exemplo que torna o DateTimeOffsetJsonConverter o padrão para
propriedades do tipo DateTimeOffset:

C#

var serializeOptions = new JsonSerializerOptions


{
WriteIndented = true,
Converters =
{
new DateTimeOffsetJsonConverter()
}
};

jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);

Suponha que você serialize uma instância do seguinte tipo:

C#

public class WeatherForecast


{
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

Aqui está um exemplo de saída JSON que mostra que o conversor personalizado foi
usado:

JSON

{
"Date": "08/01/2019",
"TemperatureCelsius": 25,
"Summary": "Hot"
}

O seguinte código usa a mesma abordagem para desserializar usando o conversor


personalizado DateTimeOffset :

C#

var deserializeOptions = new JsonSerializerOptions();


deserializeOptions.Converters.Add(new DateTimeOffsetJsonConverter());
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>(jsonString,
deserializeOptions)!;
Exemplo de registro – [JsonConverter] em uma
propriedade
O seguinte código seleciona um conversor personalizado para a propriedade Date :

C#

public class WeatherForecastWithConverterAttribute


{
[JsonConverter(typeof(DateTimeOffsetJsonConverter))]
public DateTimeOffset Date { get; set; }
public int TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

O código para serializar WeatherForecastWithConverterAttribute não requer o uso de


JsonSerializeOptions.Converters :

C#

var serializeOptions = new JsonSerializerOptions


{
WriteIndented = true
};
jsonString = JsonSerializer.Serialize(weatherForecast, serializeOptions);

O código para desserializar também não requer o uso de Converters :

C#

weatherForecast =
JsonSerializer.Deserialize<WeatherForecastWithConverterAttribute>
(jsonString)!;

Exemplo de registro – [JsonConverter] em um


tipo
Aqui está o código que cria um struct e aplica o atributo [JsonConverter] a ele:

C#

using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
[JsonConverter(typeof(TemperatureConverter))]
public struct Temperature
{
public Temperature(int degrees, bool celsius)
{
Degrees = degrees;
IsCelsius = celsius;
}

public int Degrees { get; }


public bool IsCelsius { get; }
public bool IsFahrenheit => !IsCelsius;

public override string ToString() =>


$"{Degrees}{(IsCelsius ? "C" : "F")}";

public static Temperature Parse(string input)


{
int degrees = int.Parse(input.Substring(0, input.Length - 1));
bool celsius = input.Substring(input.Length - 1) == "C";

return new Temperature(degrees, celsius);


}
}
}

Aqui está o conversor personalizado para o struct anterior:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class TemperatureConverter : JsonConverter<Temperature>
{
public override Temperature Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
Temperature.Parse(reader.GetString()!);

public override void Write(


Utf8JsonWriter writer,
Temperature temperature,
JsonSerializerOptions options) =>
writer.WriteStringValue(temperature.ToString());
}
}
O atributo [JsonConverter] no struct registra o conversor personalizado como o padrão
para propriedades do tipo Temperature . O conversor é usado automaticamente na
propriedade TemperatureCelsius do seguinte tipo ao serializá-la ou desserializá-la:

C#

public class WeatherForecastWithTemperatureStruct


{
public DateTimeOffset Date { get; set; }
public Temperature TemperatureCelsius { get; set; }
public string? Summary { get; set; }
}

Precedência de registro do conversor


Durante a serialização ou desserialização, um conversor é escolhido para cada elemento
JSON na seguinte ordem, listada da prioridade mais alta para a mais baixa:

[JsonConverter] aplicado a uma propriedade.

Um conversor adicionado à coleção Converters .


[JsonConverter] aplicado a um tipo de valor personalizado ou POCO.

Se diversos conversores personalizados para um tipo forem registrados na coleção


Converters , o primeiro conversor que retornará true para CanConvert será usado.

Um conversor interno será escolhido somente se nenhum conversor personalizado


aplicável for registrado.

Exemplos de conversor para cenários comuns


As seções a seguir apresentam exemplos de conversor que abordam alguns cenários
comuns com que a funcionalidade interna não lida.

Desserializar tipos inferidos para propriedades de objeto.


Dar suporte para viagem de ida e volta para Stack<T>.
Usar o conversor de sistema padrão.

Para obter um conversor de exemplo DataTable, confira Tipos de coleção com suporte.

Desserializar tipos inferidos para propriedades de objeto


Ao desserializar para uma propriedade do tipo object , um objeto JsonElement é criado.
O motivo é que o desserializador não sabe qual tipo CLR criar e não tenta adivinhar. Por
exemplo, se uma propriedade JSON tiver "true", o desserializador não inferirá que o
valor é um Boolean ; se um elemento tiver "01/01/2019", o desserializador não inferirá
que ele é uma DateTime .

A inferência de tipos pode ser imprecisa. Se o desserializador analisar um número JSON


que não tem nenhum ponto decimal como um long , isso poderá resultar em problemas
fora do intervalo se o valor tiver sido originalmente serializado como ulong ou
BigInteger . Analisar um número que tem um ponto decimal como um double pode

perder precisão se o número tiver sido originalmente serializado como um decimal .

Para cenários que exigem inferência de tipos, o código a seguir mostra um conversor
personalizado para propriedades object . O código converte:

true e false em Boolean

Números sem um decimal em long


Números com um decimal em double
Datas em DateTime
Cadeias de caracteres em string
Todo o resto em JsonElement

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace CustomConverterInferredTypesToObject
{
public class ObjectToInferredTypesConverter : JsonConverter<object>
{
public override object Read(
ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) => reader.TokenType switch
{
JsonTokenType.True => true,
JsonTokenType.False => false,
JsonTokenType.Number when reader.TryGetInt64(out long l) =>
l,
JsonTokenType.Number => reader.GetDouble(),
JsonTokenType.String when reader.TryGetDateTime(out DateTime
datetime) => datetime,
JsonTokenType.String => reader.GetString()!,
_ => JsonDocument.ParseValue(ref reader).RootElement.Clone()
};
public override void Write(
Utf8JsonWriter writer,
object objectToWrite,
JsonSerializerOptions options) =>
JsonSerializer.Serialize(writer, objectToWrite,
objectToWrite.GetType(), options);
}

public class WeatherForecast


{
public object? Date { get; set; }
public object? TemperatureCelsius { get; set; }
public object? Summary { get; set; }
}

public class Program


{
public static void Main()
{
string jsonString = @"{
""Date"": ""2019-08-01T00:00:00-07:00"",
""TemperatureCelsius"": 25,
""Summary"": ""Hot""
}";

WeatherForecast weatherForecast =
JsonSerializer.Deserialize<WeatherForecast>(jsonString)!;
Console.WriteLine($"Type of Date property no converter =
{weatherForecast.Date!.GetType()}");

var options = new JsonSerializerOptions();


options.WriteIndented = true;
options.Converters.Add(new ObjectToInferredTypesConverter());
weatherForecast = JsonSerializer.Deserialize<WeatherForecast>
(jsonString, options)!;
Console.WriteLine($"Type of Date property with converter =
{weatherForecast.Date!.GetType()}");

Console.WriteLine(JsonSerializer.Serialize(weatherForecast,
options));
}
}
}

// Produces output like the following example:


//
//Type of Date property no converter = System.Text.Json.JsonElement
//Type of Date property with converter = System.DateTime
//{
// "Date": "2019-08-01T00:00:00-07:00",
// "TemperatureCelsius": 25,
// "Summary": "Hot"
//}
O exemplo mostra o código do conversor e uma classe WeatherForecast com
propriedades object . O método Main desserializa uma cadeia de caracteres JSON em
uma instância WeatherForecast , primeiro sem usar o conversor e então usando o
conversor. A saída do console mostra que, sem o conversor, o tipo de tempo de
execução da propriedade Date é JsonElement ; com o conversor, o tipo de tempo de
execução é DateTime .

A pasta de testes de unidade no namespace System.Text.Json.Serialization tem


mais exemplos de conversores personalizados que lidam com a desserialização em
propriedades object .

Dar suporte para desserialização polimórfica


O .NET 7 dá suporte à serialização e à desserialização polimórficas. No entanto, em
versões anteriores do .NET, havia suporte limitado para serialização polimórfica e
nenhum suporte para desserialização. Se você estiver usando o .NET 6 ou uma versão
anterior, a desserialização exigirá um conversor personalizado.

Suponha, por exemplo, que você tenha uma classe Person base abstrata, com as classes
derivadas Employee e Customer . A desserialização polimórfica significa que, no
momento do design, você pode especificar Person como o alvo da desserialização e os
objetos Customer e Employee no JSON são corretamente desserializados no tempo de
execução. Durante a desserialização, você precisa encontrar pistas que identifiquem o
tipo necessário no JSON. Os tipos de pistas disponíveis variam conforme cada cenário.
Por exemplo, uma propriedade discriminatória pode estar disponível ou talvez você
precise confiar na presença ou na ausência de uma propriedade específica. A versão
atual de System.Text.Json não fornece atributos para especificar como lidar com
cenários de desserialização polimórfica, portanto, conversores personalizados são
necessários.

O código a seguir mostra uma classe base, duas classes derivadas e um conversor
personalizado para elas. O conversor usa uma propriedade discriminatória para fazer a
desserialização polimórfica. O tipo discriminatório não está nas definições de classe,
mas é criado durante a serialização e é lido durante a desserialização.

) Importante

O código de exemplo requer que pares de nome/valor de objeto JSON


permaneçam em ordem, o que não é um requisito padrão do JSON.
C#

public class Person


{
public string? Name { get; set; }
}

public class Customer : Person


{
public decimal CreditLimit { get; set; }
}

public class Employee : Person


{
public string? OfficeNumber { get; set; }
}

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class PersonConverterWithTypeDiscriminator :
JsonConverter<Person>
{
enum TypeDiscriminator
{
Customer = 1,
Employee = 2
}

public override bool CanConvert(Type typeToConvert) =>


typeof(Person).IsAssignableFrom(typeToConvert);

public override Person Read(


ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}

reader.Read();
if (reader.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}

string? propertyName = reader.GetString();


if (propertyName != "TypeDiscriminator")
{
throw new JsonException();
}

reader.Read();
if (reader.TokenType != JsonTokenType.Number)
{
throw new JsonException();
}

TypeDiscriminator typeDiscriminator =
(TypeDiscriminator)reader.GetInt32();
Person person = typeDiscriminator switch
{
TypeDiscriminator.Customer => new Customer(),
TypeDiscriminator.Employee => new Employee(),
_ => throw new JsonException()
};

while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
return person;
}

if (reader.TokenType == JsonTokenType.PropertyName)
{
propertyName = reader.GetString();
reader.Read();
switch (propertyName)
{
case "CreditLimit":
decimal creditLimit = reader.GetDecimal();
((Customer)person).CreditLimit = creditLimit;
break;
case "OfficeNumber":
string? officeNumber = reader.GetString();
((Employee)person).OfficeNumber = officeNumber;
break;
case "Name":
string? name = reader.GetString();
person.Name = name;
break;
}
}
}

throw new JsonException();


}

public override void Write(


Utf8JsonWriter writer, Person person, JsonSerializerOptions
options)
{
writer.WriteStartObject();

if (person is Customer customer)


{
writer.WriteNumber("TypeDiscriminator",
(int)TypeDiscriminator.Customer);
writer.WriteNumber("CreditLimit", customer.CreditLimit);
}
else if (person is Employee employee)
{
writer.WriteNumber("TypeDiscriminator",
(int)TypeDiscriminator.Employee);
writer.WriteString("OfficeNumber", employee.OfficeNumber);
}

writer.WriteString("Name", person.Name);

writer.WriteEndObject();
}
}
}

O seguinte código registra o conversor:

C#

var serializeOptions = new JsonSerializerOptions();


serializeOptions.Converters.Add(new PersonConverterWithTypeDiscriminator());

O conversor pode desserializar o JSON criado usando o mesmo conversor para


serializar, por exemplo:

JSON

[
{
"TypeDiscriminator": 1,
"CreditLimit": 10000,
"Name": "John"
},
{
"TypeDiscriminator": 2,
"OfficeNumber": "555-1234",
"Name": "Nancy"
}
]

O código conversor no exemplo anterior lê e grava cada propriedade manualmente.


Uma alternativa é chamar Deserialize ou Serialize fazer parte do trabalho. Para obter
um exemplo, confira esta postagem StackOverflow .

Uma forma alternativa de fazer a desserialização


polimórfica
Você pode chamar Deserialize no método Read :

Faça um clone da instância Utf8JsonReader . Como Utf8JsonReader é um struct,


isso requer apenas uma instrução de atribuição.
Use o clone para ler os tokens discriminatórios.
Chame Deserialize usando a instância original Reader depois de saber o tipo
necessário. Você pode chamar Deserialize porque a instância original Reader
ainda está posicionada para ler o token de objeto begin.

Uma desvantagem desse método é que você não pode passar a instância de opções
original que registra o conversor para Deserialize . Isso causaria um estouro de pilha,
conforme explicado nas propriedades necessárias. O seguinte exemplo mostra um
método Read que usa esta alternativa:

C#

public override Person Read(


ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions
options)
{
Utf8JsonReader readerClone = reader;

if (readerClone.TokenType != JsonTokenType.StartObject)
{
throw new JsonException();
}

readerClone.Read();
if (readerClone.TokenType != JsonTokenType.PropertyName)
{
throw new JsonException();
}

string? propertyName = readerClone.GetString();


if (propertyName != "TypeDiscriminator")
{
throw new JsonException();
}

readerClone.Read();
if (readerClone.TokenType != JsonTokenType.Number)
{
throw new JsonException();
}

TypeDiscriminator typeDiscriminator =
(TypeDiscriminator)readerClone.GetInt32();
Person person = typeDiscriminator switch
{
TypeDiscriminator.Customer => JsonSerializer.Deserialize<Customer>
(ref reader)!,
TypeDiscriminator.Employee => JsonSerializer.Deserialize<Employee>
(ref reader)!,
_ => throw new JsonException()
};
return person;
}

Dar suporte para viagem de ida e volta para Stack<T>


Se você desserializar uma cadeia de caracteres JSON em um objeto Stack<T> e serializar
esse objeto, o conteúdo da pilha estará em ordem inversa. Esse comportamento se
aplica aos seguintes tipos e interface e tipos definidos pelo usuário que derivam deles:

Stack
Stack<T>
ConcurrentStack<T>
ImmutableStack<T>
IImmutableStack<T>

Para dar suporte à serialização e à desserialização que retém a ordem original na pilha, é
necessário um conversor personalizado.

O seguinte código mostra um conversor personalizado que permite a viagem de ida e


volta de objetos Stack<T> :

C#

using System.Diagnostics;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;

namespace SystemTextJsonSamples
{
public class JsonConverterFactoryForStackOfT : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
=> typeToConvert.IsGenericType
&& typeToConvert.GetGenericTypeDefinition() == typeof(Stack<>);
public override JsonConverter CreateConverter(
Type typeToConvert, JsonSerializerOptions options)
{
Debug.Assert(typeToConvert.IsGenericType &&
typeToConvert.GetGenericTypeDefinition() ==
typeof(Stack<>));

Type elementType = typeToConvert.GetGenericArguments()[0];

JsonConverter converter =
(JsonConverter)Activator.CreateInstance(
typeof(JsonConverterForStackOfT<>)
.MakeGenericType(new Type[] { elementType }),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: null,
culture: null)!;

return converter;
}
}

public class JsonConverterForStackOfT<T> : JsonConverter<Stack<T>>


{
public override Stack<T> Read(
ref Utf8JsonReader reader, Type typeToConvert,
JsonSerializerOptions options)
{
if (reader.TokenType != JsonTokenType.StartArray)
{
throw new JsonException();
}
reader.Read();

var elements = new Stack<T>();

while (reader.TokenType != JsonTokenType.EndArray)


{
elements.Push(JsonSerializer.Deserialize<T>(ref reader,
options)!);

reader.Read();
}

return elements;
}

public override void Write(


Utf8JsonWriter writer, Stack<T> value, JsonSerializerOptions
options)
{
writer.WriteStartArray();

var reversed = new Stack<T>(value);


foreach (T item in reversed)
{
JsonSerializer.Serialize(writer, item, options);
}

writer.WriteEndArray();
}
}
}

O seguinte código registra o conversor:

C#

var options = new JsonSerializerOptions


{
Converters = { new JsonConverterFactoryForStackOfT() },
};

Usar o conversor de sistema padrão


Em alguns cenários, pode ser útil usar o conversor de sistema padrão em um conversor
personalizado. Para fazer isso, obtenha o conversor do sistema da propriedade
JsonSerializerOptions.Default, conforme mostra o seguinte exemplo:

C#

public class MyCustomConverter : JsonConverter<int>


{
private readonly static JsonConverter<int> s_defaultConverter =

(JsonConverter<int>)JsonSerializerOptions.Default.GetConverter(typeof(int));

// Custom serialization logic


public override void Write(
Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToString());
}

// Fall back to default deserialization logic


public override int Read(
ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions
options)
{
return s_defaultConverter.Read(ref reader, typeToConvert, options);
}
}
Manipular valores nulos
Por padrão, o serializador manipula valores nulos da seguinte maneira:

Para tipos de referência e tipos Nullable<T>:


Ele não passa null para conversores personalizados na serialização.
Ele não passa JsonTokenType.Null para conversores personalizados na
desserialização.
Ele retorna uma instância null na desserialização.
Ele grava null diretamente com o gravador na serialização.

Para tipos de valor não anuláveis:


Ele passa JsonTokenType.Null para conversores personalizados na
desserialização. (Se nenhum conversor personalizado estiver disponível, uma
exceção JsonException será gerada pelo conversor interno do tipo.)

Esse comportamento de tratamento nulo é principalmente para otimizar o desempenho


ignorando uma chamada extra para o conversor. Além disso, evita forçar conversores
para tipos anuláveis para verificar null no início de cada substituição de método Read e
Write .

Para habilitar um conversor personalizado a lidar com null para um tipo de referência
ou valor, substitua JsonConverter<T>.HandleNull para retornar true , conforme mostra
o seguinte exemplo:

C#

using System.Text.Json;
using System.Text.Json.Serialization;

namespace CustomConverterHandleNull
{
public class Point
{
public int X { get; set; }
public int Y { get; set; }

[JsonConverter(typeof(DescriptionConverter))]
public string? Description { get; set; }
}

public class DescriptionConverter : JsonConverter<string>


{
public override bool HandleNull => true;

public override string Read(


ref Utf8JsonReader reader,
Type typeToConvert,
JsonSerializerOptions options) =>
reader.GetString() ?? "No description provided.";

public override void Write(


Utf8JsonWriter writer,
string value,
JsonSerializerOptions options) =>
writer.WriteStringValue(value);
}

public class Program


{
public static void Main()
{
string json = @"{""x"":1,""y"":2,""Description"":null}";

Point point = JsonSerializer.Deserialize<Point>(json)!;


Console.WriteLine($"Description: {point.Description}");
}
}
}

// Produces output like the following example:


//
//Description: No description provided.

Preservar referências
Por padrão, os dados de referência só são armazenados em cache para cada chamada a
Serialize ou Deserialize. Para persistir as referências de uma chamada
Serialize / Deserialize para outra, enraíze a instância ReferenceResolver no site de

chamada de Serialize / Deserialize . O seguinte código mostra um exemplo para esse


cenário:

Você escreve um conversor personalizado para o tipo Company .


Não é recomendável serializar manualmente a propriedade Supervisor , que é uma
Employee . É recomendável delegar isso ao serializador e preservar as referências

que você já salvou.

Aqui estão as classes Employee e Company :

C#

public class Employee


{
public string? Name { get; set; }
public Employee? Manager { get; set; }
public List<Employee>? DirectReports { get; set; }
public Company? Company { get; set; }
}

public class Company


{
public string? Name { get; set; }
public Employee? Supervisor { get; set; }
}

O conversor tem esta aparência:

C#

class CompanyConverter : JsonConverter<Company>


{
public override Company Read(ref Utf8JsonReader reader, Type
typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}

public override void Write(Utf8JsonWriter writer, Company value,


JsonSerializerOptions options)
{
writer.WriteStartObject();

writer.WriteString("Name", value.Name);

writer.WritePropertyName("Supervisor");
JsonSerializer.Serialize(writer, value.Supervisor, options);

writer.WriteEndObject();
}
}

Uma classe derivada de ReferenceResolver armazena as referências em um dicionário:

C#

class MyReferenceResolver : ReferenceResolver


{
private uint _referenceCount;
private readonly Dictionary<string, object> _referenceIdToObjectMap =
new ();
private readonly Dictionary<object, string> _objectToReferenceIdMap =
new (ReferenceEqualityComparer.Instance);

public override void AddReference(string referenceId, object value)


{
if (!_referenceIdToObjectMap.TryAdd(referenceId, value))
{
throw new JsonException();
}
}

public override string GetReference(object value, out bool


alreadyExists)
{
if (_objectToReferenceIdMap.TryGetValue(value, out string?
referenceId))
{
alreadyExists = true;
}
else
{
_referenceCount++;
referenceId = _referenceCount.ToString();
_objectToReferenceIdMap.Add(value, referenceId);
alreadyExists = false;
}

return referenceId;
}

public override object ResolveReference(string referenceId)


{
if (!_referenceIdToObjectMap.TryGetValue(referenceId, out object?
value))
{
throw new JsonException();
}

return value;
}
}

Uma classe derivada de ReferenceHandler contém uma instância e MyReferenceResolver


cria uma nova instância somente quando necessário (em um método chamado Reset
neste exemplo):

C#

class MyReferenceHandler : ReferenceHandler


{
public MyReferenceHandler() => Reset();

private ReferenceResolver? _rootedResolver;


public override ReferenceResolver CreateResolver() => _rootedResolver!;
public void Reset() => _rootedResolver = new MyReferenceResolver();

}
Quando o código de exemplo chama o serializador, ele usa uma instância
JsonSerializerOptions na qual a propriedade ReferenceHandler é definida como uma
instância de MyReferenceHandler . Ao seguir esse padrão, redefina o dicionário
ReferenceResolver ao terminar de serializar para evitar que ele continue crescendo para

sempre.

C#

var options = new JsonSerializerOptions();

options.Converters.Add(new CompanyConverter());
var myReferenceHandler = new MyReferenceHandler();
options.ReferenceHandler = myReferenceHandler;
options.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
options.WriteIndented = true;

string str = JsonSerializer.Serialize(tyler, options);

// Reset after serializing to avoid out of bounds memory growth in the


resolver.
myReferenceHandler.Reset();

O exemplo anterior só faz serialização, mas uma abordagem semelhante pode ser
adotada para desserialização.

Outros exemplos de conversor personalizado


O artigo Migrar de Newtonsoft.Json para System.Text.Json contém exemplos adicionais
de conversores personalizados.

A pasta de testes de unidade no código-fonte System.Text.Json.Serialization inclui


outros exemplos de conversor personalizado, como:

Conversor Int32 que converte nulo em 0 ao desserializar


Conversor Int32 que permite valores de cadeia de caracteres e números em
desserializar
Conversor de enumeração
Conversor List<T> que aceita dados externos
Conversor Long[] que funciona com uma lista de números delimitada por vírgulas

Se você precisar fazer um conversor que modifique o comportamento de um conversor


interno, poderá obter o código-fonte do conversor para servir como um ponto de
partida para personalização.
Recursos adicionais
Código-fonte para conversores internos
System.Text.Json overview
Como serializar e desserializar JSON

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Personalizar um contrato JSON
Artigo • 15/06/2023

A biblioteca System.Text.Json constrói um contrato JSON para cada tipo .NET, o que
define como o tipo deve ser serializado e desserializado. O contrato é derivado da
forma do tipo, que inclui características como suas propriedades e campos e se ele
implementa a interface IEnumerable ou IDictionary. Os tipos são mapeados para
contratos no tempo de execução com o uso de reflexão ou no tempo de compilação
com o uso do gerador de origem.

A partir do .NET 7, você pode personalizar esses contratos JSON para oferecer mais
controle sobre como os tipos são convertidos em JSON e vice-versa. A lista abaixo
mostra apenas alguns exemplos dos tipos de personalização que você pode fazer para
serialização e desserialização:

Serializar campos e propriedades privados.


Dar suporte a vários nomes para uma única propriedade (por exemplo, se uma
versão anterior da biblioteca usou um nome diferente).
Ignorar propriedades com um nome, tipo ou valor específico.
Distinguir entre valores explícitos null e a falta de um valor no conteúdo JSON.
Atributos de suporte System.Runtime.Serialization, como DataContractAttribute.
Para obter mais informações, confira atributos System.Runtime.Serialization.
Gere uma exceção se o JSON incluir uma propriedade que não faça parte do tipo
de destino. Para obter mais informações, confira Manipular membros ausentes.

Como aceitar
Há duas maneiras de se conectar à personalização. Ambas envolvem a obtenção de um
resolvedor, cujo trabalho é fornecer uma instância JsonTypeInfo para cada tipo que
precisa ser serializado.

Chamada do construtor DefaultJsonTypeInfoResolver() para obtenção do


JsonSerializerOptions.TypeInfoResolver e adição de suasações personalizadas à
propriedade Modifiers.

Por exemplo:

C#

JsonSerializerOptions options = new()


{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers =
{
MyCustomModifier1,
MyCustomModifier2
}
}
};

se você adicionar vários modificadores, eles serão chamados sequencialmente.

Programação de um resolvedor personalizado que implementa


IJsonTypeInfoResolver.
Se um tipo não for tratado, IJsonTypeInfoResolver.GetTypeInfo deverá retornar
null para esse tipo.

Você também pode combinar seu resolvedor personalizado com outros, por
exemplo, o resolvedor padrão. Os resolvedores serão consultados em ordem
até que um valor não nulo JsonTypeInfo seja retornado para o tipo.

Aspectos configuráveis
A propriedade JsonTypeInfo.Kind indica como o conversor serializa determinado tipo
(por exemplo, como um objeto ou como uma matriz) e se suas propriedades são
serializadas. Você pode consultar essa propriedade para determinar quais aspectos do
contrato JSON de um tipo você pode configurar. Há quatro tipos diferentes:

JsonTypeInfo.Kind Descrição

JsonTypeInfoKind.Object O conversor serializará o tipo em um objeto JSON e usará suas


propriedades. Esse tipo é usado para a maioria dos tipos de
classe e struct e permite a maior flexibilidade.

JsonTypeInfoKind.Enumerable O conversor serializará o tipo em uma matriz JSON. Esse tipo é


usado para tipos como List<T> e matriz.

JsonTypeInfoKind.Dictionary O conversor serializará o tipo em um objeto JSON. Esse tipo é


usado para tipos como Dictionary<K, V> .

JsonTypeInfoKind.None O conversor não especifica como serializará o tipo ou quais


propriedades JsonTypeInfo ele usará. Esse tipo é usado para
tipos como System.Object, int e string e para todos os tipos
que usam um conversor personalizado.

Modificadores
Um modificador é uma classe Action<JsonTypeInfo> ou um método com um parâmetro
JsonTypeInfo que obtém o estado atual do contrato como argumento e faz
modificações no contrato. Por exemplo, você pode iterar por meio das propriedades
pré-preenchidas na JsonTypeInfo especificada encontrar a que interessa e modificar sua
propriedade JsonPropertyInfo.Get (para serialização) ou JsonPropertyInfo.Set
propriedade (para desserialização). Ou pode construir uma nova propriedade usando
JsonTypeInfo.CreateJsonPropertyInfo(Type, String) e adicioná-la à coleção
JsonTypeInfo.Properties.

A tabela a seguir mostra as modificações que você pode fazer e como alcançá-las.

Modification JsonTypeInfo.Kind Como alcançá-las Exemplo


aplicável

Personalizar o JsonTypeInfoKind.Object Modifique o delegado Incrementar


valor de uma JsonPropertyInfo.Get (para um valor da
propriedade serialização) ou o delegado propriedade
JsonPropertyInfo.Set (para
desserialização) da propriedade.

Adicionar ou JsonTypeInfoKind.Object Adicionar e remover itens da lista Serializar


remover JsonTypeInfo.Properties. campos
propriedades privados

Serializar JsonTypeInfoKind.Object Modifique o predicado Ignorar


condicionalmente JsonPropertyInfo.ShouldSerialize propriedades
uma propriedade para a propriedade. com um tipo
específico

Personalizar o JsonTypeInfoKind.None Modifique o valor Permitir que


tratamento de JsonTypeInfo.NumberHandling valores int
números para um para o tipo. sejam
tipo específico cadeias de
caracteres

Exemplo: incrementar o valor de uma


propriedade
Considere o exemplo a seguir em que o modificador incrementa o valor de determinada
propriedade na desserialização modificando seu delegado JsonPropertyInfo.Set. Além
de definir o modificador, o exemplo também apresenta um novo atributo que ele usa
para localizar a propriedade cujo valor deve ser incrementado. Esse é um exemplo de
como personalizar uma propriedade.

C#
using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

namespace Serialization
{
// Custom attribute to annotate the property
// we want to be incremented.
[AttributeUsage(AttributeTargets.Property)]
class SerializationCountAttribute : Attribute
{
}

// Example type to serialize and deserialize.


class Product
{
public string Name { get; set; } = "";
[SerializationCount]
public int RoundTrips { get; set; }
}

public class SerializationCountExample


{
// Custom modifier that increments the value
// of a specific property on deserialization.
static void IncrementCounterModifier(JsonTypeInfo typeInfo)
{
foreach (JsonPropertyInfo propertyInfo in typeInfo.Properties)
{
if (propertyInfo.PropertyType != typeof(int))
continue;

object[] serializationCountAttributes =
propertyInfo.AttributeProvider?.GetCustomAttributes(typeof(SerializationCoun
tAttribute), true) ?? Array.Empty<object>();
SerializationCountAttribute? attribute =
serializationCountAttributes.Length == 1 ?
(SerializationCountAttribute)serializationCountAttributes[0] : null;

if (attribute != null)
{
Action<object, object?>? setProperty = propertyInfo.Set;
if (setProperty is not null)
{
propertyInfo.Set = (obj, value) =>
{
if (value != null)
{
// Increment the value by 1.
value = (int)value + 1;
}

setProperty (obj, value);


};
}
}
}
}

public static void RunIt()


{
var product = new Product
{
Name = "Aquafresh"
};

JsonSerializerOptions options = new()


{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { IncrementCounterModifier }
}
};

// First serialization and deserialization.


string serialized = JsonSerializer.Serialize(product, options);
Console.WriteLine(serialized);
// {"Name":"Aquafresh","RoundTrips":0}

Product deserialized = JsonSerializer.Deserialize<Product>


(serialized, options)!;
Console.WriteLine($"{deserialized.RoundTrips}");
// 1

// Second serialization and deserialization.


serialized = JsonSerializer.Serialize(deserialized, options);
Console.WriteLine(serialized);
// { "Name":"Aquafresh","RoundTrips":1}

deserialized = JsonSerializer.Deserialize<Product>(serialized,
options)!;
Console.WriteLine($"{deserialized.RoundTrips}");
// 2
}
}
}

Observe na saída que o valor de RoundTrips é incrementado sempre que a instância


Product é desserializada.

Exemplo: serializar campos privados


Por padrão, System.Text.Json ignora campos e propriedades privados. Esse exemplo
adiciona um novo atributo de toda a classe, JsonIncludePrivateFieldsAttribute , para
alterar esse padrão. Se o modificador encontrar o atributo em um tipo, ele adicionará
todos os campos privados no tipo como novas propriedades a JsonTypeInfo.

C#

using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;

namespace Serialization
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class JsonIncludePrivateFieldsAttribute : Attribute { }

[JsonIncludePrivateFields]
public class Human
{
private string _name;
private int _age;

public Human()
{
// This constructor should be used only by deserializers.
_name = null!;
_age = 0;
}

public static Human Create(string name, int age)


{
Human h = new()
{
_name = name,
_age = age
};

return h;
}

[JsonIgnore]
public string Name
{
get => _name;
set => throw new NotSupportedException();
}

[JsonIgnore]
public int Age
{
get => _age;
set => throw new NotSupportedException();
}
}
public class PrivateFieldsExample
{
static void AddPrivateFieldsModifier(JsonTypeInfo jsonTypeInfo)
{
if (jsonTypeInfo.Kind != JsonTypeInfoKind.Object)
return;

if
(!jsonTypeInfo.Type.IsDefined(typeof(JsonIncludePrivateFieldsAttribute),
inherit: false))
return;

foreach (FieldInfo field in


jsonTypeInfo.Type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic))
{
JsonPropertyInfo jsonPropertyInfo =
jsonTypeInfo.CreateJsonPropertyInfo(field.FieldType, field.Name);
jsonPropertyInfo.Get = field.GetValue;
jsonPropertyInfo.Set = field.SetValue;

jsonTypeInfo.Properties.Add(jsonPropertyInfo);
}
}

public static void RunIt()


{
var options = new JsonSerializerOptions
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { AddPrivateFieldsModifier }
}
};

var human = Human.Create("Julius", 37);


string json = JsonSerializer.Serialize(human, options);
Console.WriteLine(json);
// {"_name":"Julius","_age":37}

Human deserializedHuman = JsonSerializer.Deserialize<Human>


(json, options)!;
Console.WriteLine($"[Name={deserializedHuman.Name}; Age=
{deserializedHuman.Age}]");
// [Name=Julius; Age=37]
}
}
}

 Dica

Se os nomes de campo privados começarem com sublinhados, considere remover


os sublinhados dos nomes quando você adicionar os campos como novas
propriedades JSON.

Exemplo: ignorar propriedades com um tipo


específico
Talvez seu modelo tenha propriedades com nomes ou tipos específicos que você não
deseja expor aos usuários. Por exemplo, você pode ter uma propriedade que armazena
credenciais ou algumas informações que são inúteis de se ter no conteúdo.

O exemplo a seguir mostra como filtrar propriedades com um tipo específico,


SecretHolder . Ele faz isso usando um método de extensão IList<T> para remover

propriedades que tenham o tipo especificado da lista JsonTypeInfo.Properties. As


propriedades filtradas desaparecem completamente do contrato, o que significa que
System.Text.Json não as analisa durante a serialização ou desserialização.

C#

using System.Text.Json;
using System.Text.Json.Serialization.Metadata;

namespace Serialization
{
class ExampleClass
{
public string Name { get; set; } = "";
public SecretHolder? Secret { get; set; }
}

class SecretHolder
{
public string Value { get; set; } = "";
}

class IgnorePropertiesWithType
{
private readonly Type[] _ignoredTypes;

public IgnorePropertiesWithType(params Type[] ignoredTypes)


=> _ignoredTypes = ignoredTypes;

public void ModifyTypeInfo(JsonTypeInfo ti)


{
if (ti.Kind != JsonTypeInfoKind.Object)
return;

ti.Properties.RemoveAll(prop =>
_ignoredTypes.Contains(prop.PropertyType));
}
}

public class IgnoreTypeExample


{
public static void RunIt()
{
var modifier = new
IgnorePropertiesWithType(typeof(SecretHolder));

JsonSerializerOptions options = new()


{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { modifier.ModifyTypeInfo }
}
};

ExampleClass obj = new()


{
Name = "Password",
Secret = new SecretHolder { Value = "MySecret" }
};

string output = JsonSerializer.Serialize(obj, options);


Console.WriteLine(output);
// {"Name":"Password"}
}
}

public static class ListHelpers


{
// IList<T> implementation of List<T>.RemoveAll method.
public static void RemoveAll<T>(this IList<T> list, Predicate<T>
predicate)
{
for (int i = 0; i < list.Count; i++)
{
if (predicate(list[i]))
{
list.RemoveAt(i--);
}
}
}
}
}

Exemplo: permitir que os valores int sejam


cadeias de caracteres
Talvez seu JSON de entrada possa ter aspas em torno de um dos tipos numéricos, mas
não em outros. Se você tivesse controle sobre a classe, poderia colocar
JsonNumberHandlingAttribute no tipo para corrigir isso, mas não tem. Antes do .NET 7,
você precisaria escrever um conversor personalizado para corrigir esse comportamento,
o que requer uma boa quantidade de programação. Usando a personalização do
contrato, você pode personalizar o comportamento de tratamento de números para
qualquer tipo.

O exemplo a seguir altera o comportamento de todos os valores int . O exemplo pode


ser facilmente ajustado para se aplicar a qualquer tipo ou a uma propriedade específica
de qualquer tipo.

C#

using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;

namespace Serialization
{
public class Point
{
public int X { get; set; }
public int Y { get; set; }
}

public class AllowIntsAsStringsExample


{
static void SetNumberHandlingModifier(JsonTypeInfo jsonTypeInfo)
{
if (jsonTypeInfo.Type == typeof(int))
{
jsonTypeInfo.NumberHandling =
JsonNumberHandling.AllowReadingFromString;
}
}

public static void RunIt()


{
JsonSerializerOptions options = new()
{
TypeInfoResolver = new DefaultJsonTypeInfoResolver
{
Modifiers = { SetNumberHandlingModifier }
}
};

// Triple-quote syntax is a C# 11 feature.


Point point = JsonSerializer.Deserialize<Point>("""
{"X":"12","Y":"3"}""", options)!;
Console.WriteLine($"({point.X},{point.Y})");
// (12,3)
}
}
}

Sem o modificador para permitir a leitura de valores int de uma cadeia de caracteres, o
programa teria terminado com uma exceção:

Exceção sem tratamento. System.Text.Json.JsonException: o valor JSON não pôde ser


convertido em System.Int32. Path: $.X | LineNumber: 0 | BytePositionInLine: 9.

Outras maneiras de personalizar a serialização


Além de personalizar um contrato, há outras maneiras de influenciar o comportamento
de serialização e desserialização, incluindo o seguinte:

Uso de atributos derivados de JsonAttribute, por exemplo, JsonIgnoreAttribute e


JsonPropertyOrderAttribute.
Modificação de JsonSerializerOptions, por exemplo, para definir uma política de
nomenclatura ou serializar valores de enumeração como cadeias de caracteres em
vez de números.
Ao escrever um conversor personalizado que faz o trabalho real de escrever o
JSON e, durante a desserialização, construir um objeto.

A personalização do contrato é uma melhoria em relação a essas personalizações


preexistentes, pois você pode não ter acesso ao tipo para adicionar atributos, e a
criação de um conversor personalizado é complexa e prejudica o desempenho.

Confira também
Personalização do contrato JSON (postagem no blog)
Novidades em System.Text.Json no .NET 7 (postagem no blog)

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
serialização XML e SOAP
Artigo • 09/05/2023

A serialização XML converte (serializa) as propriedades e os campos públicos de um


objeto, e os parâmetros e valores de retorno de métodos, em um fluxo XML que esteja
de acordo com um documento de linguagem de definição de esquema XML (XSD)
específico. A serialização XML resulta em classes fortemente tipadas com propriedades e
campos públicos que são convertidos em um formato serial (neste caso, em XML) para
armazenamento ou transporte.

Como XML é um padrão aberto, o fluxo XML pode ser processado por qualquer
aplicativo, quando necessário, independentemente da plataforma. Por exemplo, serviços
Web XML criados com ASP.NET usam a classe XmlSerializer para criar fluxos XML que
passam dados entre aplicativos de serviço Web XML por toda a Internet ou entre
intranets. Por outro lado, a desserialização obtém esse fluxo XML e reconstrói o objeto.

A serialização XML também pode ser usada para serializar objetos em fluxos XML que
atendam à especificação SOAP. SOAP é um protocolo baseado em XML, projetado
especificamente para transportar chamadas de procedimentos usando XML.

Para serializar e desserializar objetos, use a classe XmlSerializer. Para criar as classes a
serem serializadas, use a ferramenta de definição de esquema XML.

Confira também
Serialização binária
Serviços Web XML criados com o ASP.NET e clientes de serviços Web XML
Serialização XML
Artigo • 06/04/2023

A serialização é o processo de conversão de um objeto em um formulário que possa ser


prontamente transportado. Por exemplo, você pode serializar um objeto e transportá-lo
pela Internet usando HTTP entre um cliente e um servidor. Na outra extremidade, a
desserialização reconstrói o objeto a partir do fluxo.

A serialização XML serializa apenas os valores de propriedades e de campos públicos de


um objeto em um fluxo XML. A serialização XML não inclui informações de tipo. Por
exemplo, se você tiver um objeto Book no namespace Library, não haverá garantia de
que ele será desserializado em um objeto do mesmo tipo.

7 Observação

A serialização XML não converte métodos, indexadores, campos privados nem


propriedades somente leitura (exceto coleções somente leitura). Para serializar
todos os campos e propriedades (públicos e privados) de um objeto, use
DataContractSerializer, em vez da serialização XML.

A classe central na serialização XML é a classe XmlSerializer e os métodos mais


importantes nessa classe são os métodos Serialize e Deserialize. O XmlSerializer cria
arquivos C# e compila-os em arquivos .dll para executar essa serialização. A XML
Serializer Generator Tool (Sgen.exe) tem como objetivo gerar com antecedência esses
assemblies de serialização a serem implantados com seu aplicativo e melhorar o
desempenho da inicialização. O fluxo XML gerado pelo XmlSerializer é em
conformidade com a recomendação XSD (linguagem de definição de esquema XML)
1.0 do W3C (World Wide Web Consortium). Além disso, os tipos de dados gerados
são compatíveis com o documento intitulado "XML Schema Part 2: Datatypes".

Os dados em seus objetos são descritos com o uso de construções de linguagem de


programação como classes, campos, propriedades, tipos primitivos, matrizes e até
mesmo XML inserido na forma de objetos XmlElement ou XmlAttribute. Você tem a
opção de criar suas próprias classes, anotadas com atributos, ou de usar a ferramenta
de definição de esquema XML para gerar as classes com base em um esquema XML
existente.

Se você tiver um esquema XML, poderá executar a ferramenta de definição de esquema


XML para gerar um conjunto de classes fortemente tipadas para o esquema e anotadas
com atributos. Quando uma instância desse tipo de classe é serializada, o XML adere ao
esquema XML. Se você tiver uma classe desse tipo, poderá programar com um modelo
de objeto facilmente manipulado com a certeza de que o XML gerado está de acordo
com o esquema XML. Trata-se de uma alternativa ao uso de outras classes do .NET,
como as classes XmlReader e XmlWriter, para analisar e gravar um fluxo XML. Para
obter mais informações, consulte Documentos e dados XML. Essas classes permitem
analisar qualquer fluxo XML. Em contraste, use XmlSerializer quando o fluxo XML
precisar estar em conformidade com um esquema XML conhecido.

Os atributos controlam o fluxo XML gerado pela classe XmlSerializer, permitindo que
você defina itens do fluxo XML, como o namespace de XML, o nome do elemento, o
nome do atributo e assim por diante. Para obter mais informações sobre como esses
atributos e como eles controlam a serialização XML, consulte Controlando a serialização
XML usando atributos. Para ver uma tabela desses atributos que são usados para
controlar o XML gerado, consulte Atributos que controlam a serialização XML.

A classe XmlSerializer pode serializar ainda mais um objeto e gerar um fluxo XML SOAP
codificado. O XML gerado segue a seção 5 do documento do World Wide Web
Consortium intitulado "Simple Object Access Protocol (SOAP) 1.1". Para mais
informações sobre esse processo, confira Como serializar um objeto como um fluxo
XML codificado em SOAP. Para ver uma tabela dos atributos que controlam o XML
gerado, consulte Atributos que controlam a serialização SOAP codificada.

A classe XmlSerializer gera as mensagens SOAP criadas por serviços Web XML e
passadas para eles. Para controlar as mensagens SOAP, você pode aplicar atributos às
classes, aos valores de retorno, aos parâmetros e aos campos localizados em um
arquivo de serviço Web XML (.asmx). É possível usar ambos os atributos listados em
"Atributos que controlam a serialização XML" e em "Atributos que controlam a
serialização SOAP codificada" porque um serviço Web XML pode usar o estilo SOAP
literal ou codificado. Para obter mais informações sobre como usar atributos para
controlar o XML gerado por um serviço Web XML, consulte Serialização XML com
Serviços Web XML. Para mais informações sobre serviços Web XML e SOAP, confira
Como personalizar a formatação da mensagem SOAP.

Considerações de segurança para aplicativos


XmlSerializer
Ao criar um aplicativo que use XmlSerializer, esteja ciente dos seguintes itens e de suas
implicações:

O XmlSerializer cria arquivos C# (.cs) e compila-os em arquivos .dll no diretório


nomeado pela variável de ambiente TEMP; a serialização ocorre com essas DLLs.
7 Observação

Esses assemblies de serialização podem ser gerados com antecedência e


assinados com o uso da ferramenta SGen.exe. Isso não funciona em um
servidor de serviços Web. Em outras palavras, esses assemblies são apenas
para uso de cliente e para serialização manual.

O código e as DLLs ficam vulneráveis a um processo mal-intencionado no


momento de criação e compilação. É possível que dois ou mais usuários
compartilhem o diretório TEMP. O compartilhamento de um diretório TEMP é
perigoso quando as duas contas têm privilégios de segurança diferentes e a conta
com maior privilégio executa um aplicativo usando o XmlSerializer. Nesse caso,
um usuário pode violar a segurança do computador substituindo o arquivo .cs ou
.dll compilado. Para eliminar essa preocupação, sempre verifique se cada conta no
computador tem seu próprio perfil. Por padrão, as variáveis de ambiente TEMP
apontam para um diretório diferente para cada conta.

Se algum usuário mal-intencionado enviar um fluxo contínuo de dados XML para


um servidor Web (um ataque de negação de serviço), então o XmlSerializer
continuará a processar os dados até o computador ser executado com um número
insuficiente de recursos.

Esse tipo de ataque será eliminado se você usar um computador que execute o IIS
(Serviços de Informações da Internet) e seu aplicativo for executado no IIS. Os IIS
apresenta um portão que não processa fluxos maiores do que uma quantidade
especificada (o padrão é de 4 KB). Se você criar um aplicativo que não use o IIS e
que desserialize com o XmlSerializer, você deverá implementar um portão similar
que evite um ataque de negação de serviço.

O XmlSerializer serializa dados e executa qualquer código usando qualquer tipo


especificado para ele.

Existem duas maneiras de um objeto mal-intencionado apresentar uma ameaça.


Ele pode executar o código mal-intencionado ou injetar o código mal-
intencionado no arquivo C# criado pelo XmlSerializer. No segundo caso, há uma
possibilidade teórica de que um objeto mal-intencionado possa, de alguma forma,
injetar o código no arquivo C# criado pelo XmlSerializer. Embora esse problema
tenha sido examinado detalhadamente e um ataque desse tipo seja considerado
improvável, é preciso tomar cuidado para nunca serializar dados com um tipo
desconhecido e não confiável.

Dados confidenciais serializados podem ficar vulneráveis.


Depois que o XmlSerializer tiver serializado os dados, ele poderá ser armazenado
como um arquivo XML ou como outro armazenamento de dados. Se seu
repositório de dados estiver disponível para outros processos, ou estiver visível em
uma intranet ou na Internet, os dados poderão ser roubados e usados de maneira
mal-intencionada. Por exemplo, quando você cria um aplicativo que serializa
pedidos que incluam números de cartão de crédito, os dados são altamente
confidenciais. Para ajudar a evitar problemas, sempre proteja o repositório dos
dados e siga as etapas para mantê-lo privado.

Serialização de uma classe simples


O exemplo de código a seguir mostra uma classe básica com um campo público.

C#

public class OrderForm


{
public DateTime OrderDate;
}

Quando uma instância dessa classe é serializada, ela pode ser similar ao exemplo a
seguir.

XML

<OrderForm>
<OrderDate>12/12/01</OrderDate>
</OrderForm>

Para obter mais exemplos de serialização, consulte Exemplos de Serialização XML.

Itens que podem ser serializados


Os seguintes itens podem ser serializados com a classe XmlSerializer:

Propriedades e campos de leitura/gravação públicos de classes públicas.

Classes que implementam ICollection ou IEnumerable.

7 Observação

Somente as coleções são serializadas, não propriedades públicas.


Objetos XmlElement.

Objetos XmlNode.

Objetos DataSet.

Para obter mais informações sobre a serialização ou desserialização de objetos, consulte


Como serializar um objeto e Como desserializar um objeto.

Vantagens de usar a serialização XML


A classe XmlSerializer confere controle total e flexível quando você serializa um objeto
como XML. Se você estiver criando um serviço Web XML, poderá aplicar atributos que
controlam a serialização para classes e membros para garantir que a saída XML esteja
de acordo com um esquema específico.

Por exemplo, XmlSerializer permite que você:

Especificar se um campo ou propriedade deve ser codificado como atributo ou


como elemento.

Especificar um namespace XML a ser usado.

Especificar o nome de um elemento ou atributo se um nome de campo ou de


propriedade estiver incorreto.

Outra vantagem da serialização XML é que não há restrições aos aplicativos que você
desenvolve, contanto que o fluxo XML gerado esteja de acordo com o esquema
especificado. Imagine um esquema usado para descrever livros. Ele apresenta os
elementos título, autor, editor e número ISBN. Você pode desenvolver um aplicativo que
processe os dados XML da forma que desejar, por exemplo, como um pedido de livro
ou como um inventário de livros. Em ambos os casos, o único requisito é que o fluxo
XML esteja de acordo com o esquema XSD especificado.

Considerações de serialização XML


A observação a seguir deve ser considerada ao usar a classe XmlSerializer:

A ferramenta Sgen.exe é expressamente destinada a gerar assemblies de


serialização para desempenho máximo.

Os dados serializados contêm somente os próprios dados e a estrutura de suas


classes. A identidade de tipos e as informações de assemblies não são incluídas.
Somente as propriedades e os campos públicos podem ser serializados. As
propriedades devem ter acessadores públicos (métodos get e set). Se você precisar
serializar dados não públicos, use a classe DataContractSerializer, em vez da
serialização XML.

Uma classe deve ter um construtor sem parâmetro para ser serializado por
XmlSerializer.

Os métodos não podem ser serializados.

XmlSerializer pode processar classes que implementam IEnumerable ou


ICollection de maneira diferente caso elas atendam a determinados requisitos,
como a seguir.

Uma classe que implementa IEnumerable deve implementar um método Add


público que aceita um único parâmetro. O parâmetro do método Add deve ser
consistente (polimórfico) com o tipo retornado da propriedade
IEnumerator.Current, retornada do método GetEnumerator.

Uma classe que implementa ICollection, além de IEnumerable (como


CollectionBase), deve ter uma propriedade pública indexada Item (um indexador
em C#) que aceite um inteiro e deve ter uma propriedade pública Count do tipo
integer. O parâmetro passado para o método Add deve ser do mesmo tipo que
aquele retornado pela propriedade Item ou deve corresponder a uma das bases
desse tipo.

Para as classes que implementam ICollection, os valores a serem serializados são


recuperados da propriedade indexada Item, não ao chamar GetEnumerator. Além
disso, propriedades e campos públicos não são serializados, exceto os campos
públicos que retornam outra classe de coleção (que implemente ICollection). Para
obter um exemplo, consulte Exemplos de serialização XML.

Mapeamento de tipo de dados XSD


O documento do W3C intitulado XML Schema Part 2: Datatypes especifica os tipos de
dados simples permitidos em um esquema XSD (linguagem de definição de esquema
XML). Para muitos desses tipos (por exemplo, int e decimal), há um tipo de dados
correspondente no .NET. No entanto, alguns tipos de dados XML não têm um tipo de
dados correspondente no .NET, por exemplo, o tipo de dados NMTOKEN. Nesses casos,
se você usar a ferramenta de definição de esquema XML (Ferramenta de Definição de
Esquema XML (Xsd.exe)) para gerar classes de um esquema, um atributo apropriado
será aplicado a um membro de tipo string e a respectiva propriedade DataType será
definida como o nome do tipo de dados XML. Por exemplo, se um esquema contiver
um elemento nomeado "MyToken" com o tipo de dados XML NMTOKEN, a classe
gerada poderá conter um membro conforme mostrado no exemplo a seguir.

C#

[XmlElement(DataType = "NMTOKEN")]
public string MyToken;

Da mesma forma, se você estiver criando uma classe que precise estar em conformidade
com um esquema XML (XSD) específico, deverá aplicar o atributo apropriado e definir
sua propriedade DataType como o nome do tipo de dados XML desejado.

Para obter uma lista completa de mapeamentos de tipo, consulte a propriedade


DataType para algumas das seguintes classes de atributos:

SoapAttributeAttribute

SoapElementAttribute

XmlArrayItemAttribute

XmlAttributeAttribute

XmlElementAttribute

XmlRootAttribute

Confira também
XmlSerializer
DataContractSerializer
FileStream
Serialização XML e SOAP
Serialização binária
Serialização
XmlSerializer
Exemplos de Serialização XML
Como serializar um objeto
Como desserializar um objeto
Exemplos de Serialização XML
Artigo • 08/06/2023

A serialização XML pode ter mais de um formulário, de simples a complexo. Por


exemplo, é possível serializar uma classe que consiste apenas em propriedades e
campos públicos, conforme mostrado em Apresentando a serialização XML. Os
seguintes exemplos de código manipulam vários cenários avançados, incluindo como
usar a serialização XML para gerar um fluxo XML que está de acordo com um
documento de Esquema XML (XSD) específico.

Serializando um DataSet
Além de serializar uma instância de uma classe pública, você também pode serializar
uma instância de um DataSet, conforme mostrado no seguinte exemplo de código:

C#

private void SerializeDataSet(string filename)


{
XmlSerializer ser = new XmlSerializer(typeof(DataSet));

// Creates a DataSet; adds a table, column, and ten rows.


DataSet ds = new DataSet("myDataSet");
DataTable t = new DataTable("table1");
DataColumn c = new DataColumn("thing");
t.Columns.Add(c);
ds.Tables.Add(t);
DataRow r;

for (int i = 0; i < 10; i++) {


r = t.NewRow();
r[0] = "Thing " + i;
t.Rows.Add(r);
}

TextWriter writer = new StreamWriter(filename);


ser.Serialize(writer, ds);
writer.Close();
}

Serializando um XmlElement e um XmlNode


Você também pode serializar instâncias de uma classe XmlElement ou XmlNode,
conforme mostrado no seguinte exemplo de código:
C#

private void SerializeElement(string filename)


{
XmlSerializer ser = new XmlSerializer(typeof(XmlElement));
XmlElement myElement = new XmlDocument().CreateElement("MyElement",
"ns");
myElement.InnerText = "Hello World";
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, myElement);
writer.Close();
}

private void SerializeNode(string filename)


{
XmlSerializer ser = new XmlSerializer(typeof(XmlNode));
XmlNode myNode = new XmlDocument().
CreateNode(XmlNodeType.Element, "MyNode", "ns");
myNode.InnerText = "Hello Node";
TextWriter writer = new StreamWriter(filename);
ser.Serialize(writer, myNode);
writer.Close();
}

Serializando uma classe que contém um campo


que retorna um objeto complexo
Se uma propriedade ou um campo retornar um objeto complexo (como uma matriz ou
uma instância da classe), XmlSerializer o converterá em elemento aninhado dentro do
documento XML principal. Por exemplo, a primeira classe no seguinte exemplo de
código retorna uma instância da segunda classe:

C#

public class PurchaseOrder


{
public Address MyAddress;
}

public record Address


{
public string FirstName;
}

A saída XML serializada pode ter esta aparência:

XML
<PurchaseOrder>
<MyAddress>
<FirstName>George</FirstName>
</MyAddress>
</PurchaseOrder>

Serializando uma matriz de objetos


Você também pode serializar um campo que retorna uma matriz de objetos, conforme
mostrado no exemplo de código:

C#

public class PurchaseOrder


{
public Item [] ItemsOrders;
}

public class Item


{
public string ItemID;
public decimal ItemPrice;
}

Se dois itens forem ordenados, a instância da classe serializada poderá ser semelhante
ao seguinte código:

XML

<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ItemsOrders>
<Item>
<ItemID>aaa111</ItemID>
<ItemPrice>34.22</ItemPrice>
</Item>
<Item>
<ItemID>bbb222</ItemID>
<ItemPrice>2.89</ItemPrice>
</Item>
</ItemsOrders>
</PurchaseOrder>

Serializando uma classe que implementa a


interface ICollection
Você pode criar suas classes de coleção implementando a interface ICollection e usando
XmlSerializer para serializar as instâncias dessas classes.

7 Observação

Quando uma classe implementa a interface ICollection, somente a coleção contida


na classe é serializada. Propriedades ou campos públicos adicionados à classe não
serão serializados. Para ser serializada, a classe deve incluir um método Add e uma
propriedade Item (indexador C#).

C#

using System;
using System.Collections;
using System.IO;
using System.Xml.Serialization;

public class Test


{
static void Main()
{
Test t = new Test();
t.SerializeCollection("coll.xml");
}

private void SerializeCollection(string filename)


{
Employees Emps = new Employees();
// Note that only the collection is serialized -- not the
// CollectionName or any other public property of the class.
Emps.CollectionName = "Employees";
Employee John100 = new Employee("John", "100xxx");
Emps.Add(John100);
XmlSerializer x = new XmlSerializer(typeof(Employees));
TextWriter writer = new StreamWriter(filename);
x.Serialize(writer, Emps);
}
}

public class Employees : ICollection


{
public string CollectionName;
private ArrayList empArray = new ArrayList();

public Employee this[int index] => (Employee) empArray[index];

public void CopyTo(Array a, int index)


{
empArray.CopyTo(a, index);
}
public int Count => empArray.Count;

public object SyncRoot => this;

public bool IsSynchronized => false;

public IEnumerator GetEnumerator() => empArray.GetEnumerator();

public void Add(Employee newEmployee)


{
empArray.Add(newEmployee);
}
}

public class Employee


{
public string EmpName;
public string EmpID;

public Employee() {}

public Employee(string empName, string empID)


{
EmpName = empName;
EmpID = empID;
}
}

Exemplo de ordem de compra


Você pode recortar e colar o código de exemplo a seguir em um arquivo de texto
renomeá-lo com uma extensão de nome de arquivo .cs ou .vb. Use o compilador do C#
ou do Visual Basic para compilar o arquivo. Em seguida, execute-o usando o nome do
arquivo executável.

Este exemplo usa um cenário simples para demonstrar como uma instância de um
objeto é criada e serializada em um fluxo de arquivos usando o método Serialize. O
fluxo XML é salvo em um arquivo. O mesmo arquivo é lido novamente e reconstruído
em uma cópia do objeto original usando o método Deserialize.

Neste exemplo, uma classe denominada PurchaseOrder é serializada e desserializada.


Uma segunda classe denominada Address também é incluída porque o campo público
ShipTo deve ser definido como Address . Da mesma forma, uma classe OrderedItem é

incluída porque uma matriz de objetos OrderedItem deve ser definida para o campo
OrderedItems . Por fim, uma classe denominada Test contém o código que serializa e

desserializa as classes.
O método CreatePO cria os objetos de classe PurchaseOrder , Address e OrderedItem e
define os valores do campo público. O método também cria uma instância da classe
XmlSerializer que é usada para serializar e desserializar o PurchaseOrder .

7 Observação

O código passa o tipo de classe que será serializada para o construtor. O código
também cria um FileStream que é usado para gravar o fluxo XML em um
documento XML.

O método ReadPo é um pouco mais simples. Ele apenas cria os objetos a serem
desserializados e lê seus valores. Assim como acontece com o método CreatePo , você
deve primeiro construir um XmlSerializer, passando o tipo da classe a ser desserializada
para o construtor. Além disso, FileStream é necessário para ler o documento XML. Para
desserializar objetos, chame o método Deserialize com FileStream como um argumento.
O objeto desserializado deve ser convertido em um variável de objeto do tipo
PurchaseOrder . Em seguida, o código lê os valores de PurchaseOrder desserializado.

7 Observação

Você pode ler o arquivo PO.xml que é criado para ver a saída XML real.

C#

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

// The XmlRoot attribute allows you to set an alternate name


// (PurchaseOrder) for the XML element and its namespace. By
// default, the XmlSerializer uses the class name. The attribute
// also allows you to set the XML namespace for the element. Lastly,
// the attribute sets the IsNullable property, which specifies whether
// the xsi:null attribute appears if the class instance is set to
// a null reference.
[XmlRoot("PurchaseOrder", Namespace="http://www.cpandl.com",
IsNullable = false)]
public class PurchaseOrder
{
public Address ShipTo;
public string OrderDate;
// The XmlArray attribute changes the XML element name
// from the default of "OrderedItems" to "Items".
[XmlArray("Items")]
public OrderedItem[] OrderedItems;
public decimal SubTotal;
public decimal ShipCost;
public decimal TotalCost;
}

public class Address


{
// The XmlAttribute attribute instructs the XmlSerializer to serialize
the
// Name field as an XML attribute instead of an XML element (XML element
is
// the default behavior).
[XmlAttribute]
public string Name;
public string Line1;

// Setting the IsNullable property to false instructs the


// XmlSerializer that the XML attribute will not appear if
// the City field is set to a null reference.
[XmlElement(IsNullable = false)]
public string City;
public string State;
public string Zip;
}

public class OrderedItem


{
public string ItemName;
public string Description;
public decimal UnitPrice;
public int Quantity;
public decimal LineTotal;

// Calculate is a custom method that calculates the price per item


// and stores the value in a field.
public void Calculate()
{
LineTotal = UnitPrice * Quantity;
}
}

public class Test


{
public static void Main()
{
// Read and write purchase orders.
Test t = new Test();
t.CreatePO("po.xml");
t.ReadPO("po.xml");
}

private void CreatePO(string filename)


{
// Creates an instance of the XmlSerializer class;
// specifies the type of object to serialize.
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
TextWriter writer = new StreamWriter(filename);
PurchaseOrder po =new PurchaseOrder();

// Creates an address to ship and bill to.


Address billAddress = new Address();
billAddress.Name = "Teresa Atkinson";
billAddress.Line1 = "1 Main St.";
billAddress.City = "AnyTown";
billAddress.State = "WA";
billAddress.Zip = "00000";
// Sets ShipTo and BillTo to the same addressee.
po.ShipTo = billAddress;
po.OrderDate = System.DateTime.Now.ToLongDateString();

// Creates an OrderedItem.
OrderedItem i1 = new OrderedItem();
i1.ItemName = "Widget S";
i1.Description = "Small widget";
i1.UnitPrice = (decimal) 5.23;
i1.Quantity = 3;
i1.Calculate();

// Inserts the item into the array.


OrderedItem [] items = {i1};
po.OrderedItems = items;
// Calculate the total cost.
decimal subTotal = new decimal();
foreach(OrderedItem oi in items)
{
subTotal += oi.LineTotal;
}
po.SubTotal = subTotal;
po.ShipCost = (decimal) 12.51;
po.TotalCost = po.SubTotal + po.ShipCost;
// Serializes the purchase order, and closes the TextWriter.
serializer.Serialize(writer, po);
writer.Close();
}

protected void ReadPO(string filename)


{
// Creates an instance of the XmlSerializer class;
// specifies the type of object to be deserialized.
XmlSerializer serializer = new XmlSerializer(typeof(PurchaseOrder));
// If the XML document has been altered with unknown
// nodes or attributes, handles them with the
// UnknownNode and UnknownAttribute events.
serializer.UnknownNode+= new
XmlNodeEventHandler(serializer_UnknownNode);
serializer.UnknownAttribute+= new
XmlAttributeEventHandler(serializer_UnknownAttribute);

// A FileStream is needed to read the XML document.


FileStream fs = new FileStream(filename, FileMode.Open);
// Declares an object variable of the type to be deserialized.
PurchaseOrder po;
// Uses the Deserialize method to restore the object's state
// with data from the XML document. */
po = (PurchaseOrder) serializer.Deserialize(fs);
// Reads the order date.
Console.WriteLine ("OrderDate: " + po.OrderDate);

// Reads the shipping address.


Address shipTo = po.ShipTo;
ReadAddress(shipTo, "Ship To:");
// Reads the list of ordered items.
OrderedItem [] items = po.OrderedItems;
Console.WriteLine("Items to be shipped:");
foreach(OrderedItem oi in items)
{
Console.WriteLine("\t"+
oi.ItemName + "\t" +
oi.Description + "\t" +
oi.UnitPrice + "\t" +
oi.Quantity + "\t" +
oi.LineTotal);
}
// Reads the subtotal, shipping cost, and total cost.
Console.WriteLine(
"\n\t\t\t\t\t Subtotal\t" + po.SubTotal +
"\n\t\t\t\t\t Shipping\t" + po.ShipCost +
"\n\t\t\t\t\t Total\t\t" + po.TotalCost
);
}

protected void ReadAddress(Address a, string label)


{
// Reads the fields of the Address.
Console.WriteLine(label);
Console.Write("\t"+
a.Name +"\n\t" +
a.Line1 +"\n\t" +
a.City +"\t" +
a.State +"\n\t" +
a.Zip +"\n");
}

protected void serializer_UnknownNode


(object sender, XmlNodeEventArgs e)
{
Console.WriteLine("Unknown Node:" + e.Name + "\t" + e.Text);
}

protected void serializer_UnknownAttribute


(object sender, XmlAttributeEventArgs e)
{
System.Xml.XmlAttribute attr = e.Attr;
Console.WriteLine("Unknown attribute " +
attr.Name + "='" + attr.Value + "'");
}
}

A saída XML pode ter a seguinte aparência:

XML

<?xml version="1.0" encoding="utf-8"?>


<PurchaseOrder xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.cpandl.com">
<ShipTo Name="Teresa Atkinson">
<Line1>1 Main St.</Line1>
<City>AnyTown</City>
<State>WA</State>
<Zip>00000</Zip>
</ShipTo>
<OrderDate>Wednesday, June 27, 2001</OrderDate>
<Items>
<OrderedItem>
<ItemName>Widget S</ItemName>
<Description>Small widget</Description>
<UnitPrice>5.23</UnitPrice>
<Quantity>3</Quantity>
<LineTotal>15.69</LineTotal>
</OrderedItem>
</Items>
<SubTotal>15.69</SubTotal>
<ShipCost>12.51</ShipCost>
<TotalCost>28.2</TotalCost>
</PurchaseOrder>

Confira também
Apresentando a serialização XML
Controlando a serialização XML usando atributos
Atributos que controlam a serialização XML
Classe XmlSerializer
Como serializar um objeto
Como desserializar um objeto
A ferramenta de definição de esquema
XML e a serialização XML
Artigo • 10/05/2023

A ferramenta de Definição de Esquema XML (Ferramenta de Definição de Esquema XML


[Xsd.exe]) é instalada junto com as ferramentas do .NET Framework como parte do
Software Development Kit do Windows® (SDK do Windows). A ferramenta é criada
principalmente para duas finalidades:

Para gerar arquivos de classe C# ou Visual Basic que estejam em conformidade


com um esquema específico de linguagem XSD. A ferramenta usa um esquema
XML como um argumento e gera um arquivo que contém um número de classes
que, quando serializadas com o XmlSerializer, estão em conformidade com o
esquema. Para obter informações sobre como usar a ferramenta para gerar classes
em conformidade com um esquema específico, consulte Como usar a Ferramenta
de Definição de Esquema XML para gerar classes e XML Schema Documents.

Para gerar um documento de esquema XML de um arquivo .dll ou .exe. Para ver o
esquema de um conjunto de arquivos que você criou ou um que tenha sido
alterado com atributos, passe o DLL ou EXE como um argumento para a
ferramenta para gerar o esquema XML. Para obter informações sobre como usar a
ferramenta para gerar um XML Schema Document com base em um conjunto de
classes, consulte Como usar a Ferramenta de Definição de Esquema XML para
gerar classes e XML Schema Documents.

Para mais informações sobre como usar a ferramenta, confira Ferramenta de Definição
de Esquema XML (Xsd.exe).

Confira também
DataSet
Apresentando a serialização XML
Ferramenta de Definição de Esquema XML (Xsd.exe)
XmlSerializer
Como serializar um objeto
Como desserializar um objeto
Como usar a ferramenta de definição de esquema XML para gerar classes e
documentos de esquema XML
Suporte para associação do esquema XML
Controlar a serialização XML usando
atributos
Artigo • 08/06/2023

Os atributos podem ser usados para controlar a serialização XML de um objeto ou criar
um fluxo XML alternativo do mesmo conjunto de classes. Para obter mais informações
sobre como criar um fluxo XML alternativo, consulte Como especificar um nome de
elemento alternativo para um fluxo XML.

7 Observação

Se o XML gerado precisar estar em conformidade com a seção 5 do documento


intitulado Simple Object Access Protocol (SOAP) 1.1 (Protocolo SOAP 1.1) do
W3C (World Wide Web Consortium), use os atributos listados em Atributos que
controlam a serialização SOAP codificada.

Por padrão, um nome de elemento XML é determinado pela classe ou nome do


membro. Em uma classe chamada Book , um campo chamado ISBN produzirá uma
marcação de elemento XML <ISBN> , conforme mostrado seguinte exemplo:

C#

public class Book


{
public string ISBN;
}
// When an instance of the Book class is serialized, it might
// produce this XML:
// <ISBN>1234567890</ISBN>.

Esse comportamento padrão poderá ser modificado se você quiser dar ao elemento um
novo nome. O seguinte código mostra como um atributo habilita essa funcionalidade
definindo a propriedade ElementName de um XmlElementAttribute:

C#

public class TaxRates {


[XmlElement(ElementName = "TaxRate")]
public decimal ReturnTaxRate;
}
Para obter mais informações sobre atributos, consulte Atributos. Para obter uma lista de
atributos que controlam a serialização XML, consulte Atributos que controlam a
serialização XML.

Controlando a serialização de matriz


Os atributos XmlArrayAttribute e XmlArrayItemAttribute para controlam a serialização
de matrizes. Usando esses atributos, você pode controlar o nome do elemento, o
namespace e o tipo de dados XSD (esquema XML) conforme definido no documento do
W3C Parte 2 do esquema XML: tipos de dados . Você também pode especificar os
tipos que podem ser incluídos em uma matriz.

O XmlArrayAttribute determinará as propriedades do elemento XML incluído que


resulta quando uma matriz é serializada. Por exemplo, por padrão, serializar a matriz
abaixo resultará em um elemento XML denominado Employees . O elemento Employees
conterá uma série de elementos nomeados de acordo com o tipo de matriz Employee .

C#

public class Group {


public Employee[] Employees;
}
public class Employee {
public string Name;
}

Uma instância serializada pode ser semelhante ao seguinte código:

XML

<Group>
<Employees>
<Employee>
<Name>Haley</Name>
</Employee>
</Employees>
</Group>

Aplicando um XmlArrayAttribute, você pode alterar o nome do elemento XML, da


seguinte maneira:

C#

public class Group {


[XmlArray("TeamMembers")]
public Employee[] Employees;
}

O XML resultante pode ser semelhante ao seguinte código:

XML

<Group>
<TeamMembers>
<Employee>
<Name>Haley</Name>
</Employee>
</TeamMembers>
</Group>

O XmlArrayItemAttribute, por outro lado, controla como os itens contidos na matriz são
serializados.

7 Observação

O atributo é aplicado ao campo que retorna a matriz.

C#

public class Group {


[XmlArrayItem("MemberName")]
public Employee[] Employees;
}

O XML resultante pode ser semelhante ao seguinte código:

XML

<Group>
<Employees>
<MemberName>Haley</MemberName>
</Employees>
</Group>

Serializando classes derivadas


Outro uso do XmlArrayItemAttribute é permitir a serialização de classes derivadas. Por
exemplo, outra classe denominada Manager que é derivada de Employee pode ser
adicionada ao exemplo anterior. Se você não aplicar o XmlArrayItemAttribute, o código
falhará em tempo de execução porque o tipo da classe derivada não será reconhecido.
Para corrigir esse resultado, aplique o atributo duas vezes, cada vez que definir a
propriedade Type para cada tipo aceitável (base e derivada).

C#

public class Group {


[XmlArrayItem(Type = typeof(Employee)),
XmlArrayItem(Type = typeof(Manager))]
public Employee[] Employees;
}
public class Employee {
public string Name;
}
public class Manager:Employee {
public int Level;
}

Uma instância serializada pode ser semelhante ao seguinte código:

XML

<Group>
<Employees>
<Employee>
<Name>Haley</Name>
</Employee>
<Employee xsi:type = "Manager">
<Name>Ann</Name>
<Level>3</Level>
</Employee>
</Employees>
</Group>

Serializando uma matriz como uma sequência


de elementos
Você também pode serializar uma matriz como uma sequência plana dos elementos
XML aplicando um XmlElementAttribute ao campo que retorna a matriz da seguinte
maneira:

C#

public class Group {


[XmlElement]
public Employee[] Employees;
}
Uma instância serializada pode ser semelhante ao seguinte código:

XML

<Group>
<Employees>
<Name>Haley</Name>
</Employees>
<Employees>
<Name>Noriko</Name>
</Employees>
<Employees>
<Name>Marco</Name>
</Employees>
</Group>

Outra maneira para diferenciar os dois fluxos XML é usar a Ferramenta de Definição de
Esquema XML para gerar os arquivos de documento de esquema XSD de código
compilado. Para obter mais informações sobre como usar a ferramenta, consulte A
ferramenta de definição de esquema XML e a serialização XML. Quando nenhum
atributo é aplicado ao campo, o esquema descreve o elemento da seguinte maneira:

XML

<xs:element minOccurs="0" maxOccurs ="1" name="Employees"


type="ArrayOfEmployee" />

Quando o XmlElementAttribute é aplicado ao campo, o esquema resultante descreve o


elemento da seguinte maneira:

XML

<xs:element minOccurs="0" maxOccurs="unbounded" name="Employees"


type="Employee" />

Serializando um ArrayList
A classe ArrayList pode conter uma coleção de vários objetos. Você pode, portanto, usar
um ArrayList da mesma maneira que usa uma matriz. Em vez de criar um campo que
retorna uma matriz de objetos tipados, no entanto, você pode criar um campo que
retorna um único ArrayList. No entanto, da mesma forma que ocorre com matrizes, você
deverá informar o XmlSerializer dos tipos de objetos que o ArrayList contém. Para fazer
isso, atribua várias instâncias do XmlElementAttribute ao campo, conforme mostrado no
exemplo a seguir.

C#

public class Group {


[XmlElement(Type = typeof(Employee)),
XmlElement(Type = typeof(Manager))]
public ArrayList Info;
}

Controlando a serialização de classes usando


XmlRootAttribute e XmlTypeAttribute
Você pode aplicar dois atributos somente a uma classe: XmlRootAttribute e
XmlTypeAttribute. Esses atributos são semelhantes. O XmlRootAttribute pode ser
aplicado somente a uma classe: a classe que, quando serializada, representa o elemento
de abertura e fechamento do documento XML. Em outras palavras, o elemento raiz. O
XmlTypeAttribute, por outro lado, pode ser aplicado a qualquer classe, incluindo a classe
raiz.

Por exemplo, nos exemplos anteriores, a classe Group é a classe raiz, e todos os seus
campos públicos e propriedades tornam-se elementos XML encontrados no documento
XML. Portanto, você pode ter apenas uma classe raiz. Aplicando o XmlRootAttribute,
você pode controlar o fluxo XML produzido pelo XmlSerializer. Por exemplo, você pode
alterar o nome e o namespace do elemento.

O XmlTypeAttribute permite que você controle o esquema do XML gerado. Esse recurso
é útil quando você precisa publicar o esquema por um serviço Web XML. O seguinte
exemplo aplica o XmlTypeAttribute e o XmlRootAttribute à mesma classe:

C#

[XmlRoot("NewGroupName")]
[XmlType("NewTypeName")]
public class Group {
public Employee[] Employees;
}

Se essa classe for criada, e a ferramenta de Definição de Esquema XML for usada para
gerar seu esquema, você descobriria o seguinte XML descrevendo o Group :

XML
<xs:element name="NewGroupName" type="NewTypeName" />

Por outro lado, se você quisesse serializar uma instância da classe, somente
NewGroupName seria encontrado no documento XML:

XML

<NewGroupName>
. . .
</NewGroupName>

Evitando a serialização com o


XmlIgnoreAttribute
Você pode se deparar com uma situação em que uma propriedade ou campo público
não precisa ser serializado. Por exemplo, um campo ou propriedade podem ser usados
para conter metadados. Nesses casos, aplicar o XmlIgnoreAttribute ao campo ou
propriedade e o XmlSerializer o ignorarão.

Confira também
Atributos que controlam a serialização XML
Atributos que controlam a serialização SOAP codificada
Apresentando a serialização XML
Exemplos de Serialização XML
Como especificar um nome de elemento alternativo para um fluxo XML
Como serializar um objeto
Como desserializar um objeto
Atributos que controlam a serialização
XML
Artigo • 03/04/2024

Você pode aplicar os atributos na tabela a seguir para classes e membros de classe para
controlar a maneira pela qual o XmlSerializer serializa ou desserializa uma instância da
classe. Para entender como esses atributos controlam a serialização XML, consulte
Controlando a serialização XML usando atributos.

Esses atributos também podem ser usados para controlar as mensagens SOAP literais
de estilo geradas por um serviço Web XML. Para obter mais informações sobre como
aplicar esses atributos a um método de serviços Web XML, consulte Serialização XML
com serviços Web XML.

Para obter mais informações sobre atributos, consulte Atributos.

ノ Expandir a tabela

Atributo Aplica-se a Especifica

XmlAnyAttributeAttribute O valor do campo público, Ao desserializar, a matriz será


propriedade, parâmetro ou preenchida com objetos
retorno que retorna uma XmlAttribute que representam todos
matriz de objetos os atributos XML desconhecidos do
XmlAttribute. esquema.

XmlAnyElementAttribute O valor do campo público, Ao desserializar, a matriz será


propriedade, parâmetro ou preenchida com objetos XmlElement
retorno que retorna uma que representam todos os
matriz de objetos elementos XML desconhecidos do
XmlElement. esquema.

XmlArrayAttribute O campo público, Os membros da matriz serão


propriedade, parâmetro ou gerados como membros de uma
valor de retorno que matriz XML.
retorna uma matriz de
objetos complexos.

XmlArrayItemAttribute O campo público, Os tipos derivados que podem ser


propriedade, parâmetro ou inseridos em uma matriz.
valor de retorno que Geralmente aplicado em conjunto
retorna uma matriz de com um XmlArrayAttribute.
objetos complexos.

XmlAttributeAttribute Campo público, O membro será serializado como um


propriedade, parâmetro ou atributo XML.
Atributo valor de retorno.
Aplica-se a Especifica

XmlChoiceIdentifierAttribute Campo público, O membro pode ter a ambiguidade


propriedade, parâmetro ou removida usando uma enumeração.
valor de retorno.

XmlElementAttribute Campo público, O campo ou propriedade serão


propriedade, parâmetro ou serializados como um elemento
valor de retorno. XML.

XmlEnumAttribute O campo público que é um O nome do elemento de um


identificador de membro de enumeração.
enumeração.

XmlIgnoreAttribute Propriedades públicas e A propriedade ou campo devem ser


campos. ignorados quando a classe
recipiente é serializada.

XmlIncludeAttribute Declarações públicas de A classe deve ser incluída ao gerar


classe derivada e valores esquemas (para serem reconhecidos
de retorno de métodos quando serializados).
públicos para documentos
da linguagem WSDL.

XmlRootAttribute Declarações públicas de Controla a serialização XML do


classe. destino do atributo como um
elemento raiz XML. Use o atributo
para especificar ainda mais o
namespace e o nome do elemento.

XmlTextAttribute Propriedades públicas e A propriedade ou o campo devem


campos. ser serializados como texto XML.

XmlTypeAttribute Declarações públicas de O nome e o namespace do tipo


classe. XML.

ObsoleteAttribute Propriedades públicas e A propriedade ou campo serão


campos. ignorados quando a classe
recipiente for serializada.

Além desses atributos, que são todos encontrados no namespace


System.Xml.Serialization, você também pode aplicar o atributo DefaultValueAttribute a
um campo. O DefaultValueAttribute definirá o valor que será atribuído
automaticamente ao membro se nenhum valor for especificado.

Para controlar a serialização XML de SOAP codificada, consulte Atributos que controlam
a serialização SOAP codificada.
Confira também
Serialização XML e SOAP
XmlSerializer
Controlando a serialização XML usando atributos
Como especificar um nome de elemento alternativo para um fluxo XML
Como serializar um objeto
Como desserializar um objeto

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Serialização XML com Serviços Web
XML
Artigo • 03/04/2023

A serialização XML é o mecanismo de transporte subjacente utilizado na arquitetura de


serviços Web XML, executado pela classe XmlSerializer. Para controlar o XML gerado por
um serviço Web XML, aplique os atributos listados em Atributos que controlam a
serialização XML e Atributos que controlam a serialização SOAP codificada às classes,
aos valores retornados, aos parâmetros e aos campos de um arquivo usado para criar
um serviço Web XML (.asmx). Para obter mais informações sobre como criar um serviço
Web XML, consulte Serviços Web XML usando o ASP.NET.

Estilos literais e codificados


O XML gerado por um serviço Web XML pode ser formatado de uma das duas
maneiras, literal ou codificado, conforme explicado em Personalizar formatação de
mensagens SOAP. Portanto, há dois conjuntos de atributos que controlam a serialização
XML. Os atributos listados em Atributos que controlam a serialização XML foram
projetados para controlar o XML de estilo literal. Os atributos listados em Atributos que
controlam a serialização SOAP codificada controlam o estilo codificado. Aplicando
seletivamente esses atributos, você pode personalizar um aplicativo para retornar
qualquer um ou ambos os estilos. Além disso, esses atributos podem ser aplicados
(conforme apropriado) a valores e parâmetros de retorno.

Exemplo de como usar os dois estilos


Quando estiver criando um serviço Web XML, você pode usar os dois conjuntos de
atributos nos métodos. No exemplo de código a seguir, a classe denominada MyService
contém dois métodos de serviço Web XML, MyLiteralMethod e MyEncodedMethod . Os dois
métodos executam a mesma função: retornando uma instância da classe Order . Na
classe Order , os atributos XmlTypeAttribute e SoapTypeAttribute são aplicados ao
campo OrderID e ambos têm sua propriedade ElementName definida com valores
diferentes.

Para executar o exemplo, cole o código em um arquivo com uma extensão .asmx e
coloque o arquivo em um diretório virtual gerenciado pelo IIS (Serviços de Informações
da Internet). Em um navegador HTML, digite o nome do computador, o diretório virtual
e o arquivo.
C#

<%@ WebService Language="C#" Class="MyService" %>


using System;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Serialization;
public class Order {
// Both types of attributes can be applied. Depending on which type
// the method used, either one will affect the call.
[SoapElement(ElementName = "EncodedOrderID")]
[XmlElement(ElementName = "LiteralOrderID")]
public String OrderID;
}
public class MyService {
[WebMethod][SoapDocumentMethod]
public Order MyLiteralMethod(){
Order myOrder = new Order();
return myOrder;
}
[WebMethod][SoapRpcMethod]
public Order MyEncodedMethod(){
Order myOrder = new Order();
return myOrder;
}
}

O exemplo de código a seguir chama MyLiteralMethod . O nome do elemento é alterado


para "LiteralOrderID".

XML

<?xml version="1.0" encoding="utf-8"?>


<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<MyLiteralMethodResponse xmlns="http://tempuri.org/">
<MyLiteralMethodResult>
<LiteralOrderID>string</LiteralOrderID>
</MyLiteralMethodResult>
</MyLiteralMethodResponse>
</soap:Body>
</soap:Envelope>

O exemplo de código a seguir chama MyEncodedMethod . O nome do elemento é


"EncodedOrderID".

XML
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://tempuri.org/"
xmlns:types="http://tempuri.org/encodedTypes"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<tns:MyEncodedMethodResponse>
<MyEncodedMethodResult href="#id1" />
</tns:MyEncodedMethodResponse>
<types:Order id="id1" xsi:type="types:Order">
<EncodedOrderID xsi:type="xsd:string">string</EncodedOrderID>
</types:Order>
</soap:Body>
</soap:Envelope>

Aplicando atributos a valores de retorno


Você também pode aplicar atributos a valores de retorno para controlar o namespace, o
nome do elemento e assim por diante. O exemplo de código a seguir aplica o atributo
XmlElementAttribute ao valor de retorno do método MyLiteralMethod . Fazer isso
permite controlar o namespace e o nome do elemento.

C#

[return: XmlElement(Namespace = "http://www.cohowinery.com",


ElementName = "BookOrder")]
[WebMethod][SoapDocumentMethod]
public Order MyLiteralMethod(){
Order myOrder = new Order();
return myOrder;
}

Quando chamado, o código retorna XML semelhante ao seguinte.

XML

<?xml version="1.0" encoding="utf-8"?>


<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<MyLiteralMethodResponse xmlns="http://tempuri.org/">
<BookOrder xmlns="http://www.cohowinery.com">
<LiteralOrderID>string</LiteralOrderID>
</BookOrder>
</MyLiteralMethodResponse>
</soap:Body>
</soap:Envelope>

Atributos aplicados a parâmetros


Você também pode aplicar atributos a parâmetros para especificar o namespace, o
nome do elemento e assim por diante. O exemplo de código a seguir adiciona um
parâmetro ao método MyLiteralMethodResponse e aplica o atributo
XmlAttributeAttribute ao parâmetro. O nome e o namespace do elemento são
definidos para o parâmetro.

C#

[return: XmlElement(Namespace = "http://www.cohowinery.com",


ElementName = "BookOrder")]
[WebMethod][SoapDocumentMethod]
public Order MyLiteralMethod([XmlElement("MyOrderID",
Namespace="http://www.microsoft.com")] string ID){
Order myOrder = new Order();
myOrder.OrderID = ID;
return myOrder;
}

A solicitação SOAP deve ser semelhante ao seguinte.

XML

<?xml version="1.0" encoding="utf-8"?>


<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
<soap:Body>
<MyLiteralMethod xmlns="http://tempuri.org/">
<MyOrderID xmlns="http://www.microsoft.com">string</MyOrderID>
</MyLiteralMethod>
</soap:Body>
</soap:Envelope>

Aplicando atributos a classes


Se você precisar controlar o namespace dos elementos que são correlacionados a
classes, poderá aplicar XmlTypeAttribute , XmlRootAttribute e SoapTypeAttribute ,
conforme apropriado. O exemplo de código a seguir aplica todos os três à classe Order .
C#

[XmlType("BigBooksService", Namespace = "http://www.cpandl.com")]


[SoapType("SoapBookService")]
[XmlRoot("BookOrderForm")]
public class Order {
// Both types of attributes can be applied. Depending on which
// the method used, either one will affect the call.
[SoapElement(ElementName = "EncodedOrderID")]
[XmlElement(ElementName = "LiteralOrderID")]
public String OrderID;
}

Os resultados da aplicação de XmlTypeAttribute e de SoapTypeAttribute podem ser


vistos quando você examina a descrição do serviço, conforme mostrado no exemplo de
código a seguir.

XML

<s:element name="BookOrderForm" type="s0:BigBookService" />


<s:complexType name="BigBookService">
<s:sequence>
<s:element minOccurs="0" maxOccurs="1" name="LiteralOrderID"
type="s:string" />
</s:sequence>

<s:schema targetNamespace="http://tempuri.org/encodedTypes">
<s:complexType name="SoapBookService">
<s:sequence>
<s:element minOccurs="1" maxOccurs="1" name="EncodedOrderID"
type="s:string" />
</s:sequence>
</s:complexType>
</s:schema>
</s:complexType>

O efeito do XmlRootAttribute também pode ser visto nos resultados de HTTP GET e
HTTP POST, da seguinte maneira.

XML

<?xml version="1.0" encoding="utf-8"?>


<BookOrderForm xmlns="http://tempuri.org/">
<LiteralOrderID>string</LiteralOrderID>
</BookOrderForm>

Confira também
Serialização XML e SOAP
Atributos que controlam a serialização SOAP codificada
Como serializar um objeto como um fluxo XML codificado para SOAP
Como substituir a serialização XML de SOAP codificada
Apresentando a serialização XML
Como serializar um objeto
Como desserializar um objeto
Atributos que controlam a serialização
SOAP codificada
Artigo • 08/06/2023

O documento do W3C (World Wide Web Consortium) intitulado Protocolo SOAP 1.1
contém uma seção opcional (seção 5) que descreve como os parâmetros SOAP podem
ser codificados. Para estar em conformidade com a seção 5 da especificação, é
necessário usar um conjunto especial de atributos encontrados no namespace
System.Xml.Serialization. Aplique esses atributos conforme for apropriado a classes e
membros das classes e, em seguida, use o XmlSerializer para serializar instâncias da
classe ou classes.

A tabela a seguir mostra os atributos, onde podem ser aplicados e o que eles fazem.
Para obter mais informações sobre como usar esses atributos para controlar a
serialização XML, consulte Como serializar um objeto como um fluxo XML codificado
em SOAP e Como substituir a serialização XML de SOAP codificada.

Para obter mais informações sobre atributos, consulte Atributos.

Atributo Aplica-se a Especifica

SoapAttributeAttribute Campo público, propriedade, O membro da classe será


parâmetro ou valor de retorno. serializado como um atributo
XML.

SoapElementAttribute Campo público, propriedade, A classe será serializada como


parâmetro ou valor de retorno. um elemento XML.

SoapEnumAttribute O campo público que é um O nome do elemento de um


identificador de enumeração. membro de enumeração.

SoapIgnoreAttribute Propriedades públicas e campos. A propriedade ou campo devem


ser ignorados quando a classe
recipiente é serializada.

SoapIncludeAttribute Declarações públicas de classe O tipo deve ser incluído ao gerar


derivada e métodos públicos para esquemas (para serem
documentos da linguagem WSDL. reconhecidos quando
serializados).

SoapTypeAttribute Declarações públicas de classe. A classe deverá ser serializada


como um tipo XML.

Confira também
Serialização XML e SOAP
Como serializar um objeto como um fluxo XML codificado para SOAP
Como substituir a serialização XML de SOAP codificada
Atributos
XmlSerializer
Como serializar um objeto
Como desserializar um objeto
Como serializar um objeto
Artigo • 07/04/2023

Para serializar um objeto, primeiro crie o objeto a ser serializado e defina seus campos e
propriedades públicos. Para fazer isso, você deve determinar o formato de transporte
em que o fluxo XML deve ser armazenado: como um fluxo ou como um arquivo. Por
exemplo, se o fluxo XML precisar ser salvo de uma forma permanente, crie um objeto
FileStream.

7 Observação

Para obter mais exemplos de serialização XML, consulte Exemplos de Serialização


XML.

Para serializar um objeto


1. Crie o objeto e defina seus campos e propriedades públicos.

2. Construa um XmlSerializer usando o tipo do objeto. Para obter mais informações,


consulte os construtores da classe XmlSerializer.

3. Chame o método Serialize para gerar um fluxo XML ou uma representação em


arquivo de propriedades e campos públicos do objeto. O exemplo a seguir cria um
arquivo.

C#

MySerializableClass myObject = new MySerializableClass();


// Insert code to set properties and fields of the object.
XmlSerializer mySerializer = new
XmlSerializer(typeof(MySerializableClass));
// To write to a file, create a StreamWriter object.
StreamWriter myWriter = new StreamWriter("myFileName.xml");
mySerializer.Serialize(myWriter, myObject);
myWriter.Close();

Confira também
Apresentando a serialização XML
Como desserializar um objeto
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como desserializar um objeto usando
XmlSerializer
Artigo • 07/04/2023

Quando você desserializar um objeto, o formato do transporte determina se você criará


um fluxo ou objeto de arquivo. Após o formato do transporte ser determinado, você
poderá chamar os métodos Serialize ou Deserialize, conforme o necessário.

Para desserializar um objeto


1. Construa um XmlSerializer usando o tipo do objeto para desserializar.

2. Chame o método Deserialize para gerar uma réplica do objeto. Ao desserializar,


será necessário converter o objeto retornado para o tipo do original, conforme
mostrado no exemplo a seguir, que desserializará o objeto de um arquivo (embora
também possa ser desserializado de um fluxo).

C#

// Construct an instance of the XmlSerializer with the type


// of object that is being deserialized.
var mySerializer = new XmlSerializer(typeof(MySerializableClass));
// To read the file, create a FileStream.
using var myFileStream = new FileStream("myFileName.xml",
FileMode.Open);
// Call the Deserialize method and cast to the object type.
var myObject =
(MySerializableClass)mySerializer.Deserialize(myFileStream);

Confira também
Apresentando a serialização XML
Como serializar um objeto
Como usar a ferramenta de definição de
esquema XML para gerar classes e
documentos de esquema XML
Artigo • 05/06/2023

A ferramenta de Definição de Esquema XML (Xsd.exe) permite gerar um esquema XML


que descreve uma classe ou gerar a classe definida por um esquema XML. Os seguintes
procedimentos mostram como executar essas operações.

A ferramenta de Definição de Esquema XML (Xsd.exe) geralmente pode ser encontrada


no seguinte caminho:
C:\Program Files (x86)\Microsoft SDKs\Windows\{version}\bin\NETFX {version} Tools\

Para gerar classes que estão em conformidade


com um esquema específico
1. Abra um prompt de comando.

2. Passe o esquema XML como um argumento para a ferramenta de definição de


esquema XML, que cria um conjunto de classes que correspondem precisamente
ao Esquema XML, por exemplo:

Console

xsd mySchema.xsd

A ferramenta somente pode processar esquemas que fazem referência à


especificação de XML World Wide Web Consortium de 16 de março de 2001. Em
outras palavras, o namespace do Esquema XML deve ser
"http://www.w3.org/2001/XMLSchema" , conforme mostrado no exemplo a seguir.

XML

<?xml version="1.0" encoding="utf-8"?>


<xs:schema attributeFormDefault="qualified"
elementFormDefault="qualified" targetNamespace=""
xmlns:xs="http://www.w3.org/2001/XMLSchema" />
3. Modifique as classes com métodos, propriedades ou campos, conforme o
necessário. Para obter mais informações sobre como modificar uma classe com
atributos, consulte Controlando a serialização XML usando atributos e Atributos
que controlam a serialização SOAP codificada.

É geralmente útil examinar o esquema do fluxo de XML que é gerado quando instâncias
de uma classe (ou classes) são serializadas. Por exemplo, você pode publicar seu
esquema para outros usarem ou pode compará-lo com um esquema com o qual está
tentando obter conformidade.

Para gerar um documento de esquema XML de


um conjunto de classes
1. Compile uma classe ou classes em uma DLL.

2. Abra um prompt de comando.

3. Passe a DLL como argumento para Xsd.exe, por exemplo:

Console

xsd MyFile.dll

O esquema (ou esquemas) serão escritos, começando com o nome "schema0.xsd".

Confira também
DataSet
A ferramenta de definição de esquema XML e a serialização XML
Apresentando a serialização XML
Ferramenta de Definição de Esquema XML (Xsd.exe)
XmlSerializer
Como serializar um objeto
Como desserializar um objeto
Como controlar a serialização de classes
derivadas
Artigo • 07/04/2023

O uso do atributo XmlElementAttribute para alterar o nome de um elemento XML não


é a única maneira de personalizar a serialização de objetos. Você também pode
personalizar o fluxo XML derivando de uma classe existente e instruindo a instância de
XmlSerializer sobre como serializar a nova classe.

Por exemplo, dada uma classe Book , você pode derivar dela e criar uma classe
ExpandedBook que tem algumas propriedades a mais. Entretanto, é necessário instruir o
XmlSerializer a aceitar o tipo derivado durante a serialização ou desserialização. Isso
pode ser feito criando uma instância do XmlElementAttribute e definindo sua
propriedade Type com o tipo da classe derivada. Adicione o XmlElementAttribute a
uma instância XmlAttributes. Em seguida, adicione XmlAttributes a uma instância do
XmlAttributeOverrides, especificando o tipo que está sendo substituído e o nome do
membro que aceita a classe derivada. Isso é mostrado no exemplo a seguir.

Exemplo
C#

public class Orders


{
public Book[] Books;
}

public class Book


{
public string ISBN;
}

public class ExpandedBook:Book


{
public bool NewEdition;
}

public class Run


{
public void SerializeObject(string filename)
{
// Each overridden field, property, or type requires
// an XmlAttributes instance.
XmlAttributes attrs = new XmlAttributes();
// Creates an XmlElementAttribute instance to override the
// field that returns Book objects. The overridden field
// returns Expanded objects instead.
XmlElementAttribute attr = new XmlElementAttribute();
attr.ElementName = "NewBook";
attr.Type = typeof(ExpandedBook);

// Adds the element to the collection of elements.


attrs.XmlElements.Add(attr);

// Creates the XmlAttributeOverrides instance.


XmlAttributeOverrides attrOverrides = new XmlAttributeOverrides();

// Adds the type of the class that contains the overridden


// member, as well as the XmlAttributes instance to override it
// with, to the XmlAttributeOverrides.
attrOverrides.Add(typeof(Orders), "Books", attrs);

// Creates the XmlSerializer using the XmlAttributeOverrides.


XmlSerializer s =
new XmlSerializer(typeof(Orders), attrOverrides);

// Writing the file requires a TextWriter instance.


TextWriter writer = new StreamWriter(filename);

// Creates the object to be serialized.


Orders myOrders = new Orders();

// Creates an object of the derived type.


ExpandedBook b = new ExpandedBook();
b.ISBN= "123456789";
b.NewEdition = true;
myOrders.Books = new ExpandedBook[]{b};

// Serializes the object.


s.Serialize(writer,myOrders);
writer.Close();
}

public void DeserializeObject(string filename)


{
XmlAttributeOverrides attrOverrides =
new XmlAttributeOverrides();
XmlAttributes attrs = new XmlAttributes();

// Creates an XmlElementAttribute to override the


// field that returns Book objects. The overridden field
// returns Expanded objects instead.
XmlElementAttribute attr = new XmlElementAttribute();
attr.ElementName = "NewBook";
attr.Type = typeof(ExpandedBook);

// Adds the XmlElementAttribute to the collection of objects.


attrs.XmlElements.Add(attr);
attrOverrides.Add(typeof(Orders), "Books", attrs);

// Creates the XmlSerializer using the XmlAttributeOverrides.


XmlSerializer s =
new XmlSerializer(typeof(Orders), attrOverrides);

FileStream fs = new FileStream(filename, FileMode.Open);


Orders myOrders = (Orders) s.Deserialize(fs);
Console.WriteLine("ExpandedBook:");

// The difference between deserializing the overridden


// XML document and serializing it is this: To read the derived
// object values, you must declare an object of the derived type
// and cast the returned object to it.
ExpandedBook expanded;
foreach(Book b in myOrders.Books)
{
expanded = (ExpandedBook)b;
Console.WriteLine(
expanded.ISBN + "\n" +
expanded.NewEdition);
}
}
}

Confira também
XmlSerializer
XmlElementAttribute
XmlAttributes
XmlAttributeOverrides
Serialização XML e SOAP
Como serializar um objeto
Como especificar um nome de elemento alternativo para um fluxo XML
Como especificar um nome de elemento
alternativo para um fluxo XML
Artigo • 07/04/2023

Usando o XmlSerializer, você pode gerar mais de um fluxo de XML com o mesmo
conjunto de classes. Você deve querer fazer isso porque dois diferentes serviços Web
XML exigem as mesmas informações básicas, com apenas poucas diferenças. Por
exemplo, imagine dois serviços Web XML que processam pedidos para livros e,
portanto, exigem números ISBN. Um serviço usa a marca <ISBN>, enquanto o segundo
usa a marca <BookID>. Você tem uma classe nomeada Book que contém um campo
nomeado ISBN . Quando uma instância da classe Book é serializada, ela, por padrão,
usará o nome de membro (ISBN) como o nome de elemento da marca. Para o primeiro
serviço Web XML, esse é o esperado. Mas, para enviar o fluxo XML para o segundo
serviço Web XML, você deverá sobrescrever a serialização para que o nome do
elemento da marca seja BookID .

Para criar um fluxo XML com um nome de


elemento alternativo
1. Criar uma instância da classe XmlElementAttribute.

2. Defina o ElementName do XmlElementAttribute como "BookID".

3. Criar uma instância da classe XmlAttributes.

4. Adicione o objeto XmlElementAttribute à coleção acessada por meio da


propriedade XmlElements de XmlAttributes.

5. Criar uma instância da classe XmlAttributeOverrides.

6. Adicione o XmlAttributes ao XmlAttributeOverrides, passando o tipo do objeto


para sobrescrever e o nome do membro que está sendo sobrescrito.

7. Crie uma instância da classe XmlSerializer com XmlAttributeOverrides .

8. Crie uma instância da classe Book e serialize-a ou desserialize-a.

Exemplo
C#
public void SerializeOverride()
{
// Creates an XmlElementAttribute with the alternate name.
XmlElementAttribute myElementAttribute = new XmlElementAttribute();
myElementAttribute.ElementName = "BookID";
XmlAttributes myAttributes = new XmlAttributes();
myAttributes.XmlElements.Add(myElementAttribute);
XmlAttributeOverrides myOverrides = new XmlAttributeOverrides();
myOverrides.Add(typeof(Book), "ISBN", myAttributes);
XmlSerializer mySerializer =
new XmlSerializer(typeof(Book), myOverrides);
Book b = new Book();
b.ISBN = "123456789";
// Creates a StreamWriter to write the XML stream to.
StreamWriter writer = new StreamWriter("Book.xml");
mySerializer.Serialize(writer, b);
}

O fluxo XML pode ter a seguinte aparência.

XML

<Book>
<BookID>123456789</BookID>
</Book>

Confira também
XmlElementAttribute
XmlAttributes
XmlAttributeOverrides
Serialização XML e SOAP
XmlSerializer
Como serializar um objeto
Como desserializar um objeto
Como qualificar nomes de elementos
XML e de atributos XML
Artigo • 08/06/2023

Os namespaces XML contidos por instâncias da classe XmlSerializerNamespaces devem


estar em conformidade com a especificação do W3C (World Wide Web Consortium)
chamada Namespaces em XML .

Os namespaces em XML fornecem um método para qualificar os nomes de elementos


XML e atributos XML em documentos XML. Um nome qualificado é composto por um
prefixo e por um nome local separados por dois-pontos. O prefixo funciona somente
como espaço reservado; é mapeado para um URI que especifica um namespace. A
combinação do namespace de URI gerenciado universalmente e o nome local produz
um nome que é garantido para ser exclusivo universalmente.

Ao criar uma instância do XmlSerializerNamespaces e adicionar os pares de namespace


ao objeto, você pode especificar os prefixos usados em um documento XML.

Para criar nomes qualificados em um


documento XML
1. Criar uma instância da classe XmlSerializerNamespaces .

2. Adicione todos os prefixos e pares de namespace ao XmlSerializerNamespaces .

3. Aplique o atributo System.Xml.Serialization apropriado a cada membro ou classe


que o XmlSerializer deve serializar em um documento XML.

Os atributos disponíveis são: XmlAnyElementAttribute, XmlArrayAttribute,


XmlArrayItemAttribute, XmlAttributeAttribute, XmlElementAttribute,
XmlRootAttribute e XmlTypeAttribute.

4. Configure a propriedade Namespace de cada atributo para um dos valores de


namespace do XmlSerializerNamespaces .

5. Passe o XmlSerializerNamespaces para o método Serialize do XmlSerializer .

Exemplo
O exemplo a seguir cria um XmlSerializerNamespaces e adiciona dois prefixos e pares de
namespace ao objeto. O código cria um XmlSerializer que é usado para serializar uma
instância da classe Books . O código chama o método Serialize com o
XmlSerializerNamespaces , permitindo que o XML contenha namespaces com prefixo.

C#

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;

public class Program


{
public static void Main()
{
SerializeObject("XmlNamespaces.xml");
}

public static void SerializeObject(string filename)


{
var mySerializer = new XmlSerializer(typeof(Books));
// Writing a file requires a TextWriter.
TextWriter myWriter = new StreamWriter(filename);

// Creates an XmlSerializerNamespaces and adds two


// prefix-namespace pairs.
var myNamespaces = new XmlSerializerNamespaces();
myNamespaces.Add("books", "http://www.cpandl.com");
myNamespaces.Add("money", "http://www.cohowinery.com");

// Creates a Book.
var myBook = new Book();
myBook.TITLE = "A Book Title";
var myPrice = new Price();
myPrice.price = (decimal) 9.95;
myPrice.currency = "US Dollar";
myBook.PRICE = myPrice;
var myBooks = new Books();
myBooks.Book = myBook;
mySerializer.Serialize(myWriter, myBooks, myNamespaces);
myWriter.Close();
}
}

public class Books


{
[XmlElement(Namespace = "http://www.cohowinery.com")]
public Book Book;
}

[XmlType(Namespace ="http://www.cpandl.com")]
public class Book
{
[XmlElement(Namespace = "http://www.cpandl.com")]
public string TITLE;
[XmlElement(Namespace ="http://www.cohowinery.com")]
public Price PRICE;
}

public class Price


{
[XmlAttribute(Namespace = "http://www.cpandl.com")]
public string currency;
[XmlElement(Namespace = "http://www.cohowinery.com")]
public decimal price;
}

Confira também
XmlSerializer
A ferramenta de definição de esquema XML e a serialização XML
Apresentando a serialização XML
Classe XmlSerializer
Atributos que controlam a serialização XML
Como especificar um nome de elemento alternativo para um fluxo XML
Como serializar um objeto
Como desserializar um objeto
Como serializar um objeto como um
fluxo XML codificado para SOAP
Artigo • 08/06/2023

Como a mensagem de SOAP é criada usando XML, a classe XmlSerializer pode ser
usado para serializar classes e gerar mensagens SOAP codificadas. O XML resultante
está em conformidade com a seção 5 do documento "Protocolo SOAP 1.1" do World
Wide Web Consortium . Quando você está criando um serviço Web XML que se
comunica por meio de mensagens SOAP, pode personalizar o fluxo XML aplicando um
conjunto de atributos SOAP especiais para classes e membros de classes. Para obter
mais informações, consulte Atributos que controlam a serialização SOAP codificada.

Para serializar um objeto como um fluxo XML codificado


para SOAP
1. Criar a classe usando a Ferramenta de Definição de Esquema XML (Xsd.exe).

2. Aplique um ou mais dos atributos especiais localizados em


System.Xml.Serialization . Consulte a lista em "Atributos que controlam a

serialização SOAP codificada".

3. Crie um XmlTypeMapping criando um novo SoapReflectionImporter e invocando o


método ImportTypeMapping com o tipo da classe serializada.

O exemplo de código a seguir chama o método ImportTypeMapping da classe


SoapReflectionImporter para criar um XmlTypeMapping .

C#

// Serializes a class named Group as a SOAP message.


XmlTypeMapping myTypeMapping =
new SoapReflectionImporter().ImportTypeMapping(typeof(Group));

4. Cria uma instância da classe XmlSerializer passando o XmlTypeMapping para o


construtor XmlSerializer(XmlTypeMapping).

C#

XmlSerializer mySerializer = new XmlSerializer(myTypeMapping);


5. Chame o método Serialize or Deserialize .

Exemplo
C#

// Serializes a class named Group as a SOAP message.


XmlTypeMapping myTypeMapping =
new SoapReflectionImporter().ImportTypeMapping(typeof(Group));
XmlSerializer mySerializer = new XmlSerializer(myTypeMapping);

Confira também
Serialização XML e SOAP
Atributos que controlam a serialização SOAP codificada
Serialização XML com Serviços Web XML
Como serializar um objeto
Como desserializar um objeto
Como substituir a serialização XML de SOAP codificada
Como substituir a serialização XML de
SOAP codificada
Artigo • 07/04/2023

O processo para substituir a serialização XML de objetos, como mensagens SOAP é


semelhante ao processo para substituir a serialização XML padrão. Para obter
informações sobre como substituir a serialização de XML padrão, consulte Como
especificar um nome de elemento alternativo para um fluxo XML.

Para substituir a serialização XML de objetos


como mensagens SOAP
1. Criar uma instância da classe SoapAttributeOverrides.

2. Crie um SoapAttributes para cada membro de classe que está sendo serializado.

3. Crie uma instância de um ou mais atributos que afetam a serialização XML,


conforme for apropriado, para o membro que está sendo serializado. Para obter
mais informações, consulte "Atributos que controlam a serialização SOAP
codificada".

4. Consulte a propriedade apropriada de SoapAttributes para o atributo criado na


etapa 3.

5. Adicione SoapAttributes a SoapAttributeOverrides .

6. Crie um XmlTypeMapping usando o SoapAttributeOverrides . Use o método


SoapReflectionImporter.ImportTypeMapping .

7. Crie um XmlSerializer usando XmlTypeMapping .

8. Serialize ou desserialize o objeto.

Exemplo
O exemplo de código a seguir serializa um arquivo de duas maneiras: primeiro, sem
substituir o comportamento da classe XmlSerializer e, segundo, substituindo o
comportamento. O exemple contém uma classe denominada Group com vários
membros. Vários atributos, como o SoapElementAttribute , foram aplicados aos
membros da classe. Quando a classe é serializada com o método SerializeOriginal , os
atributos controlam o conteúdo da mensagem SOAP. Quando o método
SerializeOverride é chamado, o comportamento do XmlSerializer é substituído

criando vários atributos e definindo as propriedades de um SoapAttributes para esses


atributos (conforme for apropriado).

C#

using System;
using System.IO;
using System.Xml;
using System.Xml.Serialization;
using System.Xml.Schema;

public class Group


{
[SoapAttribute (Namespace = "http://www.cpandl.com")]
public string GroupName;

[SoapAttribute(DataType = "base64Binary")]
public Byte [] GroupNumber;

[SoapAttribute(DataType = "date", AttributeName = "CreationDate")]


public DateTime Today;
[SoapElement(DataType = "nonNegativeInteger", ElementName = "PosInt")]
public string PositiveInt;
// This is ignored when serialized unless it is overridden.
[SoapIgnore]
public bool IgnoreThis;

public GroupType Grouptype;

[SoapInclude(typeof(Car))]
public Vehicle myCar(string licNumber)
{
Vehicle v;
if(licNumber == "")
{
v = new Car();
v.licenseNumber = "!!!!!!";
}
else
{
v = new Car();
v.licenseNumber = licNumber;
}
return v;
}
}

public abstract class Vehicle


{
public string licenseNumber;
public DateTime makeDate;
}

public class Car: Vehicle


{
}

public enum GroupType


{
// These enums can be overridden.
small,
large
}

public class Run


{
public static void Main()
{
Run test = new Run();
test.SerializeOriginal("SoapOriginal.xml");
test.SerializeOverride("SoapOverrides.xml");
test.DeserializeOriginal("SoapOriginal.xml");
test.DeserializeOverride("SoapOverrides.xml");

}
public void SerializeOriginal(string filename)
{
// Creates an instance of the XmlSerializer class.
XmlTypeMapping myMapping =
(new SoapReflectionImporter().ImportTypeMapping(
typeof(Group)));
XmlSerializer mySerializer =
new XmlSerializer(myMapping);

// Writing the file requires a TextWriter.


TextWriter writer = new StreamWriter(filename);

// Creates an instance of the class that will be serialized.


Group myGroup = new Group();

// Sets the object properties.


myGroup.GroupName = ".NET";

Byte [] hexByte = new Byte[2]{Convert.ToByte(100),


Convert.ToByte(50)};
myGroup.GroupNumber = hexByte;

DateTime myDate = new DateTime(2002,5,2);


myGroup.Today = myDate;

myGroup.PositiveInt= "10000";
myGroup.IgnoreThis=true;
myGroup.Grouptype= GroupType.small;
Car thisCar =(Car) myGroup.myCar("1234566");
// Prints the license number just to prove the car was created.
Console.WriteLine("License#: " + thisCar.licenseNumber + "\n");

// Serializes the class and closes the TextWriter.


mySerializer.Serialize(writer, myGroup);
writer.Close();
}

public void SerializeOverride(string filename)


{
// Creates an instance of the XmlSerializer class
// that overrides the serialization.
XmlSerializer overRideSerializer = CreateOverrideSerializer();

// Writing the file requires a TextWriter.


TextWriter writer = new StreamWriter(filename);

// Creates an instance of the class that will be serialized.


Group myGroup = new Group();

// Sets the object properties.


myGroup.GroupName = ".NET";

Byte [] hexByte = new Byte[2]{Convert.ToByte(100),


Convert.ToByte(50)};
myGroup.GroupNumber = hexByte;

DateTime myDate = new DateTime(2002,5,2);


myGroup.Today = myDate;

myGroup.PositiveInt= "10000";
myGroup.IgnoreThis=true;
myGroup.Grouptype= GroupType.small;
Car thisCar =(Car) myGroup.myCar("1234566");

// Serializes the class and closes the TextWriter.


overRideSerializer.Serialize(writer, myGroup);
writer.Close();
}

public void DeserializeOriginal(string filename)


{
// Creates an instance of the XmlSerializer class.
XmlTypeMapping myMapping =
(new SoapReflectionImporter().ImportTypeMapping(
typeof(Group)));
XmlSerializer mySerializer =
new XmlSerializer(myMapping);

TextReader reader = new StreamReader(filename);

// Deserializes and casts the object.


Group myGroup;
myGroup = (Group) mySerializer.Deserialize(reader);
Console.WriteLine(myGroup.GroupName);
Console.WriteLine(myGroup.GroupNumber[0]);
Console.WriteLine(myGroup.GroupNumber[1]);
Console.WriteLine(myGroup.Today);
Console.WriteLine(myGroup.PositiveInt);
Console.WriteLine(myGroup.IgnoreThis);
Console.WriteLine();
}

public void DeserializeOverride(string filename)


{
// Creates an instance of the XmlSerializer class.
XmlSerializer overRideSerializer = CreateOverrideSerializer();
// Reading the file requires a TextReader.
TextReader reader = new StreamReader(filename);

// Deserializes and casts the object.


Group myGroup;
myGroup = (Group) overRideSerializer.Deserialize(reader);

Console.WriteLine(myGroup.GroupName);
Console.WriteLine(myGroup.GroupNumber[0]);
Console.WriteLine(myGroup.GroupNumber[1]);
Console.WriteLine(myGroup.Today);
Console.WriteLine(myGroup.PositiveInt);
Console.WriteLine(myGroup.IgnoreThis);
}

private XmlSerializer CreateOverrideSerializer()


{
SoapAttributeOverrides mySoapAttributeOverrides =
new SoapAttributeOverrides();
SoapAttributes soapAtts = new SoapAttributes();

SoapElementAttribute mySoapElement = new SoapElementAttribute();


mySoapElement.ElementName = "xxxx";
soapAtts.SoapElement = mySoapElement;
mySoapAttributeOverrides.Add(typeof(Group), "PositiveInt",
soapAtts);

// Overrides the IgnoreThis property.


SoapIgnoreAttribute myIgnore = new SoapIgnoreAttribute();
soapAtts = new SoapAttributes();
soapAtts.SoapIgnore = false;
mySoapAttributeOverrides.Add(typeof(Group), "IgnoreThis",
soapAtts);

// Overrides the GroupType enumeration.


soapAtts = new SoapAttributes();
SoapEnumAttribute xSoapEnum = new SoapEnumAttribute();
xSoapEnum.Name = "Over1000";
soapAtts.SoapEnum = xSoapEnum;

// Adds the SoapAttributes to the


// mySoapAttributeOverrides.
mySoapAttributeOverrides.Add(typeof(GroupType), "large",
soapAtts);

// Creates a second enumeration and adds it.


soapAtts = new SoapAttributes();
xSoapEnum = new SoapEnumAttribute();
xSoapEnum.Name = "ZeroTo1000";
soapAtts.SoapEnum = xSoapEnum;
mySoapAttributeOverrides.Add(typeof(GroupType), "small",
soapAtts);

// Overrides the Group type.


soapAtts = new SoapAttributes();
SoapTypeAttribute soapType = new SoapTypeAttribute();
soapType.TypeName = "Team";
soapAtts.SoapType = soapType;
mySoapAttributeOverrides.Add(typeof(Group),soapAtts);

// Creates an XmlTypeMapping that is used to create an instance


// of the XmlSerializer class. Then returns the XmlSerializer.
XmlTypeMapping myMapping = (new SoapReflectionImporter(
mySoapAttributeOverrides)).ImportTypeMapping(typeof(Group));

XmlSerializer ser = new XmlSerializer(myMapping);


return ser;
}
}

Confira também
Serialização XML e SOAP
Atributos que controlam a serialização SOAP codificada
Serialização XML com Serviços Web XML
Como serializar um objeto
Como desserializar um objeto
Como serializar um objeto como um fluxo XML codificado para SOAP
Como partir dados serializados
Artigo • 01/02/2024

2 Aviso

A serialização binária com BinaryFormatter pode ser perigosa. Para obter mais
informações, consulte o Guia de segurança do BinaryFormatter.

Dois problemas que ocorrem ao enviar grandes conjuntos de dados em mensagens de


serviço Web são:

1. Um grande conjunto de trabalho (memória) devido ao armazenamento em buffer


pelo mecanismo de serialização.

2. Consumo de largura de banda desordenado devido à inflação de 33 por cento


após a codificação Base64.

Para resolver esses problemas, implemente a interface IXmlSerializable para controlar a


serialização e a desserialização. Especificamente, implemente os métodos WriteXml e
ReadXml para partir os dados.

Para implementar o agrupamento do lado do servidor


1. Na música do servidor, o método da Web deve desativar o buffer do ASP.NET e
retornar um tipo que implementa IXmlSerializable.

2. O tipo que implementa IXmlSerializable partir os dados no método WriteXml.

Para implementar o processamento do lado do cliente


1. Altere o método da Web no proxy do cliente para retornar o tipo que implementa
IXmlSerializable. Você pode usar uma SchemaImporterExtension para fazer isso
automaticamente, mas isso não é mostrado aqui.

2. Implemente o método ReadXml para ler o fluxo de dados em partes e gravar os


bytes no disco. Essa implementação também gera eventos de progresso que
podem ser usados por um controle de gráfico, como uma barra de progresso.

Exemplo
O exemplo de código a seguir mostra o método da Web no cliente que desativa o
buffering do ASP.NET. Ele também mostra a implementação do lado do cliente da
interface IXmlSerializable que parte os dados no método WriteXml.

C#

[WebMethod]
[SoapDocumentMethod(ParameterStyle = SoapParameterStyle.Bare)]
public SongStream DownloadSong(DownloadAuthorization Authorization, string
filePath)
{
// Turn off response buffering.
System.Web.HttpContext.Current.Response.Buffer = false;
// Return a song.
SongStream song = new SongStream(filePath);
return song;
}

C#

[XmlSchemaProvider("MySchema")]
public class SongStream : IXmlSerializable
{
private const string ns = "http://demos.Contoso.com/webservices";
private string filePath;

public SongStream() { }

public SongStream(string filePath)


{
this.filePath = filePath;
}

// This is the method named by the XmlSchemaProviderAttribute applied to


the type.
public static XmlQualifiedName MySchema(XmlSchemaSet xs)
{
// This method is called by the framework to get the schema for this
type.
// We return an existing schema from disk.

XmlSerializer schemaSerializer = new


XmlSerializer(typeof(XmlSchema));
string xsdPath = null;
// NOTE: replace the string with your own path.
xsdPath =
System.Web.HttpContext.Current.Server.MapPath("SongStream.xsd");
XmlSchema s = (XmlSchema)schemaSerializer.Deserialize(
new XmlTextReader(xsdPath), null);
xs.XmlResolver = new XmlUrlResolver();
xs.Add(s);
return new XmlQualifiedName("songStream", ns);
}

void IXmlSerializable.WriteXml(System.Xml.XmlWriter writer)


{
// This is the chunking code.
// ASP.NET buffering must be turned off for this to work.

int bufferSize = 4096;


char[] songBytes = new char[bufferSize];
FileStream inFile = File.Open(this.filePath, FileMode.Open,
FileAccess.Read);

long length = inFile.Length;

// Write the file name.


writer.WriteElementString("fileName", ns,
Path.GetFileNameWithoutExtension(this.filePath));

// Write the size.


writer.WriteElementString("size", ns, length.ToString());

// Write the song bytes.


writer.WriteStartElement("song", ns);

StreamReader sr = new StreamReader(inFile, true);


int readLen = sr.Read(songBytes, 0, bufferSize);

while (readLen > 0)


{
writer.WriteStartElement("chunk", ns);
writer.WriteChars(songBytes, 0, readLen);
writer.WriteEndElement();

writer.Flush();
readLen = sr.Read(songBytes, 0, bufferSize);
}

writer.WriteEndElement();
inFile.Close();
}

XmlSchema IXmlSerializable.GetSchema()
{
throw new NotImplementedException();
}

void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)


{
throw new NotImplementedException();
}
}
C#

public class SongFile : IXmlSerializable


{
public static event ProgressMade OnProgress;

public SongFile()
{ }

private const string ns = "http://demos.teched2004.com/webservices";


public static string MusicPath;
private string filePath;
private double size;

void IXmlSerializable.ReadXml(System.Xml.XmlReader reader)


{
reader.ReadStartElement("DownloadSongResult", ns);
ReadFileName(reader);
ReadSongSize(reader);
ReadAndSaveSong(reader);
reader.ReadEndElement();
}

void ReadFileName(XmlReader reader)


{
string fileName = reader.ReadElementString("fileName", ns);
this.filePath =
Path.Combine(MusicPath, Path.ChangeExtension(fileName, ".mp3"));
}

void ReadSongSize(XmlReader reader)


{
this.size = Convert.ToDouble(reader.ReadElementString("size", ns));
}

void ReadAndSaveSong(XmlReader reader)


{
FileStream outFile = File.Open(
this.filePath, FileMode.Create, FileAccess.Write);

string songBase64;
byte[] songBytes;
reader.ReadStartElement("song", ns);
double totalRead = 0;
while (true)
{
if (reader.IsStartElement("chunk", ns))
{
songBase64 = reader.ReadElementString();
totalRead += songBase64.Length;
songBytes = Convert.FromBase64String(songBase64);
outFile.Write(songBytes, 0, songBytes.Length);
outFile.Flush();
if (OnProgress != null)
{
OnProgress(100 * (totalRead / size));
}
}

else
{
break;
}
}

outFile.Close();
reader.ReadEndElement();
}

public void Play()


{
System.Diagnostics.Process.Start(this.filePath);
}

XmlSchema IXmlSerializable.GetSchema()
{
throw new NotImplementedException();
}

public void WriteXml(XmlWriter writer)


{
throw new NotImplementedException();
}
}

Compilando o código
O código usa os seguintes namespaces: System, System.Runtime.Serialization,
System.Web.Services, System.Web.Services.Protocols, System.Xml,
System.Xml.Serialization e System.Xml.Schema.

Confira também
Serialização personalizada

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
Selecione um link para fornecer
comentários:
A fonte deste conteúdo pode
ser encontrada no GitHub, onde  Abrir um problema de
você também pode criar e documentação
revisar problemas e solicitações
de pull. Para obter mais  Fornecer comentários sobre o
informações, confira o nosso
produto
guia para colaboradores.
Elemento <system.xml.serialization>
Artigo • 10/05/2023

O elemento de nível superior para controlar a serialização XML. Para obter mais
informações sobre arquivos de configuração, consulte Esquema de arquivos de
configuração.

<configuration>
<system.xml.serialization>

Syntax
XML

<system.xml.serialization>
</system.xml.serialization>

Atributos e elementos
As seções a seguir descrevem atributos, elementos filho e elementos pai.

Atributos
Nenhum.

Elementos filho

Elemento Descrição

Elemento Determina o modo de serialização de objetos DateTime.


<dateTimeSerialization>

Elemento Contém tipos que são usados pelo XmlSchemaImporter para


<schemaImporterExtensions> mapeamento de tipos XSD para tipos do .NET.

Elementos pai

Elemento Descrição
Elemento Descrição

Elemento O elemento raiz em todos os arquivos de configuração que é usado pelo


<configuration> common language runtime e aplicativos do .NET Framework.

Exemplo
O exemplo de código a seguir ilustra como especificar o modo de serialização de um
objeto DateTime e a adição de tipos usada pelo XmlSchemaImporter ao mapear os
tipos XSD para os tipos do .NET.

XML

<system.xml.serialization>
<xmlSerializer checkDeserializeAdvances="false" />
<dateTimeSerialization mode = "Local" />
<schemaImporterExtensions>
<add
name = "MobileCapabilities"
type = "System.Web.Mobile.MobileCapabilities,
System.Web.Mobile, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f6f11d40a3a" />
</schemaImporterExtensions>
</system.xml.serialization>

Confira também
XmlSchemaImporter
DateTimeSerializationSection.DateTimeSerializationMode
Esquema de arquivos de configuração
Elemento <dateTimeSerialization>
Elemento <schemaImporterExtensions>
Elemento <add> para <schemaImporterExtensions>
Elemento <dateTimeSerialization>
Artigo • 07/04/2023

Determina o modo de serialização de objetos DateTime.

<configuration>
<dateTimeSerialization>

Syntax
XML

<dateTimeSerialization
mode = "Roundtrip|Local"
/>

Atributos e elementos
As seções a seguir descrevem atributos, elementos filho e elementos pai.

Atributos

Atributos Descrição

mode Opcional. Especifica o modo de serialização. Define como um dos valores de


DateTimeSerializationSection.DateTimeSerializationMode. O padrão é RoundTrip.

Elementos filho
Nenhum.

Elementos pai

Elemento Descrição

system.xml.serialization O elemento de nível superior para controlar a serialização XML.

Comentários
Quando essa propriedade é definida como Local, os objetos DateTime são sempre
formatados com a hora local. Ou seja, as informações de fuso horário local são sempre
incluídas com os dados serializados.

Quando essa propriedade é definida como Roundtrip, os objetos DateTime são sempre
examinados para determinar se estão em um fuso horário local, UTC ou não
especificado. Os objetos DateTime são então serializados de modo que essas
informações sejam preservadas. Esse é o comportamento padrão recomendado para
todos os novos aplicativos que não se comunicam com versões antigas do framework.

Confira também
DateTime
XmlSchemaImporter
DateTimeSerializationSection.DateTimeSerializationMode
Esquema de arquivos de configuração
Elemento <schemaImporterExtensions>
Elemento <add> para <schemaImporterExtensions>
Elemento <system.xml.serialization>
<schemaImporterExtensions> element
Artigo • 10/05/2023

Contém tipos que são usados pelo XmlSchemaImporter para mapeamento de tipos XSD
para tipos do .NET. Para obter mais informações sobre arquivos de configuração,
consulte Esquema de arquivos de configuração.

Syntax
XML

<schemaImporterExtensions>
<!-- Add types -->
</schemaImporterExtensions>

Elementos filho
Elemento Descrição

Elemento <add> para Adiciona tipos que são usados pela XmlSchemaImporter
<schemaImporterExtensions> para criar mapeamentos.

Elementos pai
Elemento Descrição

Elemento O elemento de nível superior para controlar a serialização


<system.xml.serialization> XML.

Exemplo
O exemplo de código a seguir ilustra como adicionar tipos que são usados pelo
XmlSchemaImporter ao mapear tipos XSD para tipos do .NET.

XML

<system.xml.serialization>
<schemaImporterExtensions>
<add name = "MobileCapabilities" type =
"System.Web.Mobile.MobileCapabilities,
System.Web.Mobile, Version - 2.0.0.0, Culture = neutral,
PublicKeyToken = b03f5f6f11d40a3a" />
</schemaImporterExtensions>
</system.xml.serialization>

Confira também
XmlSchemaImporter
DateTimeSerializationSection.DateTimeSerializationMode
Esquema de arquivos de configuração
Elemento <dateTimeSerialization>
Elemento <add> para <schemaImporterExtensions>
Elemento <system.xml.serialization>
Elemento <add> para
<schemaImporterExtensions>
Artigo • 10/05/2023

Adiciona tipos usados pelo XmlSchemaImporter para mapeamento de tipos XSD para
tipos do .NET. Para obter mais informações sobre arquivos de configuração, consulte
Esquema de arquivos de configuração.

<configuration>
<system.xml.serialization>
<schemaImporterExtensions>
<adicionar>

Syntax
XML

<add name = "typeName" type="fully qualified type [,Version=version number]


[,Culture=culture] [,PublicKeyToken= token]"/>

Atributos e elementos
As seções a seguir descrevem atributos, elementos filho e elementos pai.

Atributos

Atributo Descrição

name Um nome simples que é usado para localizar a instância.

tipo Obrigatórios. Especifica a classe de extensão de esquema a adicionar. O valor de


atributo type deve ser uma linha e incluir o nome do tipo totalmente qualificado.
Quando o assembly é colocado no GAC (cache de assembly global), ele também deve
incluir a versão, cultura e token de chave pública do assembly assinado.

Elementos filho
Nenhum.
Elementos pai

Elemento Descrição

<schemaImporterExtensions> Contém tipos que são usados pela XmlSchemaImporter.

Exemplo
O exemplo de código a seguir adiciona um tipo de extensão que o XmlSchemaImporter
pode usar ao mapear tipos.

XML

<configuration>
<system.xml.serialization>
<schemaImporterExtensions>
<add name="contoso" type="System.Web.Mobile.MobileCapabilities,
System.Web.Mobile, Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" />
</schemaImporterExtensions>
</system.xml.serialization>
</configuration>

Confira também
XmlSchemaImporter
Elemento <system.xml.serialization>
Elemento <schemaImporterExtensions>
Elemento <xmlSerializer>
Artigo • 07/04/2023

Especifica se uma verificação adicional de progresso do XmlSerializer é feita.

<configuração>
<system.xml.serialization>

Syntax
XML

<xmlSerializer checkDeserializerAdvance = "true|false" />

Atributos e elementos
As seções a seguir descrevem atributos, elementos filho e elementos pai.

Atributos

Atributo Descrição

checkDeserializeAdvances Especifica se o progresso do XmlSerializer é verificado.


Defina o atributo como "true" ou "false". O padrão é "true".

useLegacySerializationGeneration Especifica se o XmlSerializer usa a geração de serialização


herdada que gera assemblies escrevendo código C# em um
arquivo e, em seguida, compilando-o em um assembly. O
padrão é false.

Elementos filho
Nenhum.

Elementos pai

Elemento Descrição

<Elemento> Contém definições de configuração para as classes XmlSerializer e


system.xml.serialization XmlSchemaImporter.
Comentários
Por padrão, o XmlSerializer fornece uma camada adicional de segurança contra
potenciais ataques de negação de serviço ao desserializar dados não confiáveis. Ele faz
isso tentando detectar loops infinitos durante a desserialização. Se uma condição desse
tipo for detectada, uma exceção será gerada com a seguinte mensagem: “Erro interno: a
desserialização não pôde avançar sobre o fluxo subjacente”.

Receber essa mensagem não necessariamente indica que um ataque de negação de


serviço esteja em andamento. Em algumas circunstâncias raras, o mecanismo de
detecção de loop infinito produz um falso positivo e a exceção é gerada para uma
mensagem de entrada legítima. Se você achar que em seus aplicativos específicos
mensagens legítimas estão sendo rejeitadas por essa camada extra de proteção, defina
o atributo checkDeserializeAdvances como “false”.

Exemplo
O exemplo de código a seguir define o atributo checkDeserializeAdvances como
“false”.

XML

<configuration>
<system.xml.serialization>
<xmlSerializer checkDeserializeAdvances="false" />
</system.xml.serialization>
</configuration>

Confira também
XmlSerializer
<Elemento> system.xml.serialization
Serialização XML e SOAP
Ferramenta geradora de serializador de
XML (Sgen.exe)
Artigo • 09/05/2023

O Gerador serializador XML cria um assembly de serialização XML para tipos em um


assembly especificado. O assembly de serialização aprimora o desempenho de
inicialização de um XmlSerializer quando ele serializa ou desserializa objetos dos tipos
especificados.

Syntax
Execute a ferramenta na linha de comando.

Console

sgen [options]

 Dica

Para que as ferramentas do .NET Framework funcionem corretamente, você deve


usar o Prompt de Comando do Desenvolvedor do Visual Studio ou o PowerShell
do Desenvolvedor do Visual Studio ou definir as variáveis de ambiente Path ,
Include e Lib corretamente. Para definir essas variáveis de ambiente, execute
SDKVars.bat, que está localizado no diretório <SDK>\<version>\Bin.

parâmetros
Opção Descrição

/a[ssembly]:filename Gera o código de serialização para todos os tipos contidos no


assembly ou no executável especificado pelo filename. Somente um
nome de arquivo pode ser fornecido. Se esse argumento for repetido,
o último nome de arquivo será usado.

/c[ompiler]:options Especifica as opções para passar para o compilador C#. Todas as


opções csc.exe têm suporte quando são passadas para o compilador.
Isso pode ser usado para especificar que o assembly deve ser
assinado e para especificar o arquivo de chave.

/d[ebug] Gera uma imagem que pode ser usada com um depurador.
Opção Descrição

/f[orce] Força a substituição de um assembly existente de mesmo nome. O


padrão é false.

/help ou /? Exibe sintaxe de comando e opções para a ferramenta.

/k[eep] Suprime a exclusão dos arquivos de origem gerados e outros


arquivos temporários depois que tiverem sido compilados no
assembly de serialização. Isso pode ser usado para determinar se a
ferramenta está gerando o código de serialização para um tipo
específico.

/n[ologo] Suprime a exibição do banner de inicialização da Microsoft.

/o[ut]:path Especifica o diretório no qual salvar o assembly gerado. Observação:


o nome do assembly gerado é composto pelo nome do assembly de
entrada mais “xmlSerializers.dll”.

/p[roxytypes] Gera o código de serialização somente para os tipos de proxy de


serviço Web XML.

/r[eference]:assemblyfiles Especifica os assemblies que são referenciados pelos tipos que


exigem a serialização de XML. Aceita vários arquivos de assembly
separados por vírgulas.

/s[ilent] Suprime a exibição de mensagens de sucesso.

/t[ype]:type Gera o código de serialização somente para o tipo especificado.

/v[erbose] Exibe a saída detalhada para depuração. Lista os tipos do assembly de


destino que não podem ser serializados com o XmlSerializer.

/? Exibe sintaxe de comando e opções para a ferramenta.

Comentários
Quando o Gerador do Serializador do XML não é usado, um XmlSerializer gera o código
de serialização e um assembly de serialização para cada tipo toda vez que um aplicativo
é executado. Para aprimorar o desempenho da inicialização da serialização de XML, use
a ferramenta Sgen.exe para gerar esses assemblies com antecedência. Esses assemblies
podem então ser implantados com o aplicativo.

O Gerador do Serializador do XML também pode aprimorar o desempenho de clientes


que usam proxies de serviço Web XML para se comunicarem com servidores porque o
processo de serialização não incorrerá em um acerto de desempenho quando o tipo for
carregado pela primeira vez.
Esses assemblies gerados não podem ser usados no lado do servidor de um serviço
Web. Essa ferramenta é somente para clientes de serviço Web e cenários de serialização
manual.

Se o assembly que contém o tipo para serializar é denominado MyType.dll, o assembly


de serialização associada será denominado MyType.XmlSerializers.dll.

7 Observação

A ferramenta sgen não é compatível com setters somente init. A ferramenta falhará
se o assembly de destino contiver quaisquer propriedades públicas que usem esse
recurso.

Exemplos
O comando a seguir cria um assembly denominado Data.XmlSerializers.dll para serializar
todos os tipos contidos no assembly denominado Data.dll.

Console

sgen Data.dll

O assembly Data.XmlSerializers.dll pode ser referenciado do código que precisa


serializar e desserializar os tipos no Data.dll.

Confira também
Ferramentas
Shells de linha de comando do desenvolvedor
Ferramenta de Definição de Esquema
XML (Xsd.exe)
Artigo • 09/05/2023

A Ferramenta de Definição de Esquema XML (Xsd.exe) gera um esquema XML ou classes


de common language runtime de arquivos XDR, XML e XSD files, ou de classes em um
assembly de tempo de execução.

A ferramenta de Definição de Esquema XML (Xsd.exe) geralmente pode ser encontrada


no seguinte caminho:
C:\Program Files (x86)\Microsoft SDKs\Windows\{version}\bin\NETFX {version} Tools\

Sintaxe
Execute a ferramenta na linha de comando.

Console

xsd file.xdr [-outputdir:directory][/parameters:file.xml]


xsd file.xml [-outputdir:directory] [/parameters:file.xml]
xsd file.xsd {/classes | /dataset} [/element:element]
[/enableLinqDataSet] [/language:language]
[/namespace:namespace] [-outputdir:directory]
[URI:uri]
[/parameters:file.xml]
xsd {file.dll | file.exe} [-outputdir:directory] [/type:typename [...]]
[/parameters:file.xml]

 Dica

Para as ferramentas do .NET Framework funcionarem corretamente, você deverá


configurar corretamente suas variáveis de ambiente Path , Include e Lib . Defina
essas variáveis de ambiente executando SDKVars.bat, que está localizado no
diretório <SDK>\<versão>\Bin. SDKVars.bat deve ser executado em todo shell de
comando.

Argumento
Argumento Descrição
Argumento Descrição

file.extension Especifica o arquivo de entrada para ser convertido. Você deve especificar a
extensão como uma das seguintes: .xdr, .xml, .xsd, .dll ou .exe.

Se você especificar um arquivo de esquema XDR (extensão .xdr), o Xsd.exe


converterá o esquema XDR em um esquema XSD. O arquivo de saída tem o
mesmo nome que o esquema XDR, mas com a extensão .xsd.

Se você especificar um arquivo XML (extensão .xml), o Xsd.exe deduzirá um


esquema dos dados no arquivo e produzirá um esquema XSD. O arquivo de saída
tem o mesmo nome que o arquivo XML, mas com a extensão .xsd.

Se você especificar um arquivo de esquema XML (extensão .xsd), o Xsd.exe gerará


o código de origem para objetos em runtime que correspondem ao esquema
XML.

Se você especificar um arquivo de assembly de runtime (extensão .exe ou .dll), o


Xsd.exe gerará esquemas para um ou mais tipos nesse assembly. Você pode usar a
opção /type para especificar os tipos para os quais gerar esquemas. Os esquemas
de saída são nomeados schema0.xsd, schema1.xsd e assim por diante. O Xsd.exe
produzirá vários esquemas somente se os determinados tipos especificarem um
namespace usando o atributo personalizado XMLRoot .

Opções gerais
Opção Descrição

/h[elp] Exibe sintaxe de comando e opções para a ferramenta.

/o[utputdir]:diretório Especifica o diretório para arquivos de saída. Esse argumento pode


aparecer somente uma vez. O padrão é o diretório atual.

/? Exibe sintaxe de comando e opções para a ferramenta.

/p[arameters]:arquivo.xml As opções de leitura para vários modos de operação do arquivo .xml


especificado. A forma curta é /p: . Para obter mais informações,
consulte a seção Comentários.

Opções de arquivo XSD


Você deve especificar somente uma das seguintes opções para arquivos .xsd.

Opção Descrição
Opção Descrição

/c[lasses] Gera classes que correspondem ao esquema especificado. Para ler dados XML em um
objeto, use o método XmlSerializer.Deserialize.

/d[ataset] Gera uma classe derivada de DataSet que corresponde ao esquema especificado.
Para ler dados XML na classe derivada, use o método DataSet.ReadXml.

Você também pode especificar qualquer uma das seguintes opções para arquivos .xsd.

Opção Descrição

/e[lement]:elemento Especifica o elemento no esquema para o qual gerar código. Por


padrão, todos os elementos são tipados. Você pode especificar esse
argumento mais de uma vez.

/enableDataBinding Implementa a interface INotifyPropertyChanged em todos os tipos


gerados para habilitar a associação de dados. A forma curta é /edb .

/enableLinqDataSet (Forma abreviada: /eld .) Especifica que o DataSet gerado pode ser
consultado usando LINQ to DataSet. Essa opção é usada quando a
opção /dataset também está especificada. Para obter mais
informações, consulte Visão geral do LINQ to DataSet e Consultando
DataSets tipados. Para obter mais informações sobre o uso do LINQ,
confira LINQ (consulta integrada à linguagem) – C# ou LINQ (consulta
integrada à linguagem) – Visual Basic.

/f[ields] Gera apenas campos. Por padrão, propriedades com campos de


suporte são geradas.

/l[anguage]:linguagem Especifica a linguagem de programação a ser usada. Escolha CS (C#,


que é o padrão), VB (Visual Basic), JS (JScript), ou VJS (Visual J#).
Você também pode especificar um nome totalmente qualificado para
uma classe implementando
System.CodeDom.Compiler.CodeDomProvider

/n[amespace]:namespace Especifica o namespace de runtime para os tipos gerados. O


namespace padrão é Schemas .

/nologo Suprime o banner.

/order Gera identificadores de pedido explícito em todos os membros de


partícula.

/o[ut]:directoryName Especifica o diretório de saída no qual colocar os arquivos. O padrão


é o diretório atual.

/u[ri]:uri Especifica o URI para os elementos no esquema para o qual gerar


código. Esse URI, se houver, aplica-se a todos os elementos
especificados com a opção /element .
Opções de arquivo DLL e EXE
Opção Descrição

/t[ype]:nomedotipo Especifica o nome do tipo para o qual criar um esquema. Você pode
especificar vários argumentos de tipo. Se typename não especificar um
namespace, o Xsd.exe corresponderá todos os tipos no assembly com o
tipo especificado. Se typename especificar um namespace, somente esse
tipo terá uma correspondência. Se typename terminar com um caractere de
asterisco (*), a ferramenta corresponderá todos os tipos que começam com
a cadeia de caracteres antes do *. Se você omitir a opção /type , o Xsd.exe
gera esquemas para todos os tipos no assembly.

Comentários
A tabela a seguir mostra as operações que o Xsd.exe realiza.

Operação Descrição

XDR to Gera um esquema XML de um arquivo de esquema de dados XML reduzidos. XDR é
XSD um formato anterior do esquema baseado em XML.

XML to Gera um esquema XML de um arquivo XML.


XSD

XSD to Gera classes DataSet de common language runtime de um arquivo de esquema XSD.
DataSet As classes geradas fornecem um modelo de objeto ideal para os dados XML
regulares.

XSD para Gera classes de runtime de um arquivo de esquema XSD. As classes geradas podem
classes ser usadas em conjunto com System.Xml.Serialization.XmlSerializer para ler e
escrever código XML que segue o esquema.

Classes Gera um esquema XML de um tipo ou tipos em um arquivo de assembly de runtime.


para XSD O esquema gerado define o formato XML usado pelo XmlSerializer.

O Xsd.exe somente permite que você manipule esquemas de XML que seguem a
linguagem XSD (linguagem de definição de esquema XML) proposta pelo World Wide
Web Consortium (W3C). Para obter mais informações sobre a proposta de definição do
Esquema XML ou o padrão XML, consulte https://w3.org .

Configurando opções com um arquivo XML


Ao usar a opção /parameters , você pode especificar um único arquivo XML que define
várias opções. As opções que você define dependem de como você está usando a
ferramenta XSD.exe. As escolhas incluem gerar esquemas, arquivos de código ou
arquivos de código que incluem recursos de DataSet . Por exemplo, você pode definir o
elemento <assembly> para o nome de um executável (.exe) ou arquivo de biblioteca de
tipos (.dll) ao gerar um esquema, mas não ao gerar um arquivo de código. O XML a
seguir mostra como usar o elemento <generateSchemas> com um executável
especificado:

XML

<!-- This is in a file named GenerateSchemas.xml. -->


<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'>
<generateSchemas>
<assembly>ConsoleApplication1.exe</assembly>
</generateSchemas>
</xsd>

Se o XML anterior estiver contido em um arquivo chamado GenerateSchemas.xml, use a


opção /parameters digitando o seguinte no prompt de comando e pressionando Enter :

Console

xsd /p:GenerateSchemas.xml

Por outro lado, se você estiver gerando um esquema para um único tipo localizado no
assembly, poderá usar o seguinte XML:

XML

<!-- This is in a file named GenerateSchemaFromType.xml. -->


<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'>
<generateSchemas>
<type>IDItems</type>
</generateSchemas>
</xsd>

Mas para usar o código precedente, você também deverá fornecer o nome do assembly
no prompt de comando. Insira o seguinte em um prompt de comando (supondo que o
arquivo XML se chame GenerateSchemaFromType.xml):

Console

xsd /p:GenerateSchemaFromType.xml ConsoleApplication1.exe


Você deve especificar somente uma das seguintes opções para o elemento
<generateSchemas> .

Elemento Descrição

<assembly> Especifica um assembly do qual gerar o esquema.

<tipo> Especifica um tipo encontrado em um assembly para o qual gerar um esquema.

<xml> Especifica um arquivo XML para o qual gerar um esquema.

<xdr> Especifica um arquivo XDR para o qual gerar um esquema.

Para gerar um arquivo de código, use o elemento <generateClasses> . O exemplo a


seguir gera um arquivo de código. Observe que dois atributos também são mostrados e
permitem definir a linguagem de programação e o namespace do arquivo gerado.

XML

<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'>
<generateClasses language='VB'
namespace='Microsoft.Serialization.Examples'/>
</xsd>
<!-- You must supply an .xsd file when typing in the command line.-->
<!-- For example: xsd /p:genClasses mySchema.xsd -->

As opções que você pode definir para o elemento <generateClasses> incluem o


seguinte.

Elemento Descrição

<element> Especifica um elemento no arquivo .xsd para o qual gerar código.

<schemaImporterExtensions> Especifica um tipo derivado de uma classe


SchemaImporterExtension.

<schema> Especifica um arquivo de esquema XML para o qual gerar um


código. Vários arquivos do Esquema XML podem ser
especificados usando vários elementos <schema>.

A tabela a seguir mostra os atributos que também podem ser usados com o elemento
<generateClasses> .

Atributo Descrição
Atributo Descrição

Linguagem Especifica a linguagem de programação a ser usada. Escolha CS (C#, o padrão), VB


(Visual Basic), JS (JScript), ou VJS (Visual J#). Você também pode especificar um
nome totalmente qualificado para uma classe que implementa CodeDomProvider

namespace Especifica o namespace para o código gerado. O namespace deve estar em


conformidade com os padrões CLR (por exemplo, sem caracteres de espaço ou
barra invertida).

opções Um dos seguintes valores: none , properties (gera propriedades em vez de campos
públicos), order ou enableDataBinding (consulte as opções /order e
/enableDataBinding na seção anterior Opções de arquivo XSD).

Você também pode controlar como o código do DataSet é gerado usando o elemento
<generateDataSet> . O XML a seguir especifica que o código gerado usa estruturas
DataSet (como a classe DataTable) para criar um código do Visual Basic para um

elemento especificado. As estruturas de DataSet geradas darão suporte a consultas


LINQ.

XML

<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'>
<generateDataSet language='VB'
namespace='Microsoft.Serialization.Examples' enableLinqDataSet='true'>
</generateDataSet>
</xsd>

As opções que você pode definir para o elemento <generateDataSet> incluem o


seguinte.

Elemento Descrição

<schema> Especifica um arquivo de esquema XML para o qual gerar um código. Vários
arquivos do Esquema XML podem ser especificados usando vários elementos
<schema>.

A tabela a seguir mostra os atributos que podem ser usados com o elemento
<generateDataSet> .

Atributo Descrição

enableLinqDataSet Especifica que o DataSet gerado pode ser consultado usando LINQ to
DataSet. O valor padrão é false.
Atributo Descrição

Linguagem Especifica a linguagem de programação a ser usada. Escolha CS (C#, o


padrão), VB (Visual Basic), JS (JScript), ou VJS (Visual J#). Você também
pode especificar um nome totalmente qualificado para uma classe que
implementa CodeDomProvider

namespace Especifica o namespace para o código gerado. O namespace deve estar em


conformidade com os padrões CLR (por exemplo, sem caracteres de espaço
ou barra invertida).

Há atributos que você pode definir no elemento <xsd> de nível superior. Essas opções
podem ser usadas com qualquer um dos elementos filho ( <generateSchemas> ,
<generateClasses> ou <generateDataSet> ). O código XML a seguir gera código para um
elemento denominado "IDItems" no diretório de saída denominado
"MyOutputDirectory".

XML

<xsd xmlns='http://microsoft.com/dotnet/tools/xsd/'
output='MyOutputDirectory'>
<generateClasses>
<element>IDItems</element>
</generateClasses>
</xsd>

A tabela a seguir mostra os atributos que também podem ser usados com o elemento
<xsd> .

Atributo Descrição

output O nome de um diretório onde o esquema ou arquivo de código gerado será colocado.

nologo Suprime o banner. Definir como true ou false .

ajuda Exibe sintaxe de comando e opções para a ferramenta. Definir como true ou false .

Exemplos
O comando a seguir gera um esquema XML de myFile.xdr e salva-o no diretório atual.

Console

xsd myFile.xdr
O comando a seguir gera um esquema XML de myFile.xml e salva-o no diretório
especificado.

Console

xsd myFile.xml /outputdir:myOutputDir

O comando a seguir gera um conjunto de dados que corresponde ao esquema


especificado na linguagem C# e salva-o como XSDSchemaFile.cs no diretório atual.

Console

xsd /dataset /language:CS XSDSchemaFile.xsd

O comando a seguir gera esquemas XML para todos os tipos no assembly


myAssembly.dll e salva-os como schema0.xsd no diretório atual.

Console

xsd myAssembly.dll

Confira também
DataSet
System.Xml.Serialization.XmlSerializer
Ferramentas
Shells de linha de comando do desenvolvedor
LINQ para visão geral do DataSet
Consultando DataSets tipados
LINQ (Consulta Integrada à Linguagem) (C#)
LINQ (Consulta integrada à linguagem) (Visual Basic)
Riscos de desserialização no uso do
BinaryFormatter e tipos relacionados
Artigo • 06/04/2023

Este artigo se aplica aos seguintes tipos:

BinaryFormatter
SoapFormatter
NetDataContractSerializer
LosFormatter
ObjectStateFormatter

Este artigo se aplica às seguintes implementações do .NET:

.NET Framework todas as versões


.NET Core 2.1 – 3.1
.NET 5 e posteriores

2 Aviso

O tipo BinaryFormatter é perigoso e não é recomendado para processamento de


dados. Os aplicativos devem parar de usar BinaryFormatter o mais rápido possível,
mesmo que acreditem que os dados que estão processando são confiáveis.
BinaryFormatter não é seguro e não pode ser tornado seguro.

Vulnerabilidades de desserialização
As vulnerabilidades de desserialização são uma categoria de ameaça em que as cargas
de solicitação são processadas de maneira insegura. Um invasor que aproveita com
êxito essas vulnerabilidades em um aplicativo pode causar DoS (negação de serviço),
divulgação de informações ou execução de código remoto dentro do aplicativo de
destino. Essa categoria de risco consistentemente fica no Top 10 do OWASP . Os
destinos incluem aplicativos escritos em uma variedade de linguagens , incluindo
C/C++, Java e C#.

No .NET, o alvo de maior destino são os aplicativos que usam o tipo BinaryFormatter
para desserializar dados. BinaryFormatter é amplamente usado em todo o ecossistema
do .NET devido ao seu poder e sua facilidade de uso. No entanto, esse mesmo poder
permite que os invasores influenciem o fluxo de controle dentro do aplicativo de
destino. Ataques bem-sucedidos podem fazer com que o invasor possa executar o
código dentro do contexto do processo de destino.

Como uma analogia mais simples, suponha que chamar BinaryFormatter.Deserialize


sobre um conteúdo seja o equivalente a interpretar esse conteúdo como um executável
autônomo e iniciá-lo.

Vulnerabilidades de segurança do
BinaryFormatter

2 Aviso

O método BinaryFormatter.Deserialize nunca é seguro quando usado com


entrada não confiável. Recomendamos fortemente que os consumidores
considerem usar uma das alternativas descritas posteriormente neste artigo.

BinaryFormatter foi implementado antes de vulnerabilidades de desserialização serem

uma categoria de ameaça bem compreendida. Assim, o código não segue as práticas
recomendadas modernas. O método Deserialize pode ser usado como um vetor para
invasores executarem ataques DoS contra aplicativos de consumo. Esses ataques podem
fazer com que o aplicativo deixe de responder ou resultar em um encerramento
inesperado do processo. Essa categoria de ataque não pode ser atenuada com um
SerializationBinder ou qualquer outro comutador de configuração BinaryFormatter . O

.NET considera esse comportamento padrão e não emitirá uma atualização de código
para modificá-lo.

BinaryFormatter.Deserialize pode ser vulnerável a outras categorias de ataque, como

divulgação de informações ou execução remota de código. Utilizar recursos como um


SerializationBinder personalizado pode ser insuficiente para atenuar corretamente esses
riscos. Existe a possibilidade de que seja descoberta uma nova vulnerabilidade para a
qual o .NET não consiga publicar de modo prático uma atualização de segurança. Os
consumidores devem avaliar seus cenários individuais e considerar sua potencial
exposição a esses riscos.

Recomendamos que os consumidores do BinaryFormatter realizem avaliações de risco


individuais em seus aplicativos. É responsabilidade exclusiva do consumidor determinar
se ele deve utilizar o BinaryFormatter . Se estiver considerando usá-lo, avalie os riscos
das consequências de segurança, técnicas, de reputação, legais e regulatórias.
Alternativas preferenciais
O .NET oferece vários serializadores in-box que podem lidar com os dados não
confiáveis com segurança:

XmlSerializer e DataContractSerializer para serializar grafos de objeto em XML e


em XML. Não confunda DataContractSerializer com NetDataContractSerializer.
BinaryReader e BinaryWriter para XML e JSON.
As APIs System.Text.Json para serializar grafos de objeto em JSON.

Alternativas perigosas
Evite os seguintes serializadores:

SoapFormatter
LosFormatter
NetDataContractSerializer
ObjectStateFormatter

Todos os serializadores anteriores executam desserialização polimórfica irrestrita e são


perigosos, assim como BinaryFormatter .

Os riscos de presumir que os dados são


confiáveis
Frequentemente, um desenvolvedor de aplicativos pode acreditar que está processando
apenas entrada confiável. O caso de entrada segura é verdadeiro em algumas
circunstâncias raras. Contudo, é muito mais comum que um conteúdo cruze um limite
de confiança sem que o desenvolvedor perceba.

Considere um servidor local em que os funcionários usam um cliente da área de


trabalho de suas estações de trabalho para interagir com o serviço. Esse cenário pode
ser visto ingenuamente como uma configuração "segura" em que a utilização
BinaryFormatter é aceitável. No entanto, esse cenário apresenta um vetor para malware

que ganha acesso ao computador de um só funcionário para se espalhar por toda a


empresa. Esse malware pode aproveitar o uso BinaryFormatter pela empresa para se
mover lateralmente da estação de trabalho do funcionário para o servidor de back-end.
Então ele pode exfiltrar os dados confidenciais da empresa. Esses dados podem incluir
segredos comerciais ou dados do cliente.
Considere também um aplicativo que usa BinaryFormatter para persistir o estado de
salvamento. À primeira vista, isso pode parecer um cenário seguro, pois ler e gravar
dados em um disco rígido próprio representa uma pequena ameaça. Porém, o
compartilhamento de documentos por email ou pela Internet é comum e a maioria dos
usuários finais não perceberia a abertura desses arquivos baixados como um
comportamento arriscado.

Esse cenário pode ser aproveitado para efeito nefasto. Se o aplicativo for um jogo, os
usuários que compartilham arquivos de salvamento se colocam em risco
inadvertidamente. Os próprios desenvolvedores também podem ser alvos. O invasor
pode enviar por email o suporte técnico dos desenvolvedores anexando um arquivo de
dados mal-intencionado e solicitando que a equipe de suporte o abra. Esse tipo de
ataque pode abrir as portas para o invasor entrar na empresa.

Outro cenário é quando o arquivo de dados é armazenado no armazenamento em


nuvem e sincronizado automaticamente entre os computadores do usuário. Um invasor
que consegue obter acesso à conta de armazenamento em nuvem pode envenenar o
arquivo de dados. Esse arquivo de dados será sincronizado automaticamente com os
computadores do usuário. Na próxima vez que o usuário abrir o arquivo de dados, o
conteúdo do invasor será executado. Portanto, o invasor poderá aproveitar um
compromisso de conta de armazenamento em nuvem para obter permissões de
execução de código completas.

Considere um aplicativo que passa de um modelo de instalação da área de trabalho


para um modelo primeiro na nuvem. Esse cenário inclui aplicativos que se movem de
um aplicativo da área de trabalho ou modelo de cliente avançado para um modelo
baseado na Web. Os modelos de ameaça elaborados para o aplicativo da área de
trabalho não são necessariamente aplicáveis ao serviço baseado em nuvem. O modelo
de risco para o aplicativo da área de trabalho pode descartar uma determinada ameaça
como "não interessante para o cliente atacar a si mesmo". Mas essa mesma ameaça
pode se tornar interessante quando considera um usuário remoto (o cliente) atacando o
serviço de nuvem em si.

7 Observação

Em termos gerais, a intenção de serialização é transmitir um objeto para dentro ou


para fora de um aplicativo. Um exercício de modelagem de ameaças quase sempre
marca esse tipo de transferência de dados como cruzando um limite de confiança.

Confira também
Serialização binária
YSoSerial.Net para pesquisas sobre como adversários atacam aplicativos que
utilizam BinaryFormatter .
Contexto geral sobre vulnerabilidades de desserialização:
OWASP: desserialização de dados não confiáveis
CWE-502: desserialização de dados não confiáveis

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Origem do evento do BinaryFormatter
Artigo • 27/04/2023

A partir do .NET 5, BinaryFormatter inclui um EventSource interno que fornece


visibilidade de quando uma serialização de objeto ou desserialização está ocorrendo. Os
aplicativos podem usar os tipos derivados de EventListener para escutar essas
notificações e registrá-las em log.

Essa funcionalidade não substitui um SerializationBinder ou um ISerializationSurrogate e


não pode ser usada para modificar os dados que estão sendo serializados ou
desserializados. Em vez disso, esse sistema de eventos destina-se a fornecer
informações sobre os tipos que estão sendo serializados ou desserializados. Ele também
pode ser usado para detectar chamadas não intencionais para a infraestrutura
BinaryFormatter , como chamadas provenientes do código de biblioteca de terceiros.

Descrição de eventos
A origem do evento BinaryFormatter tem o nome
System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource
conhecido. Os ouvintes podem assinar seis eventos.

Evento SerializationStart (id = 10 )


Gerado quando BinaryFormatter.Serialize foi chamado e iniciou o processo de
serialização. Esse evento é emparelhado com o evento SerializationEnd . O evento
SerializationStart poderá ser chamado recursivamente se um objeto chamar

BinaryFormatter.Serialize dentro de sua própria rotina de serialização.

Esse evento não contém uma carga.

Evento SerializationEnd (id = 11 )


Gerado quando BinaryFormatter.Serialize tiver concluído seu trabalho. Cada
ocorrência de SerializationEnd indica a conclusão do último evento não pago
SerializationStart .

Esse evento não contém uma carga.

Evento SerializingObject (id = 12 )


Gerado quando BinaryFormatter.Serialize está em processo de serialização de um tipo
não primitivo. A infraestrutura BinaryFormatter diferencia determinados tipos (como
string e int ) e não gera esse evento quando esses tipos são encontrados. Esse evento

é gerado para tipos definidos pelo usuário e para outros tipos em que BinaryFormatter
não entende nativamente.

Esse evento pode ser gerado zero ou mais vezes entre os eventos SerializationStart e
SerializationEnd .

Esse evento contém uma carga com um argumento:

typeName ( string ): O nome qualificado para o assembly (confira


Type.AssemblyQualifiedName) do tipo que está sendo serializado.

Evento DeserializationStart (id = 20 )


Gerado quando BinaryFormatter.Deserialize foi chamado e iniciou o processo de
desserialização. Esse evento é emparelhado com o evento DeserializationEnd . O
evento DeserializationStart poderá ser chamado recursivamente se um objeto chamar
BinaryFormatter.Deserialize dentro de sua própria rotina de desserialização.

Esse evento não contém uma carga.

Evento DeserializationEnd (id = 21 )


Gerado quando BinaryFormatter.Deserialize tiver concluído seu trabalho. Cada
ocorrência de DeserializationEnd indica a conclusão do último evento não pago
DeserializationStart .

Esse evento não contém uma carga.

Evento DeserializingObject (id = 22 )


Gerado quando BinaryFormatter.Deserialize está em processo de desserialização de
um tipo não primitivo. A infraestrutura BinaryFormatter diferencia determinados tipos
(como string e int ) e não gera esse evento quando esses tipos são encontrados. Esse
evento é gerado para tipos definidos pelo usuário e para outros tipos em que
BinaryFormatter não entende nativamente.

Esse evento pode ser gerado zero ou mais vezes entre os eventos DeserializationStart
e DeserializationEnd .
Esse evento contém uma carga com um argumento.

typeName ( string ): O nome qualificado para o assembly (confira


Type.AssemblyQualifiedName) do tipo que está sendo desserializado.

[Avançado] Assinando um subconjunto de notificações


Os ouvintes que desejam assinar apenas um subconjunto de notificações podem
escolher quais palavras-chave habilitar.

Serialization = (EventKeywords)1 : gera os eventos SerializationStart ,


SerializationEnd e SerializingObject .

Deserialization = (EventKeywords)2 : gera os eventos DeserializationStart ,

DeserializationEnd e DeserializingObject .

Se nenhum filtro de palavra-chave for fornecido durante o registro EventListener ,


todos os eventos serão gerados.

Para obter mais informações, consulte System.Diagnostics.Tracing.EventKeywords.

Código de exemplo
O seguinte código:

cria um tipo EventListener derivado que grava em System.Console ,


assina esse ouvinte em notificações produzidas de BinaryFormatter ,
serializa e desserializa um grafo de objeto simples usando BinaryFormatter e
analisa os eventos que foram gerados.

C#

using System;
using System.Diagnostics.Tracing;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace BinaryFormatterEventSample
{
class Program
{
static EventListener? _globalListener = null;

static void Main(string[] args)


{
// First, set up the event listener.
// Note: We assign it to a static field so that it doesn't get
GCed.
// We also provide a callback that subscribes this listener to
all
// events produced by the well-known BinaryFormatter source.

_globalListener = new ConsoleEventListener();


_globalListener.EventSourceCreated += (sender, args) =>
{
if (args.EventSource?.Name ==

"System.Runtime.Serialization.Formatters.Binary.BinaryFormatterEventSource")
{
((EventListener?)sender)?
.EnableEvents(args.EventSource,
EventLevel.LogAlways);
}
};

// Next, create the Person object and serialize it.

Person originalPerson = new Person()


{
FirstName = "Logan",
LastName = "Edwards",
FavoriteBook = new Book()
{
Title = "A Tale of Two Cities",
Author = "Charles Dickens",
Price = 10.25m
}
};

byte[] serializedPerson = SerializePerson(originalPerson);

// Finally, deserialize the Person object.

Person rehydratedPerson = DeserializePerson(serializedPerson);

Console.WriteLine
($"Rehydrated person {rehydratedPerson.FirstName}
{rehydratedPerson.LastName}");
Console.Write
($"Favorite book: {rehydratedPerson.FavoriteBook?.Title} ");
Console.Write
($"by {rehydratedPerson.FavoriteBook?.Author}, ");
Console.WriteLine
($"list price {rehydratedPerson.FavoriteBook?.Price}");
}

private static byte[] SerializePerson(Person p)


{
MemoryStream memStream = new MemoryStream();
BinaryFormatter formatter = new BinaryFormatter();
#pragma warning disable SYSLIB0011 // BinaryFormatter.Serialize is obsolete
formatter.Serialize(memStream, p);
#pragma warning restore SYSLIB0011

return memStream.ToArray();
}

private static Person DeserializePerson(byte[] serializedData)


{
MemoryStream memStream = new MemoryStream(serializedData);
BinaryFormatter formatter = new BinaryFormatter();

#pragma warning disable SYSLIB0011 // Danger: BinaryFormatter.Deserialize is


insecure for untrusted input
return (Person)formatter.Deserialize(memStream);
#pragma warning restore SYSLIB0011
}
}

[Serializable]
public class Person
{
public string? FirstName;
public string? LastName;
public Book? FavoriteBook;
}

[Serializable]
public class Book
{
public string? Title;
public string? Author;
public decimal? Price;
}

// A sample EventListener that writes data to System.Console.


public class ConsoleEventListener : EventListener
{
protected override void OnEventWritten(EventWrittenEventArgs
eventData)
{
base.OnEventWritten(eventData);

Console.WriteLine($"Event {eventData.EventName} (id=


{eventData.EventId}) received.");
if (eventData.PayloadNames != null)
{
for (int i = 0; i < eventData.PayloadNames.Count; i++)
{
Console.WriteLine($"{eventData.PayloadNames[i]} =
{eventData.Payload?[i]}");
}
}
}
}
}
O código anterior produz uma saída semelhante ao exemplo a seguir:

Saída

Event SerializationStart (id=10) received.


Event SerializingObject (id=12) received.
typeName = BinaryFormatterEventSample.Person, BinaryFormatterEventSample,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event SerializingObject (id=12) received.
typeName = BinaryFormatterEventSample.Book, BinaryFormatterEventSample,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event SerializationStop (id=11) received.
Event DeserializationStart (id=20) received.
Event DeserializingObject (id=22) received.
typeName = BinaryFormatterEventSample.Person, BinaryFormatterEventSample,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event DeserializingObject (id=22) received.
typeName = BinaryFormatterEventSample.Book, BinaryFormatterEventSample,
Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Event DeserializationStop (id=21) received.
Rehydrated person Logan Edwards
Favorite book: A Tale of Two Cities by Charles Dickens, list price 10.25

Neste exemplo, os logs baseados no console EventListener que a serialização inicia, as


instâncias de Person Book são serializadas e, em seguida, a serialização é concluída. Da
mesma forma, depois que a desserialização é iniciada, as instâncias de Person e Book
são desserializadas e, em seguida, a desserialização é concluída.

Em seguida, o aplicativo imprime os valores contidos no Person desserializado para


demonstrar que o objeto de fato serializou e desserializou corretamente.

Confira também
Para obter mais informações sobre como usar EventListener para receber notificações
baseadas em EventSource , confira a classe EventListener.
System.Runtime.Serialization.DataContr
actAttribute classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Aplique o DataContractAttribute atributo a tipos (classes, estruturas ou enumerações)


usados em operações de serialização e desserialização pelo DataContractSerializer. Se
você enviar ou receber mensagens usando a infraestrutura do Windows Communication
Foundation (WCF), também deverá aplicar o DataContractAttribute a todas as classes
que contêm e manipulam dados enviados em mensagens. Para obter mais informações
sobre contratos de dados, confira Como usar contratos de dados.

Você também deve aplicar o DataMemberAttribute a qualquer campo, propriedade ou


evento que contenha valores que você deseja serializar. Ao aplicar o , você habilita
explicitamente o DataContractAttributeDataContractSerializer para serializar e
desserializar os dados.

U Cuidado

Você pode aplicar o DataMemberAttribute a campos privados. Lembre-se de que


os dados retornados pelo campo (mesmo que sejam privados) são serializados e
desserializados e, portanto, podem ser exibidos ou interceptados por um usuário
ou processo mal-intencionado.

Para obter mais informações sobre contratos de dados, consulte os tópicos listados em
Usando contratos de dados.

Contratos de dados
Um contrato de dados é uma descrição abstrata de um conjunto de campos com um
nome e um tipo de dados para cada campo. O contrato de dados existe fora de
qualquer implementação única para permitir que serviços em diferentes plataformas
interoperem. Desde que os dados passados entre os serviços estejam em conformidade
com o mesmo contrato, todos os serviços podem processar os dados. Esse
processamento também é conhecido como um sistema fracamente acoplado. Um
contrato de dados também é semelhante a uma interface, pois o contrato especifica
como os dados devem ser entregues para que possam ser processados por um
aplicativo. Por exemplo, o contrato de dados pode chamar um tipo de dados chamado
"Pessoa" que tem dois campos de texto, chamados "Nome" e "Sobrenome". Para criar
um contrato de dados, aplique o à classe e aplique o
DataContractAttributeDataMemberAttribute a quaisquer campos ou propriedades que
devem ser serializados. Quando serializados, os dados estão em conformidade com o
contrato de dados que é incorporado implicitamente no tipo.

7 Observação

Um contrato de dados difere significativamente de uma interface real em seu


comportamento de herança. As interfaces são herdadas por quaisquer tipos
derivados. Quando você aplica o a uma classe base, os tipos derivados não herdam
o atributo ou o DataContractAttribute comportamento. No entanto, se um tipo
derivado tiver um contrato de dados, os membros de dados da classe base serão
serializados. No entanto, você deve aplicar o DataMemberAttribute a novos
membros em uma classe derivada para torná-los serializáveis.

Documentos do esquema XML e a ferramenta


SvcUtil
Se você estiver trocando dados com outros serviços, deverá descrever o contrato de
dados. Para a versão atual do DataContractSerializer, um esquema XML pode ser usado
para definir contratos de dados. (Outras formas de metadados/descrição podem ser
usadas para a mesma finalidade.) Para criar um esquema XML a partir do seu aplicativo,
use a ServiceModel Metadata Utility Tool (Svcutil.exe) com a opção de linha de
comando /dconly. Quando a entrada para a ferramenta é um assembly, por padrão, a
ferramenta gera um conjunto de esquemas XML que definem todos os tipos de
contrato de dados encontrados nesse assembly. Por outro lado, você também pode usar
a ferramenta Svcutil.exe para criar definições de classe Visual Basic ou C# que estejam
em conformidade com os requisitos de esquemas XML que usam construções que
podem ser expressas por contratos de dados. Nesse caso, a opção de linha de comando
/dconly não é necessária.

Se a entrada para a ferramenta Svcutil.exe for um esquema XML, por padrão, a


ferramenta criará um conjunto de classes. Se você examinar essas classes, descobrirá
que o DataContractAttribute foi aplicado. Você pode usar essas classes para criar um
novo aplicativo para processar dados que devem ser trocados com outros serviços.

Você também pode executar a ferramenta em um ponto de extremidade que retorna


um documento WSDL (Web Services Description Language) para gerar
automaticamente o código e a configuração para criar um cliente Windows
Communication Foundation (WCF). O código gerado inclui tipos marcados com o
DataContractAttribute.

Reutilizar tipos existentes


Um contrato de dados tem dois requisitos básicos: um nome estável e uma lista de
membros. O nome estável consiste no URI (identificador uniforme de recursos) do
namespace e no nome local do contrato. Por padrão, quando você aplica o a uma
classe, ele usa o nome da classe como o nome local e o namespace da classe (prefixado
com "http://schemas.datacontract.org/2004/07/" ) como o DataContractAttribute URI
do namespace. Você pode substituir os padrões definindo as Name propriedades e
Namespace . Você também pode alterar o namespace aplicando o
ContractNamespaceAttribute ao namespace. Use esse recurso quando você tiver um
tipo existente que processa dados exatamente como você precisa, mas tem um
namespace e um nome de classe diferentes do contrato de dados. Substituindo os
valores padrão, você pode reutilizar seu tipo existente e fazer com que os dados
serializados estejam em conformidade com o contrato de dados.

7 Observação

Em qualquer código, você pode usar a palavra DataContract em vez do mais longo
DataContractAttribute.

Controle de versão
Um contrato de dados também pode acomodar versões posteriores de si mesmo. Ou
seja, quando uma versão posterior do contrato inclui dados extras, esses dados são
armazenados e retornados a um remetente intocado. Para fazer isso, implemente a
IExtensibleDataObject interface.

Para obter mais informações sobre controle de versão, consulte Controle de versão de
contrato de dados.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
 Abrir um problema de
revisar problemas e solicitações
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Runtime.Serialization.DataContr
actSerializer classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Use a DataContractSerializer classe para serializar e desserializar instâncias de um tipo


em um fluxo XML ou documento. Por exemplo, você pode criar um tipo nomeado
Person com propriedades que contêm dados essenciais, como um nome e endereço.

Em seguida, você pode criar e manipular uma instância da Person classe e gravar todos
os seus valores de propriedade em um documento XML para recuperação posterior ou
em um fluxo XML para transporte imediato. Mais importante, o DataContractSerializer é
usado para serializar e desserializar dados enviados em mensagens do Windows
Communication Foundation (WCF). Aplique o atributo a classes e o
DataContractAttribute atributo a membros de DataMemberAttribute classe para
especificar propriedades e campos serializados.

Para obter uma lista de tipos que podem ser serializados, consulte Tipos suportados
pelo serializador de contrato de dados.

Para usar o , primeiro crie uma instância de uma classe e um objeto apropriado para
escrever ou ler o DataContractSerializerformato, por exemplo, uma instância do
XmlDictionaryWriter. Em seguida, chame o WriteObject método para persistir os dados.
Para recuperar dados, crie um objeto apropriado para ler o formato de dados (como um
para um XmlDictionaryReader documento XML) e chame o ReadObject método.

Para obter mais informações sobre como usar o DataContractSerializer, consulte


Serialização e desserialização.

Você pode definir o tipo de um serializador de contrato de dados usando o <elemento


dataContractSerializer> em um arquivo de configuração de aplicativo cliente.

Preparar classes para serialização ou


desserialização
O DataContractSerializer é usado em combinação com as DataContractAttribute classes
e DataMemberAttribute . Para preparar uma classe para serialização, aplique o
DataContractAttribute à classe. Para cada membro da classe que retorna dados que
você deseja serializar, aplique o DataMemberAttribute. Você pode serializar campos e
propriedades, independentemente da acessibilidade: privada, protegida, interna, interna
protegida ou pública.

Por exemplo, seu esquema especifica um com uma propriedade, mas você já tem um
aplicativo existente que usa um Customer tipo nomeado Person com uma ID Name
propriedade. Para criar um tipo que esteja em conformidade com o contrato, primeiro
aplique o DataContractAttribute à classe. Em seguida, aplique o DataMemberAttribute a
cada campo ou propriedade que você deseja serializar.

7 Observação

Você pode aplicar o DataMemberAttribute para membros privados e públicos.

O formato final do XML não precisa ser texto. Em vez disso, o grava
DataContractSerializer os dados como um infoset XML, que permite gravar os dados em
qualquer formato reconhecido pelo XmlReader e XmlWriter. É recomendável que você
use as XmlDictionaryReader classes e para ler e XmlDictionaryWriter escrever, pois
ambas são otimizadas para trabalhar com o DataContractSerializer.

Se você estiver criando uma classe que tenha campos ou propriedades que devem ser
preenchidos antes que a serialização ou desserialização ocorra, use atributos de retorno
de chamada, conforme descrito em Retornos de chamada de serialização tolerante à
versão.

Adicionar à coleção de tipos conhecidos


Ao serializar ou desserializar um objeto, é necessário que o tipo seja "conhecido" para o
DataContractSerializer. Comece criando uma instância de uma classe que implementa
IEnumerable<T> (como List<T>) e adicionando os tipos conhecidos à coleção. Em
seguida, crie uma instância do DataContractSerializer uso de uma das sobrecargas que
leva o IEnumerable<T> (por exemplo, DataContractSerializer(Type,
IEnumerable<Type>)).

7 Observação

Ao contrário de outros tipos primitivos, a DateTimeOffset estrutura não é um tipo


conhecido por padrão, portanto, ela deve ser adicionada manualmente à lista de
tipos conhecidos (consulte Tipos conhecidos de contrato de dados).
Compatibilidade com encaminhamento
O DataContractSerializer compreende contratos de dados que foram projetados para
serem compatíveis com versões futuras do contrato. Tais tipos implementam a
IExtensibleDataObject interface. A interface apresenta a ExtensionData propriedade que
retorna um ExtensionDataObject objeto. Para obter mais informações, consulte
Contratos de dados compatíveis por encaminhamento.

Executar sob confiança parcial


Ao instanciar o objeto de destino durante a desserialização, o não chama o
DataContractSerializer construtor do objeto de destino. Se você criar um tipo
[DataContract] que é acessível a partir de confiança parcial (ou seja, é público e em um
assembly que tem o atributo aplicado) e que executa algumas ações relacionadas à
segurança, você deve estar ciente de que o AllowPartiallyTrustedCallers construtor
não é chamado. Em particular, as seguintes técnicas não funcionam:

Se você tentar restringir o acesso de confiança parcial tornando o construtor


interno ou privado, ou adicionando um LinkDemand ao construtor -- nenhum deles
terá qualquer efeito durante a desserialização sob confiança parcial.
Se você codificar a classe que assume que o construtor foi executado, a classe
pode entrar em um estado interno inválido que é explorável.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Interface
System.Runtime.Serialization.IExtensible
DataObject
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A IExtensibleDataObject interface fornece uma única propriedade que define ou retorna


uma estrutura usada para armazenar dados externos a um contrato de dados. Os dados
extras são armazenados em uma instância da ExtensionDataObject classe e acessados
por meio da ExtensionData propriedade. Em uma operação de ida e volta em que os
dados são recebidos, processados e enviados de volta, os dados extras são enviados de
volta ao remetente original intactos. Isso é útil para armazenar dados recebidos de
versões futuras do contrato. Se você não implementar a interface, todos os dados extras
serão ignorados e descartados durante uma operação de ida e volta.

Para usar esse recurso de controle de versão


1. Implemente a IExtensibleDataObject interface em uma classe.

2. Adicione a ExtensionData propriedade ao seu tipo.

3. Adicione um membro privado do tipo ExtensionDataObject à classe.

4. Implemente métodos get e set para a propriedade usando o novo membro


privado.

5. Aplique o DataContractAttribute atributo à classe. Defina as propriedades e


Namespace para valores apropriados, Name se necessário.

Para obter mais informações sobre controle de versão de tipos, consulte Controle de
versão de contrato de dados. Para obter informações sobre como criar contratos de
dados compatíveis com encaminhamento, consulte Contratos de dados compatíveis
com encaminhamento. Para obter mais informações sobre contratos de dados, confira
Como usar contratos de dados.
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Runtime.Serialization.XsdDataCo
ntractExporter classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Use a classe quando você tiver criado um serviço Web que incorpora dados
representados por tipos CLR (Common Language Runtime) e quando precisar exportar
esquemas XML para cada tipo a XsdDataContractExporter ser consumido por outros
serviços Web. Ou seja, XsdDataContractExporter transforma um conjunto de tipos CLR
em esquemas XML. (Para obter mais informações sobre os tipos que podem ser usados,
consulte Tipos suportados pelo serializador de contrato de dados.) Os esquemas podem
ser expostos por meio de um documento WSDL (Web Services Description Language)
para uso por outras pessoas que precisam interoperar com seu serviço.

Por outro lado, se você estiver criando um serviço Web que deve interoperar com um
serviço Web existente, use o XsdDataContractImporter para transformar esquemas XML
e criar os tipos CLR que representam os dados em uma linguagem de programação
selecionada.

O XsdDataContractExporter gera um XmlSchemaSet objeto que contém a coleção de


esquemas. Acesse o conjunto de esquemas através da Schemas() propriedade.

7 Observação

Para gerar rapidamente arquivos de definição de esquema XML (XSD) que outros
serviços da Web podem consumir, use o XsdDataContractExporter.

Exportar esquemas para um XmlSchemaSet


Para criar uma instância da XmlSchemaSet classe que contém arquivos de esquema
XML, você deve estar ciente do seguinte.

O conjunto de tipos que você está exportando é registrado como um conjunto interno
de contratos de dados. Assim, você pode chamar o método várias vezes para adicionar
novos tipos ao conjunto de esquemas sem degradar o CanExport desempenho, pois
somente os novos tipos serão adicionados ao conjunto. Durante a Export operação, os
esquemas existentes são comparados com os novos esquemas que estão sendo
adicionados. Se houver conflitos, uma exceção será lançada. Um conflito geralmente é
detectado se dois tipos com o mesmo nome de contrato de dados, mas contratos
diferentes (membros diferentes) são exportados pela mesma XsdDataContractExporter
instância.

Use o exportador
Uma maneira recomendada de usar essa classe é a seguinte:

1. Use uma das CanExport sobrecargas para determinar se o tipo especificado ou o


conjunto de tipos pode ser exportado. Use uma das sobrecargas apropriadas às
suas necessidades.

2. Chame o método correspondente Export .

3. Recupere os esquemas da Schemas propriedade.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Visão geral de System.CommandLine
Artigo • 27/01/2024

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

A biblioteca System.CommandLine fornece funcionalidades que são normalmente


necessárias para aplicativos de linha de comando, como a análise da entrada da linha de
comando e a exibição do texto de ajuda.

Os aplicativos que usam System.CommandLine incluem a CLI do .NET, ferramentas


adicionais e muitas ferramentas globais e locais.

Para desenvolvedores de aplicativos, a biblioteca:

Permite se concentrar na elaboração do código do aplicativo, pois não é


necessário escrever códigos para analisar a entrada da linha de comando ou
produzir uma página de ajuda.
Permite testar o código do aplicativo independentemente do código de análise de
entrada.
É compatível com corte, o que a torna uma boa opção para desenvolver um
aplicativo de CLI rápido, leve e compatível com AOT.

O uso da biblioteca também beneficia os usuários do aplicativo:

Ela garante que a entrada de linha de comando seja analisada consistentemente


de acordo com convenções POSIX ou do Windows.
Ela dá suporte automático ao preenchimento de guias e a arquivos de resposta.

Pacote NuGet
A biblioteca está disponível em um pacote NuGet:

System.CommandLine
Próximas etapas
Para se familiarizar com System.CommandLine, confira os seguintes recursos:

Tutorial: introdução a System.CommandLine


Visão geral da sintaxe de linha de comando

Para saber mais, consulte os seguintes recursos:

Como definir comandos, opções e argumentos


Como associar argumentos a manipuladores
Como configurar a injeção de dependência
Como habilitar e personalizar o preenchimento de guias
Como personalizar a ajuda
Como lidar com o encerramento
Como gravar middlewares e diretivas
System.CommandLine Referência da API

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Tutorial: Introdução a
System.CommandLine
Artigo • 08/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Este tutorial mostra como criar um aplicativo de linha de comando .NET que usa a
biblioteca System.CommandLine. Você começará criando um comando raiz simples que
tenha uma opção. Em seguida, adicionará a essa base, criando um aplicativo mais
complexo que contém vários subcomandos e opções diferentes para cada comando.

Neste tutorial, você aprenderá a:

" Criar comandos, opções e argumentos ocultos.


" Especificar valores padrão para opções.
" Atribuir opções e argumentos a comandos.
" Atribuir uma opção recursivamente a todos os subcomandos sob um comando.
" Trabalhar com vários níveis de subcomandos aninhados.
" Criar aliases para comandos e opções.
" Trabalhar com string , string[] , int , bool , FileInfo e tipos de opção de
enumeração.
" Associar valores de opção ao código do manipulador de comandos.
" Usar o código personalizado para analisar e validar opções.

Pré-requisitos
Um editor de código, como Visual Studio Code com a extensão C# .
O SDK do .NET 6 .

Ou

Visual Studio 2022 com a carga de trabalho Desenvolvimento de área de trabalho


do .NET instalada.
Criar o aplicativo
Crie um projeto de aplicativo de console do .NET 6 chamado "scl".

1. Crie uma pasta chamada scl para o projeto e abra um prompt de comando na
nova pasta.

2. Execute o comando a seguir:

CLI do .NET

dotnet new console --framework net6.0

Instalar o pacote System.CommandLine


Execute o comando a seguir:

CLI do .NET

dotnet add package System.CommandLine --prerelease

A opção --prerelease é necessária porque a biblioteca ainda está em beta.

1. Substitua o conteúdo do Program.cs pelo seguinte código:

C#

using System.CommandLine;

namespace scl;

class Program
{
static async Task<int> Main(string[] args)
{
var fileOption = new Option<FileInfo?>(
name: "--file",
description: "The file to read and display on the
console.");

var rootCommand = new RootCommand("Sample app for


System.CommandLine");
rootCommand.AddOption(fileOption);

rootCommand.SetHandler((file) =>
{
ReadFile(file!);
},
fileOption);

return await rootCommand.InvokeAsync(args);


}

static void ReadFile(FileInfo file)


{
File.ReadLines(file.FullName).ToList()
.ForEach(line => Console.WriteLine(line));
}
}

O código anterior:

Cria uma opção chamada --file do tipo FileInfo e a atribui ao comando raiz:

C#

var fileOption = new Option<FileInfo?>(


name: "--file",
description: "The file to read and display on the console.");

var rootCommand = new RootCommand("Sample app for System.CommandLine");


rootCommand.AddOption(fileOption);

Especifica que ReadFile é o método que será chamado quando o comando raiz
for invocado:

C#

rootCommand.SetHandler((file) =>
{
ReadFile(file!);
},
fileOption);

Exibe o conteúdo do arquivo especificado quando o comando raiz é invocado:

C#

static void ReadFile(FileInfo file)


{
File.ReadLines(file.FullName).ToList()
.ForEach(line => Console.WriteLine(line));
}
Testar o aplicativo
Você pode usar qualquer uma das seguintes maneiras de testar ao desenvolver um
aplicativo de linha de comando:

Execute o comando dotnet build e abra um prompt de comando na pasta


scl/bin/Debug/net6.0 para executar o executável:

Console

dotnet build
cd bin/Debug/net6.0
scl --file scl.runtimeconfig.json

Use dotnet run e passe valores de opção para o aplicativo em vez de para o
comando run , incluindo-os após -- , como no seguinte exemplo:

CLI do .NET

dotnet run -- --file scl.runtimeconfig.json

No .NET 7.0.100 SDK (versão prévia), você pode usar o commandLineArgs de um


arquivo launchSettings.json executando o comando dotnet run --launch-profile
<profilename> .

Publique o projeto em uma pasta, abra um prompt de comando para essa pasta e
execute o executável:

Console

dotnet publish -o publish


cd ./publish
scl --file scl.runtimeconfig.json

No Visual Studio 2022, selecione Depurar>Propriedades da Depuração no menu e


insira as opções e argumentos na caixa Argumentos de linha de comando. Por
exemplo:
Em seguida, execute o aplicativo, por exemplo, pressionando Ctrl+F5.

Este tutorial pressupõe que você esteja usando a primeira dessas opções.

Quando você executa o aplicativo, ele exibe o conteúdo do arquivo especificado pela
opção --file .

Saída

{
"runtimeOptions": {
"tfm": "net6.0",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
}
}
}

Saída de Ajuda
A System.CommandLine fornece automaticamente a saída de ajuda:

Console

scl --help

Saída

Description:
Sample app for System.CommandLine

Usage:
scl [options]

Options:
--file <file> The file to read and display on the console.
--version Show version information
-?, -h, --help Show help and usage information
Saída de versão
A System.CommandLine fornece automaticamente a saída da versão:

Console

scl --version

Saída

1.0.0

Adicionar um subcomando e opções


Nesta seção, você:

Crie mais opções.


Crie um subcomando.
Atribua as novas opções ao novo subcomando.

As novas opções permitirão que você configure as cores de texto em primeiro plano e
plano de fundo e a velocidade de leitura. Esses recursos serão usados para ler uma
coleção de aspas provenientes do Tutorial do aplicativo de console Teleprompter.

1. Copie o arquivo sampleQuotes.txt do repositório do GitHub para este exemplo


no diretório de seu projeto. Para obter informações sobre como baixar arquivos,
confira as instruções em Exemplos e tutoriais.

2. Abra o arquivo de projeto e adicione um elemento <ItemGroup> pouco antes da


marca de fechamento </Project> :

XML

<ItemGroup>
<Content Include="sampleQuotes.txt">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

Adicionar essa marcação faz com que o arquivo de texto seja copiado para a pasta
bin/debug/net6.0 ao compilar o aplicativo. Portanto, ao executar o executável
nessa pasta, você pode acessar o arquivo pelo nome sem especificar um caminho
de pasta.

3. Em Program.cs, após o código que cria a opção --file , crie opções para controlar
a velocidade de leitura e as cores do texto:

C#

var delayOption = new Option<int>(


name: "--delay",
description: "Delay between lines, specified as milliseconds per
character in a line.",
getDefaultValue: () => 42);

var fgcolorOption = new Option<ConsoleColor>(


name: "--fgcolor",
description: "Foreground color of text displayed on the console.",
getDefaultValue: () => ConsoleColor.White);

var lightModeOption = new Option<bool>(


name: "--light-mode",
description: "Background color of text displayed on the console:
default is black, light mode is white.");

4. Depois da linha que cria o comando raiz, exclua a linha que adiciona a opção --
file a ela. Você a está removendo aqui porque vai adicioná-la a um novo
subcomando.

C#

var rootCommand = new RootCommand("Sample app for System.CommandLine");


//rootCommand.AddOption(fileOption);

5. Depois da linha que cria o comando raiz, crie um subcomando read . Adicione as
opções a esse subcomando e adicione o subcomando ao comando raiz.

C#

var readCommand = new Command("read", "Read and display the file.")


{
fileOption,
delayOption,
fgcolorOption,
lightModeOption
};
rootCommand.AddCommand(readCommand);
6. Substitua o código SetHandler pelo seguinte código SetHandler para o novo
subcomando:

C#

readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>


{
await ReadFile(file!, delay, fgcolor, lightMode);
},
fileOption, delayOption, fgcolorOption, lightModeOption);

Você não está mais chamando SetHandler no comando raiz porque o comando
raiz não precisa mais de um manipulador. Quando um comando tem
subcomandos, normalmente você precisa especificar um dos subcomandos ao
invocar um aplicativo de linha de comando.

7. Substitua o método do manipulador ReadFile pelo seguinte código:

C#

internal static async Task ReadFile(


FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
{
Console.BackgroundColor = lightMode ? ConsoleColor.White :
ConsoleColor.Black;
Console.ForegroundColor = fgColor;
List<string> lines = File.ReadLines(file.FullName).ToList();
foreach (string line in lines)
{
Console.WriteLine(line);
await Task.Delay(delay * line.Length);
};
}

O aplicativo agora tem esta aparência:

C#

using System.CommandLine;

namespace scl;

class Program
{
static int Main(string[] args)
{
var fileOption = new Option<FileInfo?>(
name: "--file",
description: "The file to read and display on the console.");
var delayOption = new Option<int>(
name: "--delay",
description: "Delay between lines, specified as milliseconds per
character in a line.",
getDefaultValue: () => 42);

var fgcolorOption = new Option<ConsoleColor>(


name: "--fgcolor",
description: "Foreground color of text displayed on the
console.",
getDefaultValue: () => ConsoleColor.White);

var lightModeOption = new Option<bool>(


name: "--light-mode",
description: "Background color of text displayed on the console:
default is black, light mode is white.");

var rootCommand = new RootCommand("Sample app for


System.CommandLine");
//rootCommand.AddOption(fileOption);

var readCommand = new Command("read", "Read and display the file.")


{
fileOption,
delayOption,
fgcolorOption,
lightModeOption
};
rootCommand.AddCommand(readCommand);

readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>


{
await ReadFile(file!, delay, fgcolor, lightMode);
},
fileOption, delayOption, fgcolorOption, lightModeOption);

return rootCommand.InvokeAsync(args).Result;
}

internal static async Task ReadFile(


FileInfo file, int delay, ConsoleColor fgColor, bool lightMode)
{
Console.BackgroundColor = lightMode ? ConsoleColor.White :
ConsoleColor.Black;
Console.ForegroundColor = fgColor;
List<string> lines = File.ReadLines(file.FullName).ToList();
foreach (string line in lines)
{
Console.WriteLine(line);
await Task.Delay(delay * line.Length);
};
}
}
Testar o novo subcomando
Agora, se você tentar executar o aplicativo sem especificar o subcomando, receberá
uma mensagem de erro seguida de uma mensagem de ajuda que especifica o
subcomando disponível.

Console

scl --file sampleQuotes.txt

Saída

'--file' was not matched. Did you mean one of the following?
--help
Required command was not provided.
Unrecognized command or argument '--file'.
Unrecognized command or argument 'sampleQuotes.txt'.

Description:
Sample app for System.CommandLine

Usage:
scl [command] [options]

Options:
--version Show version information
-?, -h, --help Show help and usage information

Commands:
read Read and display the file.

O texto de ajuda para subcomando read mostra que quatro opções estão disponíveis.
Ele mostra valores válidos para a enumeração.

Console

scl read -h

Saída

Description:
Read and display the file.

Usage:
scl read [options]

Options:
--file <file> The file to
read and display on the console.
--delay <delay> Delay between
lines, specified as milliseconds per
character in a
line. [default: 42]
--fgcolor Foreground
color of text displayed on the console.
<Black|Blue|Cyan|DarkBlue|DarkCyan|DarkGray|DarkGreen|Dark [default:
White]
Magenta|DarkRed|DarkYellow|Gray|Green|Magenta|Red|White|Ye
llow>
--light-mode Background
color of text displayed on the console:
default is
black, light mode is white.
-?, -h, --help Show help and
usage information

Execute subcomando especificando read apenas a opção --file e você obterá os


valores padrão para as outras três opções.

Console

scl read --file sampleQuotes.txt

O atraso padrão de 42 milissegundos por caractere causa uma velocidade de leitura


lenta. Você pode acelerá-lo configurando --delay para um número menor.

Console

scl read --file sampleQuotes.txt --delay 0

Você pode usar --fgcolor e --light-mode definir cores de texto:

Console

scl read --file sampleQuotes.txt --fgcolor red --light-mode

Forneça um valor --delay inválido e você receberá uma mensagem de erro:

Console

scl read --file sampleQuotes.txt --delay forty-two

Saída
Cannot parse argument 'forty-two' for option '--int' as expected type
'System.Int32'.

Forneça um valor --file inválido e você obterá uma exceção:

Console

scl read --file nofile

Saída

Unhandled exception: System.IO.FileNotFoundException:


Could not find file 'C:\bin\Debug\net6.0\nofile'.

Adicionar subcomandos e validação


personalizada
Esta seção cria a versão final do aplicativo. Ao terminar, o aplicativo terá os seguintes
comandos e opções:

comando raiz com uma opção global* chamada --file


Comando quotes
comando read com opções chamadas --delay , --fgcolor e --light-mode
comando add com argumentos chamados quote e byline
comando delete com a opção chamada --search-terms

* Uma opção global está disponível para o comando ao qual ela é atribuída e
recursivamente a todos os seus subcomandos.

Aqui está uma entrada de linha de comando de exemplo que invoca cada um dos
comandos disponíveis com suas opções e argumentos:

Console

scl quotes read --file sampleQuotes.txt --delay 40 --fgcolor red --light-


mode
scl quotes add "Hello world!" "Nancy Davolio"
scl quotes delete --search-terms David "You can do" Antoine "Perfection is
achieved"

1. Em Program.cs, substitua o código que cria a opção --file pelo seguinte código:
C#

var fileOption = new Option<FileInfo?>(


name: "--file",
description: "An option whose argument is parsed as a FileInfo",
isDefault: true,
parseArgument: result =>
{
if (result.Tokens.Count == 0)
{
return new FileInfo("sampleQuotes.txt");

}
string? filePath = result.Tokens.Single().Value;
if (!File.Exists(filePath))
{
result.ErrorMessage = "File does not exist";
return null;
}
else
{
return new FileInfo(filePath);
}
});

Esse código usa ParseArgument<T> para fornecer análise personalizada, validação


e tratamento de erro.

Sem esse código, arquivos ausentes são relatados com uma exceção e
rastreamento de pilha. Com esse código, apenas a mensagem de erro especificada
é exibida.

Esse código também especifica um valor padrão, e é por isso que ele define
isDefault como true . Se você não definir isDefault como true , o delegado
parseArgument não será chamado quando nenhuma entrada for fornecida para --

file .

2. Depois do código que cria lightModeOption , adicione opções e argumentos para


os comandos add e delete :

C#

var searchTermsOption = new Option<string[]>(


name: "--search-terms",
description: "Strings to search for when deleting entries.")
{ IsRequired = true, AllowMultipleArgumentsPerToken = true };

var quoteArgument = new Argument<string>(


name: "quote",
description: "Text of quote.");

var bylineArgument = new Argument<string>(


name: "byline",
description: "Byline of quote.");

A configuração AllowMultipleArgumentsPerToken permite omitir o nome da opção


--search-terms ao especificar elementos na lista após a primeira. Ele torna os
seguintes exemplos de entrada de linha de comando equivalentes:

Console

scl quotes delete --search-terms David "You can do"


scl quotes delete --search-terms David --search-terms "You can do"

3. Substitua o código que cria o comando raiz e o comando read pelo seguinte
código:

C#

var rootCommand = new RootCommand("Sample app for System.CommandLine");


rootCommand.AddGlobalOption(fileOption);

var quotesCommand = new Command("quotes", "Work with a file that


contains quotes.");
rootCommand.AddCommand(quotesCommand);

var readCommand = new Command("read", "Read and display the file.")


{
delayOption,
fgcolorOption,
lightModeOption
};
quotesCommand.AddCommand(readCommand);

var deleteCommand = new Command("delete", "Delete lines from the


file.");
deleteCommand.AddOption(searchTermsOption);
quotesCommand.AddCommand(deleteCommand);

var addCommand = new Command("add", "Add an entry to the file.");


addCommand.AddArgument(quoteArgument);
addCommand.AddArgument(bylineArgument);
addCommand.AddAlias("insert");
quotesCommand.AddCommand(addCommand);

Este código faz as seguintes alterações:

Remove a opção --file do comando read .


Adiciona a opção --file como uma opção global ao comando raiz.

Cria um comando quotes e o adiciona ao comando raiz.

Adiciona o comando read ao comando quotes em vez do comando raiz.

Cria os comandos add e delete e os adiciona ao comando quotes .

O resultado é a seguinte hierarquia de comandos:

Comando raiz
quotes
read

add
delete

O aplicativo agora implementa o padrão recomendado em que o comando pai


( quotes ) especifica uma área ou grupo e seus comandos filho ( read , add , delete )
são ações.

As opções globais são aplicadas ao comando e recursivamente a subcomandos.


Como --file está no comando raiz, ele estará disponível automaticamente em
todos os subcomandos do aplicativo.

4. Após o código SetHandler , adicione um código SetHandler aos novos


subcomandos:

C#

deleteCommand.SetHandler((file, searchTerms) =>


{
DeleteFromFile(file!, searchTerms);
},
fileOption, searchTermsOption);

addCommand.SetHandler((file, quote, byline) =>


{
AddToFile(file!, quote, byline);
},
fileOption, quoteArgument, bylineArgument);

O subcomando quotes não tem um manipulador porque não é um comando


folha. Os subcomandos read , add e delete são comandos folha sob quotes , e
SetHandler é chamado para cada um deles.

5. Adicione os manipuladores para add e delete .


C#

internal static void DeleteFromFile(FileInfo file, string[]


searchTerms)
{
Console.WriteLine("Deleting from file");
File.WriteAllLines(
file.FullName, File.ReadLines(file.FullName)
.Where(line => searchTerms.All(s =>
!line.Contains(s))).ToList());
}
internal static void AddToFile(FileInfo file, string quote, string
byline)
{
Console.WriteLine("Adding to file");
using StreamWriter? writer = file.AppendText();
writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}
{quote}");
writer.WriteLine($"{Environment.NewLine}-{byline}");
writer.Flush();
}

O aplicativo concluído tem esta aparência:

C#

using System.CommandLine;

namespace scl;

class Program
{
static async Task<int> Main(string[] args)
{
var fileOption = new Option<FileInfo?>(
name: "--file",
description: "An option whose argument is parsed as a FileInfo",
isDefault: true,
parseArgument: result =>
{
if (result.Tokens.Count == 0)
{
return new FileInfo("sampleQuotes.txt");

}
string? filePath = result.Tokens.Single().Value;
if (!File.Exists(filePath))
{
result.ErrorMessage = "File does not exist";
return null;
}
else
{
return new FileInfo(filePath);
}
});

var delayOption = new Option<int>(


name: "--delay",
description: "Delay between lines, specified as milliseconds per
character in a line.",
getDefaultValue: () => 42);

var fgcolorOption = new Option<ConsoleColor>(


name: "--fgcolor",
description: "Foreground color of text displayed on the
console.",
getDefaultValue: () => ConsoleColor.White);

var lightModeOption = new Option<bool>(


name: "--light-mode",
description: "Background color of text displayed on the console:
default is black, light mode is white.");

var searchTermsOption = new Option<string[]>(


name: "--search-terms",
description: "Strings to search for when deleting entries.")
{ IsRequired = true, AllowMultipleArgumentsPerToken = true };

var quoteArgument = new Argument<string>(


name: "quote",
description: "Text of quote.");

var bylineArgument = new Argument<string>(


name: "byline",
description: "Byline of quote.");

var rootCommand = new RootCommand("Sample app for


System.CommandLine");
rootCommand.AddGlobalOption(fileOption);

var quotesCommand = new Command("quotes", "Work with a file that


contains quotes.");
rootCommand.AddCommand(quotesCommand);

var readCommand = new Command("read", "Read and display the file.")


{
delayOption,
fgcolorOption,
lightModeOption
};
quotesCommand.AddCommand(readCommand);

var deleteCommand = new Command("delete", "Delete lines from the


file.");
deleteCommand.AddOption(searchTermsOption);
quotesCommand.AddCommand(deleteCommand);
var addCommand = new Command("add", "Add an entry to the file.");
addCommand.AddArgument(quoteArgument);
addCommand.AddArgument(bylineArgument);
addCommand.AddAlias("insert");
quotesCommand.AddCommand(addCommand);

readCommand.SetHandler(async (file, delay, fgcolor, lightMode) =>


{
await ReadFile(file!, delay, fgcolor, lightMode);
},
fileOption, delayOption, fgcolorOption, lightModeOption);

deleteCommand.SetHandler((file, searchTerms) =>


{
DeleteFromFile(file!, searchTerms);
},
fileOption, searchTermsOption);

addCommand.SetHandler((file, quote, byline) =>


{
AddToFile(file!, quote, byline);
},
fileOption, quoteArgument, bylineArgument);

return await rootCommand.InvokeAsync(args);


}

internal static async Task ReadFile(


FileInfo file, int delay, ConsoleColor fgColor, bool
lightMode)
{
Console.BackgroundColor = lightMode ? ConsoleColor.White :
ConsoleColor.Black;
Console.ForegroundColor = fgColor;
var lines = File.ReadLines(file.FullName).ToList();
foreach (string line in lines)
{
Console.WriteLine(line);
await Task.Delay(delay * line.Length);
};

}
internal static void DeleteFromFile(FileInfo file, string[] searchTerms)
{
Console.WriteLine("Deleting from file");
File.WriteAllLines(
file.FullName, File.ReadLines(file.FullName)
.Where(line => searchTerms.All(s =>
!line.Contains(s))).ToList());
}
internal static void AddToFile(FileInfo file, string quote, string
byline)
{
Console.WriteLine("Adding to file");
using StreamWriter? writer = file.AppendText();
writer.WriteLine($"{Environment.NewLine}{Environment.NewLine}
{quote}");
writer.WriteLine($"{Environment.NewLine}-{byline}");
writer.Flush();
}
}

Crie o projeto e tente os comandos a seguir.

Envie um arquivo inexistente com comando --file o read e você obtém uma
mensagem de erro em vez de uma exceção e um rastreamento de pilha:

Console

scl quotes read --file nofile

Saída

File does not exist

Tente executar subcomando quotes e você recebe uma mensagem direcionando você a
usar read , add ou delete :

Console

scl quotes

Saída

Required command was not provided.

Description:
Work with a file that contains quotes.

Usage:
scl quotes [command] [options]

Options:
--file <file> An option whose argument is parsed as a FileInfo [default:
sampleQuotes.txt]
-?, -h, --help Show help and usage information

Commands:
read Read and display the file.
delete Delete lines from the file.
add, insert <quote> <byline> Add an entry to the file.
Execute o subcomando add e examine o final do arquivo de texto para ver o texto
adicionado:

Console

scl quotes add "Hello world!" "Nancy Davolio"

Execute subcomando delete com cadeias de caracteres de pesquisa desde o início do


arquivo e examine o início do arquivo de texto para ver em que ponto o texto foi
removido:

Console

scl quotes delete --search-terms David "You can do" Antoine "Perfection is
achieved"

7 Observação

Se você estiver executando na pasta bin/debug/net6.0, será nessa pasta que você
encontrará o arquivo com alterações dos comandos add e delete . A cópia do
arquivo na pasta do projeto permanece inalterada.

Próximas etapas
Neste tutorial, você criou um aplicativo de linha de comando simples que usa
System.CommandLine . Para saber mais sobre a biblioteca, confira Visão geral de

System.CommandLine.
Visão geral da sintaxe de linha de
comando para System.CommandLine
Artigo • 08/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Este artigo explica a sintaxe de linha de comando que o System.CommandLine reconhece.


As informações serão úteis para usuários e desenvolvedores de aplicativos de linha de
comando do .NET, incluindo a CLI do .NET.

Tokens
System.CommandLine analisa a entrada de linha de comando em tokens, que são cadeias
de caracteres delimitadas por espaços. Por exemplo, considere a seguinte linha de
comando:

CLI do .NET

dotnet tool install dotnet-suggest --global --verbosity quiet

Essa entrada é analisada pelo aplicativo dotnet nos tokens tool , install , dotnet-
suggest , --global , --verbosity e quiet .

Os tokens são interpretados como comandos, opções ou argumentos. O aplicativo de


linha de comando que está sendo invocado determina como os tokens após o primeiro
são interpretados. A tabela abaixo mostra como System.CommandLine interpreta o
exemplo anterior:

Token Analisado como

tool Subcomando

install Subcomando
Token Analisado como

dotnet-suggest Argumento para o comando de instalação

--global Opção para o comando de instalação

--verbosity Opção para o comando de instalação

quiet Argumento para a opção --verbosity

Um token poderá conter espaços se estiver entre aspas ( " ). Veja um exemplo:

Console

dotnet tool search "ef migrations add"

Comandos
Um comando na entrada de linha de comando é um token que especifica uma ação ou
define um grupo de ações relacionadas. Por exemplo:

Em dotnet run , run é um comando que especifica uma ação.


Em dotnet tool install , install é um comando que especifica uma ação e tool
é um comando que especifica um grupo de comandos relacionados. Há outros
comandos relacionados a ferramentas, como tool uninstall , tool list e tool
update .

Comandos raiz
O comando raiz é aquele que especifica o nome do executável do aplicativo. Por
exemplo, o comando dotnet especifica o dotnet.exe executável.

Subcomandos
A maioria dos aplicativos de linha de comando dá suporte a subcomandos, também
conhecidos como verbos. Por exemplo, o comando dotnet tem um subcomando run
que você invoca inserindo dotnet run .

Subcomandos podem ter seus próprios subcomandos. Em dotnet tool install ,


install é um subcomando de tool .
Opções
Uma opção é um parâmetro nomeado que pode ser transmitido a um comando. A
convenção POSIX é prefixar o nome da opção com dois hifens ( -- ). O exemplo abaixo
mostra duas opções:

CLI do .NET

dotnet tool update dotnet-suggest --verbosity quiet --global


^---------^ ^------^

Como ilustrado por este exemplo, o valor da opção pode ser explícito ( quiet para --
verbosity ) ou implícito (nada segue --global ). As opções que não têm nenhum valor
especificado normalmente são parâmetros boolianos que assumem o padrão de true
se a opção é especificada na linha de comando.

Para alguns aplicativos de linha de comando do Windows, você identifica uma opção
usando uma barra à esquerda ( / ) com o nome da opção. Por exemplo:

Console

msbuild /version
^------^

System.CommandLine dá suporte a convenções de prefixo POSIX e Windows. Ao


configurar uma opção, especifique o nome da opção incluindo o prefixo.

Argumentos
Um argumento é um valor transmitido a uma opção ou a um comando. Os exemplos a
seguir mostram um argumento para a opção verbosity e um argumento para o
comando build .

Console

dotnet tool update dotnet-suggest --verbosity quiet --global


^---^

Console

dotnet build myapp.csproj


^----------^
Os argumentos podem ter valores padrão que se aplicarão se nenhum argumento for
fornecido explicitamente. Por exemplo, muitas opções são parâmetros implicitamente
boolianos com um padrão de true quando o nome da opção está na linha de
comando. Os exemplos de linha de comando abaixo são equivalentes:

CLI do .NET

dotnet tool update dotnet-suggest --global


^------^

dotnet tool update dotnet-suggest --global true


^-----------^

Algumas opções exigem argumentos. Por exemplo, na CLI do .NET, --output requer um
argumento de nome de pasta. Se o argumento não for fornecido, o comando falhará.

Os argumentos podem ter tipos esperados, e System.CommandLine exibe uma mensagem


de erro quando um argumento não pode ser analisado no tipo esperado. Por exemplo,
os seguintes erros de comando por "silencioso" não ser um dos valores válidos para --
verbosity :

CLI do .NET

dotnet build --verbosity silent

Saída

Cannot parse argument 'silent' for option '-v' as expected type


'Microsoft.DotNet.Cli.VerbosityOptions'. Did you mean one of the following?
Detailed
Diagnostic
Minimal
Normal
Quiet

Os argumentos também têm expectativas sobre quantos valores podem ser fornecidos.
Exemplos são fornecidos na seção sobre arity de argumento.

Ordem de opções e argumentos


Você pode fornecer opções antes de argumentos ou argumentos antes das opções na
linha de comando. Os seguintes comandos são equivalentes:

CLI do .NET
dotnet add package System.CommandLine --prerelease
dotnet add package --prerelease System.CommandLine

As opções podem ser especificadas em qualquer ordem. Os seguintes comandos são


equivalentes:

CLI do .NET

dotnet add package System.CommandLine --prerelease --no-restore --source


https://api.nuget.org/v3/index.json
dotnet add package System.CommandLine --source
https://api.nuget.org/v3/index.json --no-restore --prerelease

Quando há vários argumentos, a ordem importa. Os seguintes comandos não são


necessariamente equivalentes:

Console

myapp argument1 argument2


myapp argument2 argument1

Esses comandos transmitem uma lista com os mesmos valores ao código do


manipulador de comandos, mas eles diferem na ordem dos valores, o que pode levar a
resultados diferentes.

Aliases
No POSIX e no Windows, é comum que alguns comandos e opções tenham aliases.
Geralmente, são formas curtas, mais fáceis de digitar. Os aliases também podem ser
usados para outras finalidades, como simular a falta de diferenciação de maiúsculas e
minúsculas e dar suporte a ortografias alternativas de uma palavra.

Normalmente, as formas curtas POSIX têm um único hífen à esquerda seguido por um
único caractere. Os seguintes comandos são equivalentes:

CLI do .NET

dotnet build --verbosity quiet


dotnet build -v quiet

O padrão GNU recomenda aliases automáticos. Ou seja, você pode inserir qualquer
parte de um comando ou nome de opção de forma longa e ela será aceita. Esse
comportamento tornaria as seguintes linhas de comando equivalentes:
CLI do .NET

dotnet publish --output ./publish


dotnet publish --outpu ./publish
dotnet publish --outp ./publish
dotnet publish --out ./publish
dotnet publish --ou ./publish
dotnet publish --o ./publish

System.CommandLine não dá suporte a aliases automáticos.

Diferenciar maiúsculas de minúsculas


Nomes e aliases de comando e opções diferenciam maiúsculas de minúsculas por
padrão de acordo com a convenção POSIX, e System.CommandLine segue essa
convenção. Se você quiser que sua CLI não diferencie maiúsculas de minúsculas, defina
aliases para as várias alternativas de maiúsculas e minúsculas. Por exemplo, --
additional-probing-path poderia ter aliases --Additional-Probing-Path e --ADDITIONAL-

PROBING-PATH .

Em algumas ferramentas de linha de comando, uma diferença no uso de maiúsculas e


minúsculas especifica uma diferença na função. Por exemplo, git clean -X se comporta
de forma diferente de git clean -x . A CLI do .NET é toda em minúsculas.

A diferenciação de maiúsculas e minúsculas não se aplica a valores de argumento em


opções baseadas em enumerações. Os nomes de enumeração têm correspondência
independentemente do uso de maiúsculas e minúsculas.

O token --
A convenção POSIX interpreta o token de traço duplo ( -- ) como um mecanismo de
escape. Tudo o que segue o token de traço duplo é interpretado como argumentos
para o comando. Essa funcionalidade pode ser usada para enviar argumentos que se
parecem com opções, pois impede que eles sejam interpretados como opções.

Digamos que myapp use um argumento message e você queira que o valor de message
seja --interactive . A linha de comando a seguir pode fornecer resultados inesperados.

Console

myapp --interactive
Se myapp não tiver uma opção --interactive , o token --interactive será interpretado
como um argumento. Mas se o aplicativo tiver uma opção --interactive , essa entrada
será interpretada como referenciando essa opção.

A linha de comando abaixo usa o token de traço duplo para definir o valor do
argumento message como "--interactive":

Console

myapp -- --interactive
^^

System.CommandLine dá suporte a essa funcionalidade de traço duplo.

Delimitadores de argumento de opção


System.CommandLine permite que você use um espaço, '=' ou ':' como delimitador entre

um nome de opção e seu argumento. Por exemplo, os comandos abaixo são


equivalentes:

CLI do .NET

dotnet build -v quiet


dotnet build -v=quiet
dotnet build -v:quiet

Uma convenção POSIX permite omitir o delimitador quando você está especificando um
alias de opção de caractere único. Por exemplo, os comandos abaixo são equivalentes:

Console

myapp -vquiet
myapp -v quiet

System.CommandLine dá suporte a essa sintaxe por padrão.

Arity de argumento
O arity de uma opção ou argumento de comando é o número de valores que podem
ser transmitidos se essa opção ou comando é especificada.
O arity é expresso com um valor mínimo e um valor máximo, como ilustra a tabela
abaixo:

Mín Max Validade de exemplo Exemplo

0 0 Válido: --file

Inválido: --file a.json

Inválido: --file a.json --file b.json

0 1 Válido: --flag

Válido: --flag true

Válido: --flag false

Inválido: --flag false --flag false

1 1 Válido: --file a.json

Inválido: --file

Inválido: --file a.json --file b.json

0 n Válido: --file

Válido: --file a.json

Válido: --file a.json --file b.json

1 n Válido: --file a.json

Válido: --file a.json b.json

Inválido: --file

System.CommandLine tem um struct ArgumentArity para definir o arity, com os seguintes

valores:

Zero – Nenhum valor permitido.


ZeroOrOne – Pode ter um valor, pode não ter valores.
ExactlyOne – Precisa ter um valor.
ZeroOrMore – Pode ter um valor, vários valores ou nenhum valor.
OneOrMore – Pode ter vários valores, precisa ter pelo menos um valor.

Geralmente, o arity pode ser inferido do tipo. Por exemplo, uma opção int tem aridade
de ExactlyOne , e uma opção List<int> tem arity OneOrMore .
Substituições de opção
Se o máximo de arity for 1, System.CommandLine ainda poderá ser configurada para
aceitar várias instâncias de uma opção. Nesse caso, a última instância de uma opção
repetida substitui todas as instâncias anteriores. No exemplo a seguir, o valor 2 seria
transmitido ao comando myapp .

Console

myapp --delay 3 --message example --delay 2

Vários argumentos
Se o máximo de arity for maior que um, System.CommandLine poderá ser configurado a
fim de aceitar vários argumentos para uma opção sem repetir o nome da opção.

No exemplo abaixo, a lista transmitida ao comando myapp conteria "a", "b", "c" e "d":

Console

myapp --list a b c --list d

Agrupamento de opções
O POSIX recomenda que você dê suporte ao agrupamento de opções de caractere
único, também conhecido como empilhamento. As opções agrupadas são aliases de
opção de caractere único especificados juntos após um único prefixo de hífen. Somente
a última opção pode especificar um argumento. Por exemplo, as seguintes linhas de
comando são equivalentes:

Console

git clean -f -d -x
git clean -fdx

Se um argumento for fornecido após um pacote de opções, ele se aplicará à última


opção no pacote. As seguintes linhas de comando são equivalentes:

Console

myapp -a -b -c arg
myapp -abc arg
Em ambas as variantes neste exemplo, o argumento arg se aplicaria somente à opção -
c.

Opções boolianas (sinalizadores)


Se true ou false for transmitido a uma opção que tenha um argumento bool , ele será
analisado conforme o esperado. Mas uma opção cujo tipo de argumento seja bool
normalmente não exige que um argumento seja especificado. As opções boolianas, às
vezes chamadas de "sinalizadores", normalmente têm uma arity de ZeroOrOne. A
presença do nome da opção na linha de comando, sem nenhum argumento a seguir,
resulta em um valor padrão de true . A ausência do nome da opção na entrada de linha
de comando resulta em um valor de false . Se o comando myapp imprimir o valor de
uma opção booliana chamada --interactive , a entrada abaixo criará a seguinte saída:

Console

myapp
myapp --interactive
myapp --interactive false
myapp --interactive true

Saída

False
True
False
True

A opção --help
Normalmente, os aplicativos de linha de comando fornecem uma opção para exibir uma
breve descrição dos comandos, das opções e dos argumentos disponíveis.
System.CommandLine gera automaticamente a saída da ajuda. Por exemplo:

CLI do .NET

dotnet list --help

Saída
Description:
List references or packages of a .NET project.

Usage:
dotnet [options] list [<PROJECT | SOLUTION>] [command]

Arguments:
<PROJECT | SOLUTION> The project or solution file to operate on. If a
file is not specified, the command will search the current directory for
one.

Options:
-?, -h, --help Show command line help.

Commands:
package List all package references of the project or solution.
reference List all project-to-project references of the project.

Os usuários do aplicativo podem estar acostumados a maneiras diferentes de solicitar


ajuda em diferentes plataformas, ou seja, os aplicativos criados com System.CommandLine
respondem a muitas maneiras de solicitar ajuda. Os seguintes comandos são todos
equivalentes:

CLI do .NET

dotnet --help
dotnet -h
dotnet /h
dotnet -?
dotnet /?

A saída da ajuda não mostra necessariamente todos os comandos, argumentos e


opções disponíveis. Alguns deles podem estar ocultos, o que significa que eles não
aparecem na saída da ajuda, mas podem ser especificados na linha de comando.

A opção --version
Os aplicativos criados com System.CommandLine fornecem automaticamente o número
de versão em resposta à opção --version usada com o comando raiz. Por exemplo:

CLI do .NET

dotnet --version

Saída
6.0.100

Arquivos de resposta
Um arquivo de resposta é um arquivo que contém um conjunto de tokens para um
aplicativo de linha de comando. Os arquivos de resposta são um recurso de
System.CommandLine que é útil em dois cenários:

Para invocar um aplicativo de linha de comando especificando uma entrada maior


que o limite de caracteres do terminal.
Para invocar o mesmo comando repetidamente sem redigitar a linha inteira.

Para usar um arquivo de resposta, insira o nome do arquivo prefixado por um sinal @
onde quer que você queira inserir comandos, opções e argumentos. A extensão de
arquivo .rsp é uma convenção comum, mas você pode usar qualquer extensão de
arquivo.

As linhas de comando a seguir são equivalentes:

CLI do .NET

dotnet build --no-restore --output ./build-output/


dotnet @sample1.rsp
dotnet build @sample2.rsp --output ./build-output/

Conteúdo de sample1.rsp:

Console

build
--no-restore
--output
./build-output/

Conteúdo de sample2.rsp:

Console

--no-restore

Aqui estão as regras de sintaxe que determinam como o texto em um arquivo de


resposta é interpretado:
Os tokens são delimitados por espaços. Uma linha que contém Bom dia! é tratada
como dois tokens, Bom e dia!.
Vários tokens entre aspas são interpretados como um único token. Uma linha que
contém “Bom dia!” é tratada como um token, Bom dia!.
Qualquer texto entre um símbolo # e o final da linha é tratado como um
comentário e ignorado.
Os tokens prefixados com @ podem fazer referência a arquivos de resposta
adicionais.
O arquivo de resposta pode ter várias linhas de texto. As linhas são concatenadas e
interpretadas como uma sequência de tokens.

Diretivas
System.CommandLine apresenta um elemento sintático chamado diretiva. A diretiva

[parse] é um exemplo. Quando você inclui [parse] após o nome do aplicativo,

System.CommandLine exibe um diagrama do resultado da análise em vez de invocar o


aplicativo de linha de comando:

CLI do .NET

dotnet [parse] build --no-restore --output ./build-output/


^-----^

Saída

[ dotnet [ build [ --no-restore <True> ] [ --output <./build-output/> ] ] ]

A finalidade das diretivas é fornecer funcionalidades de corte cruzado que podem ser
aplicadas entre aplicativos de linha de comando. Como as diretivas são sintaticamente
distintas da própria sintaxe do aplicativo, elas podem fornecer funcionalidade que se
aplica entre aplicativos.

Uma diretiva precisa estar em conformidade com as seguintes regras de sintaxe:

É um token na linha de comando que vem após o nome do aplicativo, mas antes
de qualquer subcomando ou opções.
Fica entre colchetes.
Não contém espaços.

Uma diretiva não reconhecida é ignorada sem causar um erro de análise.


Uma diretiva pode incluir um argumento, separado do nome da diretiva por dois-
pontos.

As seguintes diretivas são internas:

[parse]
[suggest]

A diretiva [parse]
Os usuários e os desenvolvedores podem achar útil ver como um aplicativo interpretará
determinada entrada. Um dos recursos padrão de um aplicativo System.CommandLine é a
diretiva [parse] , que permite visualizar o resultado da análise da entrada de comando.
Por exemplo:

Console

myapp [parse] --delay not-an-int --interactive --file filename.txt extra

Saída

![ myapp [ --delay !<not-an-int> ] [ --interactive <True> ] [ --file


<filename.txt> ] *[ --fgcolor <White> ] ] ???--> extra

No exemplo anterior:

O comando ( myapp ), suas opções filho e os argumentos para essas opções são
agrupados com colchetes.
Para o resultado da opção [ --delay !<not-an-int> ] , o ! indica um erro de
análise. O valor not-an-int de uma opção int não pode ser analisado para o tipo
esperado. O erro também é sinalizado pelo ! na frente do comando que contém a
opção com erro: ![ myapp... .
Para o resultado da opção *[ --fgcolor <White> ] , a opção não foi especificada
na linha de comando, ou seja, o padrão configurado foi usado. White é o valor
efetivo para essa opção. O asterisco indica que o valor é o padrão.
???--> aponta para a entrada que não foi correspondida com nenhum dos

comandos ou opções do aplicativo.

A diretiva [suggest]
A diretiva [suggest] permite pesquisar comandos quando você não sabe o comando
exato.

CLI do .NET

dotnet [suggest] buil

Saída

build
build-server
msbuild

Diretrizes de design
As seções a seguir apresentam diretrizes que recomendamos que você siga na criação
de uma CLI. Pense no que seu aplicativo espera na linha de comando de forma
semelhante ao que um servidor de API REST espera na URL. Regras consistentes para
APIs REST são o que as tornam prontamente utilizáveis por desenvolvedores de
aplicativos cliente. Da mesma forma, os usuários de seus aplicativos de linha de
comando terão uma experiência melhor se o design da CLI seguir padrões comuns.

Depois de criada uma CLI, é difícil alterá-la, especialmente se os usuários usaram sua CLI
em scripts que esperam manter em execução. As diretrizes aqui foram desenvolvidas de
acordo com a CLI do .NET e nem sempre seguem essas diretrizes. Estamos atualizando a
CLI do .NET para podermos fazer isso sem introduzir alterações significativas. Um
exemplo desse trabalho é o novo design para dotnet new no .NET 7.

Comandos e subcomandos
Se um comando tiver subcomandos, ele deverá funcionar como uma área ou um
identificador de agrupamento para os subcomandos, em vez de especificar uma ação.
Ao invocar o aplicativo, especifique o comando de agrupamento e um de seus
subcomandos. Por exemplo, tente executar dotnet tool ; você receberá uma mensagem
de erro porque o comando tool identifica apenas um grupo de subcomandos
relacionados a ferramentas, como install e list . Você pode executar dotnet tool
install , mas dotnet tool por si só estaria incompleto.

Uma das maneiras pelas quais a definição de áreas ajuda seus usuários é que ela
organiza a saída da ajuda.
Dentro de uma CLI, muitas vezes há uma área implícita. Por exemplo, na CLI do .NET, a
área implícita é o projeto e, na CLI do Docker, é a imagem. Como resultado, você pode
usar dotnet build sem incluir uma área. Considere se sua CLI tem uma área implícita. Se
isso acontecer, considere se deseja permitir que o usuário a inclua ou omita como em
docker build e docker image build . Se, opcionalmente, permitir que a área implícita

seja digitada pelo usuário, você também terá ajuda e preenchimento de guia
automaticamente para esse agrupamento de comandos. Forneça o uso opcional do
grupo implícito definindo dois comandos que executam a mesma operação.

Opções como parâmetros


As opções devem fornecer parâmetros para comandos, em vez de especificar ações em
si. Esse é um princípio de design recomendado, embora nem sempre seja seguido pela
System.CommandLine ( --help exibe informações de ajuda).

Aliases de forma curta


Em geral, recomendamos minimizar o número de aliases de opção de forma curta
definidos.

Em particular, evite usar qualquer um dos seguintes aliases sem ser como o uso comum
na CLI do .NET e em outros aplicativos de linha de comando do .NET:

-i para --interactive .

Essa opção sinaliza ao usuário que ele pode ter que inserir entradas para
perguntas que o comando precisa responder. Por exemplo, a solicitação de um
nome de usuário. Sua CLI pode ser usada em scripts, ou seja, tenha cuidado ao
solicitar a usuários que não especificaram essa opção.

-o para --output .

Alguns comandos produzem arquivos como resultado de sua execução. Essa


opção deve ser usada para ajudar a determinar onde os arquivos devem estar
localizados. Nos casos em que um único arquivo é criado, essa opção deve ser um
caminho de arquivo. Nos casos em que muitos arquivos são criados, essa opção
deve ser um caminho de diretório.

-v para --verbosity .

Os comandos geralmente fornecem saída para o usuário no console; essa opção é


usada para especificar a quantidade de saída que o usuário solicita. Para saber
mais, confira A opção --verbosity mais adiante neste artigo.

Há também alguns aliases com uso comum limitado à CLI do .NET. Você pode usar
esses aliases para outras opções em seus aplicativos, mas esteja ciente da possibilidade
de confusão.

-c para --configuration

Essa opção geralmente se refere a uma Configuração de Build nomeada, por


exemplo, Debug ou Release . Você pode usar qualquer nome desejado para uma
configuração, mas a maioria das ferramentas está esperando um desses. Essa
configuração normalmente é usada para configurar outras propriedades de forma
que faça sentido para essa configuração, por exemplo, fazer menos otimização de
código na hora de compilar a configuração de Debug . Considere essa opção se o
comando tiver modos de operação diferentes.

-f para --framework

Essa opção é usada para selecionar um único TFM (Moniker da Estrutura de


Destino) para o qual executar, ou seja, se o aplicativo da CLI tiver um
comportamento diferente com base em qual TFM é escolhido, você deverá dar
suporte a esse sinalizador.

-p para --property

Se o aplicativo eventualmente invocar o MSBuild, o usuário precisará personalizar


essa chamada de alguma forma. Essa opção permite que as propriedades do
MSBuild sejam fornecidas na linha de comando e transmitidas à chamada
subjacente do MSBuild. Se o aplicativo não usar o MSBuild, mas precisar de um
conjunto de pares chave-valor, considere usar esse mesmo nome de opção para
tirar proveito das expectativas dos usuários.

-r para --runtime

Se o aplicativo puder ser executado em runtimes diferentes ou tiver uma lógica


específica do runtime, considere dar suporte a essa opção como uma forma de
especificar um Identificador de Runtime. Se o aplicativo for compatível com --
runtime, considere dar suporte também a --os e --arch . Essas opções permitem
que você especifique apenas o sistema operacional ou as partes de arquitetura do
RID, deixando a parte não especificada para ser determinada na plataforma atual.
Para obter mais informações, confira dotnet publish.

Nomes curtos
Crie nomes curtos e fáceis de soletrar para comandos, opções e argumentos. Por
exemplo, se class estiver claro o suficiente, não crie o comando classification .

Nomes em minúsculas
Defina nomes somente em letras minúsculas; você pode fazer aliases em maiúsculas
para fazer com que os comandos ou as opções não diferenciem maiúsculas e
minúsculas.

Nomes Kebab
Use kebab para distinguir palavras. Por exemplo, --additional-probing-path .

Pluralização
Dentro de um aplicativo, seja consistente na pluralização. Por exemplo, não misture
nomes plural e singular em opções que possam ter vários valores (aridade máxima
maior que um):

Nomes de opção Consistência

--additional-probing-paths e --sources ✔️

--additional-probing-path e --source ✔️

--additional-probing-paths e --source ❌

--additional-probing-path e --sources ❌

Verbos X substantivos
Use verbos em vez de substantivos para comandos que se referem a ações (aquelas sem
subcomandos sob eles), por exemplo: dotnet workload remove , não dotnet workload
removal . E use substantivos em vez de verbos para opções, por exemplo: --

configuration , não --configure .

A opção --verbosity
Os aplicativos System.CommandLine normalmente oferecem uma opção --verbosity que
especifica a quantidade de saída enviada ao console. Aqui estão as cinco configurações
padrão:
Q[uiet]

M[inimal]
N[ormal]

D[etailed]
Diag[nostic]

Esses são os nomes padrão, mas os aplicativos existentes às vezes usam Silent no
lugar de Quiet , e Trace , Debug ou Verbose no lugar de Diagnostic .

Cada aplicativo define seus próprios critérios que determinam o que é exibido em cada
nível. Normalmente, um aplicativo só precisa de três níveis:

Quiet
Normal
Diagnostic

Se um aplicativo não precisar de cinco níveis diferentes, a opção ainda deverá definir as
mesmas cinco configurações. Nesse caso, Minimal e Normal produzirão a mesma saída,
e Detailed Diagnostic também será o mesmo. Isso permite que os usuários digitem
apenas aquilo com que estão familiarizados, e o melhor será usado.

A expectativa de Quiet é que nenhuma saída seja exibida no console. No entanto, se


um aplicativo oferecer um modo interativo, o aplicativo deverá fazer uma das seguintes
alternativas:

Exibir solicitações de entrada quando --interactive for especificado, mesmo que


--verbosity seja Quiet .
Não permitir o uso de --verbosity Quiet e --interactive juntos.

Caso contrário, o aplicativo aguardará a entrada sem dizer ao usuário o que ele está
esperando. Vai parecer que seu aplicativo congelou e o usuário não terá ideia de que o
aplicativo está aguardando entrada.

Se você definir aliases, use -v para --verbosity e torne -v sem um argumento um alias
para --verbosity Diagnostic . Usar -q para --verbosity Quiet .

As convenções de POSIX e CLI do .NET


A CLI do .NET não segue consistentemente todas as convenções POSIX.

Traço duplo
Vários comandos na CLI do .NET têm uma implementação especial do token de traço
duplo. No caso de dotnet run , dotnet watch e dotnet tool run , os tokens que seguem
-- são transmitidos ao aplicativo que está sendo executado pelo comando. Por

exemplo:

CLI do .NET

dotnet run --project ./myapp.csproj -- --message "Hello world!"


^^

Neste exemplo, a opção --project é transmitida ao comando dotnet run , e a opção --


message com seu argumento é transmitida como uma opção de linha de comando a

myapp quando ela é executada.

Nem sempre o token -- é necessário para transmitir opções a um aplicativo que você
executa usando dotnet run . Sem o traço duplo, o comando dotnet run transmite
automaticamente ao aplicativo que está sendo executado todas as opções que não são
reconhecidas como aplicáveis ao próprio dotnet run ou ao MSBuild. Portanto, as
seguintes linhas de comando são equivalentes porque dotnet run não reconhece os
argumentos e as opções:

CLI do .NET

dotnet run -- quotes read --delay 0 --fg-color red


dotnet run quotes read --delay 0 --fg-color red

Omissão do delimitador de opção para argumento


A CLI do .NET não dá suporte à convenção POSIX que permite omitir o delimitador
quando você está especificando um alias de opção de caractere único.

Vários argumentos sem repetir o nome da opção


A CLI do .NET não aceita vários argumentos para uma opção sem repetir o nome da
opção.

Opções boolianas
Na CLI do .NET, algumas opções boolianas resultam no mesmo comportamento quando
você transmite false e quando transmite true . Esse comportamento resulta quando o
código da CLI do .NET que implementa a opção verifica apenas a presença ou ausência
da opção, ignorando o valor. Um exemplo é --no-restore para o comando dotnet
build . Transmita no-restore false e a operação de restauração será ignorada da
mesma forma que quando você especificar no-restore true ou no-restore .

Kebab
Em alguns casos, a CLI do .NET não usa kebab para nomes de comando, opção ou
argumento. Por exemplo, há uma opção de CLI do .NET chamada --
additionalprobingpath em vez de --additional-probing-path .

Confira também
Diretrizes de design da CLI de código aberto
Padrões GNU
Visão geral de System.CommandLine
Tutorial: Introdução a System.CommandLine
Como definir comandos, opções e
argumentos em System.CommandLine
Artigo • 15/02/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Este artigo explica como definir comandos, opções e argumentos em aplicativos de


linha de comando criados com a biblioteca System.CommandLine . Para criar um aplicativo
completo que ilustra essas técnicas, confira o tutorial Introdução a
System.CommandLine.

Para obter diretrizes sobre como criar comandos, opções e argumentos de um


aplicativo de linha de comando, confira as Diretrizes de design.

Definir um comando raiz


Cada aplicativo de linha de comando tem um comando raiz, que se refere ao próprio
arquivo executável. O caso mais simples para invocar seu código, se você tiver um
aplicativo sem subcomandos, opções ou argumentos, terá esta aparência:

C#

using System.CommandLine;

class Program
{
static async Task Main(string[] args)
{
var rootCommand = new RootCommand("Sample command-line app");

rootCommand.SetHandler(() =>
{
Console.WriteLine("Hello world!");
});

await rootCommand.InvokeAsync(args);
}
}

Definir subcomandos
Os comandos podem ter comandos filhos, conhecidos como subcomandos ou verbos, e
eles podem aninhar quantos níveis você precisar. Você pode adicionar subcomandos
como mostrado neste exemplo:

C#

var rootCommand = new RootCommand();


var sub1Command = new Command("sub1", "First-level subcommand");
rootCommand.Add(sub1Command);
var sub1aCommand = new Command("sub1a", "Second level subcommand");
sub1Command.Add(sub1aCommand);

O subcomando mais interno neste exemplo pode ser invocado da seguinte maneira:

Console

myapp sub1 sub1a

Definir opções
Um método de manipulador de comando normalmente tem parâmetros e os valores
podem vir de opções de linha de comando. O exemplo a seguir cria duas opções e as
adiciona ao comando raiz. Os nomes de opção incluem prefixos de hífen duplo, de
acordo com as convenções POSIX. O código do manipulador de comando exibe os
valores dessas opções:

C#

var delayOption = new Option<int>


(name: "--delay",
description: "An option whose argument is parsed as an int.",
getDefaultValue: () => 42);
var messageOption = new Option<string>
("--message", "An option whose argument is parsed as a string.");

var rootCommand = new RootCommand();


rootCommand.Add(delayOption);
rootCommand.Add(messageOption);

rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>


{
Console.WriteLine($"--delay = {delayOptionValue}");
Console.WriteLine($"--message = {messageOptionValue}");
},
delayOption, messageOption);

Aqui está um exemplo de entrada de linha de comando e a saída resultante do código


de exemplo anterior:

Console

myapp --delay 21 --message "Hello world!"

Saída

--delay = 21
--message = Hello world!

Opções globais
Para adicionar uma opção a um comando por vez, use o método Add ou AddOption ,
conforme mostrado no exemplo anterior. Para adicionar uma opção a um comando e
recursivamente a todos os seus subcomandos, use o método AddGlobalOption ,
conforme mostrado no exemplo a seguir:

C#

var delayOption = new Option<int>


("--delay", "An option whose argument is parsed as an int.");
var messageOption = new Option<string>
("--message", "An option whose argument is parsed as a string.");

var rootCommand = new RootCommand();


rootCommand.AddGlobalOption(delayOption);
rootCommand.Add(messageOption);

var subCommand1 = new Command("sub1", "First level subcommand");


rootCommand.Add(subCommand1);

var subCommand1a = new Command("sub1a", "Second level subcommand");


subCommand1.Add(subCommand1a);

subCommand1a.SetHandler((delayOptionValue) =>
{
Console.WriteLine($"--delay = {delayOptionValue}");
},
delayOption);
await rootCommand.InvokeAsync(args);

O código anterior adiciona --delay como uma opção global ao comando raiz e está
disponível no manipulador para subCommand1a .

Definir argumentos
Os argumentos são definidos e adicionados aos comandos como opções. O exemplo a
seguir é como o exemplo de opções, mas define argumentos em vez de opções:

C#

var delayArgument = new Argument<int>


(name: "delay",
description: "An argument that is parsed as an int.",
getDefaultValue: () => 42);
var messageArgument = new Argument<string>
("message", "An argument that is parsed as a string.");

var rootCommand = new RootCommand();


rootCommand.Add(delayArgument);
rootCommand.Add(messageArgument);

rootCommand.SetHandler((delayArgumentValue, messageArgumentValue) =>


{
Console.WriteLine($"<delay> argument = {delayArgumentValue}");
Console.WriteLine($"<message> argument = {messageArgumentValue}");
},
delayArgument, messageArgument);

await rootCommand.InvokeAsync(args);

Aqui está um exemplo de entrada de linha de comando e a saída resultante do código


de exemplo anterior:

Console

myapp 42 "Hello world!"

Saída

<delay> argument = 42
<message> argument = Hello world!
Um argumento definido sem um valor padrão, como messageArgument no exemplo
anterior, é tratado como um argumento obrigatório. Uma mensagem de erro é exibida,
e o manipulador de comandos não é chamado, se um argumento obrigatório não é
fornecido.

Definir aliases
Comandos e opções dão suporte para aliases. Você pode adicionar um alias a uma
opção chamando AddAlias :

C#

var option = new Option("--framework");


option.AddAlias("-f");

Com esse alias, as seguintes linhas de comando são equivalentes:

Console

myapp -f net6.0
myapp --framework net6.0

Os aliases de comando funcionam da mesma maneira.

C#

var command = new Command("serialize");


command.AddAlias("serialise");

Esse código torna as seguintes linhas de comando equivalentes:

Console

myapp serialize
myapp serialise

Recomendamos que você minimize o número de aliases de opção definidos e evite


definir determinados aliases específicos. Para obter mais informações, confira Aliases de
forma abreviada.

Opções obrigatórias
Para tornar uma opção obrigatória, defina a propriedade IsRequired como true ,
conforme mostrado no exemplo a seguir:

C#

var endpointOption = new Option<Uri>("--endpoint") { IsRequired = true };


var command = new RootCommand();
command.Add(endpointOption);

command.SetHandler((uri) =>
{
Console.WriteLine(uri?.GetType());
Console.WriteLine(uri?.ToString());
},
endpointOption);

await command.InvokeAsync(args);

A seção de opções da ajuda de comando indica que a opção é obrigatória:

Saída

Options:
--endpoint <uri> (REQUIRED)
--version Show version information
-?, -h, --help Show help and usage information

Se a linha de comando do aplicativo de exemplo não incluir --endpoint , uma


mensagem de erro será exibida e o manipulador de comando não será chamado:

Saída

Option '--endpoint' is required.

Se uma opção obrigatória tiver um valor padrão, ela não precisará ser especificada na
linha de comando. Nesse caso, o valor padrão fornecerá o valor da opção obrigatória.

Comandos, opções e argumentos ocultos


Talvez você queira dar suporte a um comando, opção ou argumento, mas evitar facilitar
a descoberta. Por exemplo, pode ser um recurso preterido, administrativo ou de versão
prévia. Use a propriedade IsHidden para evitar que os usuários descubram esses
recursos usando a conclusão da guia ou a ajuda, conforme mostrado no exemplo a
seguir:
C#

var endpointOption = new Option<Uri>("--endpoint") { IsHidden = true };


var command = new RootCommand();
command.Add(endpointOption);

command.SetHandler((uri) =>
{
Console.WriteLine(uri?.GetType());
Console.WriteLine(uri?.ToString());
},
endpointOption);

await command.InvokeAsync(args);

A seção de opções do comando deste exemplo ajuda a omitir a opção --endpoint .

Saída

Options:
--version Show version information
-?, -h, --help Show help and usage information

Definir arity de argumento


Você pode definir explicitamente a arity do argumento usando a propriedade Arity .
Mas, na maioria dos casos, isso não é necessário. System.CommandLine determina
automaticamente a arity do argumento com base no tipo dele:

Tipo de argumento Arity padrão

Boolean ArgumentArity.ZeroOrOne

Tipos de coleção ArgumentArity.ZeroOrMore

Todo o resto ArgumentArity.ExactlyOne

Vários argumentos
Por padrão, ao chamar um comando, você pode repetir um nome de opção para
especificar vários argumentos para uma opção que tenha arity máxima maior que um.

Console

myapp --items one --items two --items three


Para permitir vários argumentos sem repetir o nome da opção, defina
Option.AllowMultipleArgumentsPerToken como true . Essa configuração permite que
você insira a linha de comando a seguir.

Console

myapp --items one two three

A mesma configuração terá um efeito diferente se a arity máxima do argumento for 1.


Ela permite que você repita uma opção, mas usa apenas o último valor na linha. No
exemplo a seguir, o valor three seria passado para o aplicativo.

Console

myapp --item one --item two --item three

Listar valores de argumento válidos


Para especificar uma lista de valores válidos para uma opção ou argumento, especifique
uma enumeração como o tipo de opção ou use FromAmong, conforme mostrado no
exemplo a seguir:

C#

var languageOption = new Option<string>(


"--language",
"An option that that must be one of the values of a static list.")
.FromAmong(
"csharp",
"fsharp",
"vb",
"pwsh",
"sql");

Aqui está um exemplo de entrada de linha de comando e a saída resultante do código


de exemplo anterior:

Console

myapp --language not-a-language

Saída
Argument 'not-a-language' not recognized. Must be one of:
'csharp'
'fsharp'
'vb'
'pwsh'
'sql'

A seção de opções da ajuda de comando mostra os valores válidos:

Saída

Options:
--language <csharp|fsharp|vb|pwsh|sql> An option that that must be one of
the values of a static list.
--version Show version information
-?, -h, --help Show help and usage information

Validação de opção e argumento


Para obter informações sobre validação de argumento e como personalizá-la, confira as
seguintes seções no artigo Associação de parâmetros:

Validação interna de argumento de tipo e arity


Validação e associação personalizadas

Confira também
Visão geral de System.CommandLine
Associação de parâmetro
Como vincular argumentos a
manipuladores em
System.CommandLine
Artigo • 16/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

O processo de analisar argumentos e fornecê-los ao código do manipulador de


comandos é chamado de associação de parâmetros. System.CommandLine tem a
capacidade de associar muitos tipos de argumentos incorporados. Por exemplo, inteiros,
enumerações e objetos do sistema de arquivos, como FileInfo e DirectoryInfo, podem
ser associados. Vários tipos System.CommandLine também podem ser vinculados.

Validação de argumento interno


Os argumentos têm tipos esperados e aridade. System.CommandLine rejeita argumentos
que não correspondem a essas expectativas.

Por exemplo, um erro de análise é exibido se o argumento para uma opção de número
inteiro não for um número inteiro.

Console

myapp --delay not-an-int

Saída

Cannot parse argument 'not-an-int' as System.Int32.

Um erro de aridade é exibido se vários argumentos forem passados para uma opção
que tenha a aridade máxima de um:
Console

myapp --delay-option 1 --delay-option 2

Saída

Option '--delay' expects a single argument but 2 were provided.

Esse comportamento pode ser substituído definindo


Option.AllowMultipleArgumentsPerToken como true . Nesse caso, você pode repetir
uma opção que tenha aridade máxima de um, mas apenas o último valor da linha é
aceito. No exemplo a seguir, o valor three seria passado para o aplicativo.

Console

myapp --item one --item two --item three

Associação de parâmetros até 16 opções e


argumentos
O exemplo a seguir mostra como associar opções aos parâmetros do manipulador de
comandos, chamando SetHandler:

C#

var delayOption = new Option<int>


("--delay", "An option whose argument is parsed as an int.");
var messageOption = new Option<string>
("--message", "An option whose argument is parsed as a string.");

var rootCommand = new RootCommand("Parameter binding example");


rootCommand.Add(delayOption);
rootCommand.Add(messageOption);

rootCommand.SetHandler(
(delayOptionValue, messageOptionValue) =>
{
DisplayIntAndString(delayOptionValue, messageOptionValue);
},
delayOption, messageOption);

await rootCommand.InvokeAsync(args);

C#
public static void DisplayIntAndString(int delayOptionValue, string
messageOptionValue)
{
Console.WriteLine($"--delay = {delayOptionValue}");
Console.WriteLine($"--message = {messageOptionValue}");
}

Os parâmetros lambda são variáveis que representam os valores de opções e


argumentos:

C#

(delayOptionValue, messageOptionValue) =>


{
DisplayIntAndString(delayOptionValue, messageOptionValue);
},

As variáveis que seguem o lambda representam os objetos de opção e argumento que


são as fontes dos valores de opção e argumento:

C#

delayOption, messageOption);

As opções e argumentos devem ser declarados na mesma ordem no lambda e nos


parâmetros que seguem o lambda. Se o pedido não for consistente, ocorrerá um dos
seguintes cenários:

Se as opções ou argumentos fora de ordem forem de tipos diferentes, uma


exceção de tempo de execução será lançada. Por exemplo, um int pode aparecer
onde um string deveria estar na lista de fontes.
Se as opções ou argumentos fora de ordem forem do mesmo tipo, o manipulador
obterá silenciosamente os valores errados nos parâmetros fornecidos a ele. Por
exemplo, a string opção x pode aparecer onde a string opção y deveria estar
na lista de fontes. Nesse caso, a variável para o valor da opção y obtém o valor da
opção x .

Há sobrecargas de SetHandler que dão suporte a até 16 parâmetros, com assinaturas


síncronas e assíncronas.

Parâmetro vinculando mais de 16 opções e


argumentos
Para tratar mais de 16 opções ou construir um tipo personalizado de várias opções, você
pode usar InvocationContext ou um associador personalizado.

Use InvocationContext .
Uma sobrecarga SetHandler fornece acesso ao objeto InvocationContext e você pode
usar InvocationContext para obter qualquer número de valores de opção e argumento.
Para obter exemplos, consulte Definir códigos de saída e Lidar com rescisão.

Use um fichário personalizado


Um fichário personalizado permite combinar vários valores de opção ou argumento em
um tipo complexo e passá-lo para um único parâmetro de manipulador. Suponha que
você tenha um tipo Person :

C#

public class Person


{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}

Crie uma classe derivada de BinderBase<T>, onde T é o tipo a ser construído com base
na entrada da linha de comando:

C#

public class PersonBinder : BinderBase<Person>


{
private readonly Option<string> _firstNameOption;
private readonly Option<string> _lastNameOption;

public PersonBinder(Option<string> firstNameOption, Option<string>


lastNameOption)
{
_firstNameOption = firstNameOption;
_lastNameOption = lastNameOption;
}

protected override Person GetBoundValue(BindingContext bindingContext)


=>
new Person
{
FirstName =
bindingContext.ParseResult.GetValueForOption(_firstNameOption),
LastName =
bindingContext.ParseResult.GetValueForOption(_lastNameOption)
};
}

Com o fichário personalizado, você pode obter seu tipo personalizado passado para seu
manipulador da mesma forma que obtém valores para opções e argumentos:

C#

rootCommand.SetHandler((fileOptionValue, person) =>


{
DoRootCommand(fileOptionValue, person);
},
fileOption, new PersonBinder(firstNameOption, lastNameOption));

Aqui está o programa completo do qual os exemplos anteriores são retirados:

C#

using System.CommandLine;
using System.CommandLine.Binding;

public class Program


{
internal static async Task Main(string[] args)
{
var fileOption = new Option<FileInfo?>(
name: "--file",
description: "An option whose argument is parsed as a
FileInfo",
getDefaultValue: () => new
FileInfo("scl.runtimeconfig.json"));

var firstNameOption = new Option<string>(


name: "--first-name",
description: "Person.FirstName");

var lastNameOption = new Option<string>(


name: "--last-name",
description: "Person.LastName");

var rootCommand = new RootCommand();


rootCommand.Add(fileOption);
rootCommand.Add(firstNameOption);
rootCommand.Add(lastNameOption);

rootCommand.SetHandler((fileOptionValue, person) =>


{
DoRootCommand(fileOptionValue, person);
},
fileOption, new PersonBinder(firstNameOption, lastNameOption));
await rootCommand.InvokeAsync(args);
}

public static void DoRootCommand(FileInfo? aFile, Person aPerson)


{
Console.WriteLine($"File = {aFile?.FullName}");
Console.WriteLine($"Person = {aPerson?.FirstName}
{aPerson?.LastName}");
}

public class Person


{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}

public class PersonBinder : BinderBase<Person>


{
private readonly Option<string> _firstNameOption;
private readonly Option<string> _lastNameOption;

public PersonBinder(Option<string> firstNameOption, Option<string>


lastNameOption)
{
_firstNameOption = firstNameOption;
_lastNameOption = lastNameOption;
}

protected override Person GetBoundValue(BindingContext


bindingContext) =>
new Person
{
FirstName =
bindingContext.ParseResult.GetValueForOption(_firstNameOption),
LastName =
bindingContext.ParseResult.GetValueForOption(_lastNameOption)
};
}
}

Definir códigos de saída


Há sobrecargas Func de Taskretorno de SetHandler. Se seu manipulador for chamado a
partir de um código assíncrono, você poderá retornar um Task<int> de um manipulador
que use um desses e usar o valor int para definir o código de saída do processo, como
no exemplo a seguir:

C#
static async Task<int> Main(string[] args)
{
var delayOption = new Option<int>("--delay");
var messageOption = new Option<string>("--message");

var rootCommand = new RootCommand("Parameter binding example");


rootCommand.Add(delayOption);
rootCommand.Add(messageOption);

rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>


{
Console.WriteLine($"--delay = {delayOptionValue}");
Console.WriteLine($"--message = {messageOptionValue}");
return Task.FromResult(100);
},
delayOption, messageOption);

return await rootCommand.InvokeAsync(args);


}

No entanto, se o próprio lambda precisar ser assíncrono, você não poderá retornar um
Task<int> . Nesse caso, use InvocationContext.ExitCode. Você pode obter a instância

InvocationContext injetada em seu lambda usando uma sobrecarga SetHandler que


especifica o InvocationContext como o único parâmetro. Essa sobrecarga SetHandler
não permite especificar objetos IValueDescriptor<T> , mas você pode obter valores de
opção e argumento da propriedade ParseResult de InvocationContext , conforme
mostrado no exemplo a seguir:

C#

static async Task<int> Main(string[] args)


{
var delayOption = new Option<int>("--delay");
var messageOption = new Option<string>("--message");

var rootCommand = new RootCommand("Parameter binding example");


rootCommand.Add(delayOption);
rootCommand.Add(messageOption);

rootCommand.SetHandler(async (context) =>


{
int delayOptionValue =
context.ParseResult.GetValueForOption(delayOption);
string? messageOptionValue =
context.ParseResult.GetValueForOption(messageOption);

Console.WriteLine($"--delay = {delayOptionValue}");
await Task.Delay(delayOptionValue);
Console.WriteLine($"--message = {messageOptionValue}");
context.ExitCode = 100;
});

return await rootCommand.InvokeAsync(args);


}

Se você não tiver trabalho assíncrono para fazer, poderá usar as sobrecargas Action.
Nesse caso, defina o código de saída usando InvocationContext.ExitCode da mesma
forma que faria com um lambda assíncrono.

O código de saída é padronizado para 1. Se você não definir explicitamente, seu valor
será definido como 0 quando seu manipulador sair normalmente. Se uma exceção for
lançada, ela mantém o valor padrão.

Tipos com suporte


Os exemplos a seguir mostram o código que associa alguns tipos comumente usados.

Enumerações
Os valores dos tipos enum são vinculados por nome e a vinculação não diferencia
maiúsculas de minúsculas:

C#

var colorOption = new Option<ConsoleColor>("--color");

var rootCommand = new RootCommand("Enum binding example");


rootCommand.Add(colorOption);

rootCommand.SetHandler((colorOptionValue) =>
{ Console.WriteLine(colorOptionValue); },
colorOption);

await rootCommand.InvokeAsync(args);

Aqui está uma amostra de entrada de linha de comando e a saída resultante do


exemplo anterior:

Console

myapp --color red


myapp --color RED

Saída
Red
Red

Matrizes e listas
Muitos tipos comuns que implementam IEnumerable são suportados. Por exemplo:

C#

var itemsOption = new Option<IEnumerable<string>>("--items")


{ AllowMultipleArgumentsPerToken = true };

var command = new RootCommand("IEnumerable binding example");


command.Add(itemsOption);

command.SetHandler((items) =>
{
Console.WriteLine(items.GetType());

foreach (string item in items)


{
Console.WriteLine(item);
}
},
itemsOption);

await command.InvokeAsync(args);

Aqui está uma amostra de entrada de linha de comando e a saída resultante do


exemplo anterior:

Console

--items one --items two --items three

Saída

System.Collections.Generic.List`1[System.String]
one
two
three

Como AllowMultipleArgumentsPerToken está definido como true , a entrada a seguir


resulta na mesma saída:

Console
--items one two three

Tipos de sistema de arquivos


Os aplicativos de linha de comando que funcionam com o sistema de arquivos podem
usar os tipos FileSystemInfo, FileInfo e DirectoryInfo. O exemplo a seguir mostra o uso
de FileSystemInfo :

C#

var fileOrDirectoryOption = new Option<FileSystemInfo>("--file-or-


directory");

var command = new RootCommand();


command.Add(fileOrDirectoryOption);

command.SetHandler((fileSystemInfo) =>
{
switch (fileSystemInfo)
{
case FileInfo file :
Console.WriteLine($"File name: {file.FullName}");
break;
case DirectoryInfo directory:
Console.WriteLine($"Directory name: {directory.FullName}");
break;
default:
Console.WriteLine("Not a valid file or directory name.");
break;
}
},
fileOrDirectoryOption);

await command.InvokeAsync(args);

Com FileInfo e DirectoryInfo o código de correspondência de padrões não é


necessário:

C#

var fileOption = new Option<FileInfo>("--file");

var command = new RootCommand();


command.Add(fileOption);

command.SetHandler((file) =>
{
if (file is not null)
{
Console.WriteLine($"File name: {file?.FullName}");
}
else
{
Console.WriteLine("Not a valid file name.");
}
},
fileOption);

await command.InvokeAsync(args);

Outros tipos suportados


Muitos tipos que possuem um construtor que recebe um único parâmetro de string
podem ser vinculados dessa maneira. Por exemplo, o código que funcionaria com
FileInfo funciona com um Uri.

C#

var endpointOption = new Option<Uri>("--endpoint");

var command = new RootCommand();


command.Add(endpointOption);

command.SetHandler((uri) =>
{
Console.WriteLine($"URL: {uri?.ToString()}");
},
endpointOption);

await command.InvokeAsync(args);

Além dos tipos de sistema de arquivos e Uri , os seguintes tipos são suportados:

bool

byte
DateTime

DateTimeOffset
decimal

double
float

Guid

int
long

sbyte
short

uint
ulong

ushort

Use objetos System.CommandLine


Há uma sobrecarga SetHandler que dá acesso ao objeto InvocationContext. Esse objeto
pode então ser usado para acessar outros objetos System.CommandLine . Por exemplo,
você tem acesso aos seguintes objetos:

InvocationContext
CancellationToken
IConsole
ParseResult

InvocationContext

Para obter exemplos, consulte Definir códigos de saída e Lidar com rescisão.

CancellationToken

Para obter informações sobre como usar CancellationToken, consulte Como lidar com
rescisão.

IConsole

IConsole torna o teste e muitos cenários de extensibilidade mais fáceis do que usar
System.Console . Está disponível na propriedade InvocationContext.Console.

ParseResult

O objeto ParseResult está disponível na propriedade InvocationContext.ParseResult. É


uma estrutura singleton que representa os resultados da análise da entrada da linha de
comando. Você pode usá-lo para verificar a presença de opções ou argumentos na linha
de comando ou para obter a propriedade ParseResult.UnmatchedTokens. Essa
propriedade contém uma lista dos tokens que foram analisados, mas não
corresponderam a nenhum comando, opção ou argumento configurado.
A lista de tokens sem correspondência é útil em comandos que se comportam como
wrappers. Um comando wrapper pega um conjunto de tokens e os encaminha para
outro comando ou aplicativo. O comando sudo no Linux é um exemplo. É preciso o
nome de um usuário para representar seguido por um comando para ser executado. Por
exemplo:

Console

sudo -u admin apt update

Essa linha de comando executaria o apt update comando como o usuário admin .

Para implementar um comando de wrapper como este, defina a propriedade de


comando TreatUnmatchedTokensAsErrors como false . Em seguida, a propriedade
ParseResult.UnmatchedTokens conterá todos os argumentos que não pertencem
explicitamente ao comando. No exemplo anterior, ParseResult.UnmatchedTokens
conteria os tokens apt e update . Seu manipulador de comandos poderia então
encaminhar o UnmatchedTokens para uma nova invocação de shell, por exemplo.

Validação e associação personalizadas


Para fornecer um código de validação personalizado, chame AddValidator em seu
comando, opção ou argumento, conforme mostrado no exemplo a seguir:

C#

var delayOption = new Option<int>("--delay");


delayOption.AddValidator(result =>
{
if (result.GetValueForOption(delayOption) < 1)
{
result.ErrorMessage = "Must be greater than 0";
}
});

Se você quiser analisar e validar a entrada, use um delegado ParseArgument<T>,


conforme mostrado no exemplo a seguir:

C#

var delayOption = new Option<int>(


name: "--delay",
description: "An option whose argument is parsed as an int.",
isDefault: true,
parseArgument: result =>
{
if (!result.Tokens.Any())
{
return 42;
}

if (int.TryParse(result.Tokens.Single().Value, out var delay))


{
if (delay < 1)
{
result.ErrorMessage = "Must be greater than 0";
}
return delay;
}
else
{
result.ErrorMessage = "Not an int.";
return 0; // Ignored.
}
});

O código anterior define isDefault como true para que o delegado parseArgument
seja chamado mesmo que o usuário não insira um valor para essa opção.

Aqui estão alguns exemplos do que você pode fazer com ParseArgument<T> que não
pode fazer com AddValidator :

Análise de tipos personalizados, como a classe Person no exemplo a seguir:

C#

public class Person


{
public string? FirstName { get; set; }
public string? LastName { get; set; }
}

C#

var personOption = new Option<Person?>(


name: "--person",
description: "An option whose argument is parsed as a Person",
parseArgument: result =>
{
if (result.Tokens.Count != 2)
{
result.ErrorMessage = "--person requires two arguments";
return null;
}
return new Person
{
FirstName = result.Tokens.First().Value,
LastName = result.Tokens.Last().Value
};
})
{
Arity = ArgumentArity.OneOrMore,
AllowMultipleArgumentsPerToken = true
};

Análise de outros tipos de strings de entrada (por exemplo, analisar "1,2,3" em


int[] ).

Aridade dinâmica. Por exemplo, você tem dois argumentos que são definidos
como matrizes de strings e precisa manipular uma sequência de strings na entrada
da linha de comando. O método ArgumentResult.OnlyTake permite dividir
dinamicamente as strings de entrada entre os argumentos.

Confira também
System.CommandLine overview
Preenchimento com Tab para
System.CommandLine
Artigo • 08/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Os aplicativos que usam System.CommandLine têm suporte interno para o preenchimento


com Tab em determinados shells. Para habilitá-lo, o usuário final precisa executar
algumas etapas uma vez por shell. Depois que o usuário faz isso, o preenchimento com
Tab é automático para valores estáticos no seu aplicativo, como valores de enumeração
ou valores que você define chamando FromAmong. Você também pode personalizar o
preenchimento com Tab ao obter valores dinamicamente no runtime.

Ativar o recurso auto-completar com TAB


No computador em que você deseja habilitar o preenchimento com Tab, siga as etapas
a seguir.

Para a CLI do .NET:

Veja Como habilitar o preenchimento com Tab.

Para outros aplicativos de linha de comando criados em System.CommandLine :

Instale a ferramenta global dotnet-suggest .

Adicionar o script de shim apropriado ao seu perfil de shell. Talvez você precise
criar um arquivo de perfil de shell. O script de shim encaminha solicitações de
conclusão do seu shell para a ferramenta dotnet-suggest , que delega ao aplicativo
baseado em System.CommandLine apropriado.

Para bash , adicione o conteúdo de dotnet-suggest-shim.bash a


~/.bash_profile.
Para zsh , adicione o conteúdo de dotnet-suggest-shim.zsh a ~/.zshrc.

Para o PowerShell, adicione o conteúdo do dotnet-suggest-shim.ps1 ao seu


perfil do PowerShell. Você pode encontrar o caminho esperado para seu perfil
do PowerShell executando o comando a seguir em seu console:

Console

echo $profile

Depois que o shell do usuário for configurado, as conclusões funcionarão para todos os
aplicativos criados usando-se System.CommandLine .

Para cmd.exe no Windows (o Prompt de Comando do Windows), não há nenhum


mecanismo de preenchimento com Tab plugável, portanto, nenhum script shim está
disponível. Para outros shells, procure um problema do GitHub rotulado Area-
Completions . Se você não encontrar um problema, poderá abrir um novo .

Obter valores de preenchimento com Tab em


tempo de execução
O código a seguir mostra um aplicativo que obtém valores para o preenchimento com
Tab dinamicamente em runtime. O código obtém uma lista das próximas duas semanas
de datas após a data atual. A lista é fornecida à opção --date ao chamar
AddCompletions :

C#

using System.CommandLine;
using System.CommandLine.Completions;
using System.CommandLine.Parsing;

await new DateCommand().InvokeAsync(args);

class DateCommand : Command


{
private Argument<string> subjectArgument =
new ("subject", "The subject of the appointment.");
private Option<DateTime> dateOption =
new ("--date", "The day of week to schedule. Should be within one
week.");

public DateCommand() : base("schedule", "Makes an appointment for


sometime in the next week.")
{
this.AddArgument(subjectArgument);
this.AddOption(dateOption);

dateOption.AddCompletions((ctx) => {
var today = System.DateTime.Today;
var dates = new List<CompletionItem>();
foreach (var i in Enumerable.Range(1, 7))
{
var date = today.AddDays(i);
dates.Add(new CompletionItem(
label: date.ToShortDateString(),
sortText: $"{i:2}"));
}
return dates;
});

this.SetHandler((subject, date) =>


{
Console.WriteLine($"Scheduled \"{subject}\" for {date}");
},
subjectArgument, dateOption);
}
}

Os valores mostrados quando a tecla tab é pressionada são fornecidos como instâncias
CompletionItem :

C#

dates.Add(new CompletionItem(
label: date.ToShortDateString(),
sortText: $"{i:2}"));

As seguintes propriedades CompletionItem são definidas:

Label é o valor de conclusão a ser mostrado.

SortText garante que os valores na lista sejam apresentados na ordem certa. Ele é

definido convertendo i em uma cadeia de caracteres de dois dígitos, de modo


que a classificação seja baseada em 01, 02, 03 e assim por diante, até 14. Se você
não definir esse parâmetro, a classificação será baseada no Label , que neste
exemplo está em formato de data curto e não classificará corretamente.

Há outras propriedades CompletionItem , como Documentation e Detail , mas elas ainda


não são usadas System.CommandLine .

A lista dinâmica de preenchimento com Tab criada por esse código também aparece na
saída da ajuda:

Saída
Description:
Makes an appointment for sometime in the next week.

Usage:
schedule <subject> [options]

Arguments:
<subject> The subject of the appointment.

Options:
--date
The day of week to schedule. Should be within one week.
<2/4/2022|2/5/2022|2/6/2022|2/7/2022|2/8/2022|2/9/2022|2/10/2022>
--version
Show version information
-?, -h, --help

Confira também
System.CommandLine overview
Como configurar a injeção de
dependência em System.CommandLine
Artigo • 08/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Use um associador personalizado para injetar os tipos personalizados em um


manipulador de comandos.

É recomendável a DI (injeção de dependência) específica do manipulador pelos


seguintes motivos:

Os aplicativos de linha de comando geralmente são processos de curta duração,


nos quais o custo de inicialização poderá ter um impacto perceptível no
desempenho. Otimizar o desempenho é particularmente importante quando for
necessário calcular as conclusões de tabulação. Os aplicativos de linha de
comando são diferentes dos aplicativos Web e GUI, que tendem a ser processos de
vida relativamente longa. O tempo de inicialização desnecessário não é apropriado
para processos de curta duração.
Ao executar um aplicativo de linha de comando com vários subcomandos, apenas
um desses subcomandos executará. Se um aplicativo configurar dependências
para os subcomandos que não executarem, ele degradará desnecessariamente o
desempenho.

Para configurar o DI, crie uma classe derivada de BinderBase<T> em que T está a
interface para a qual você deseja injetar uma instância. Na substituição do método
GetBoundValue, obtenha e retorne a instância que você quiser injetar. O exemplo a
seguir injeta a implementação do agente padrão para ILogger:

C#

public class MyCustomBinder : BinderBase<ILogger>


{
protected override ILogger GetBoundValue(
BindingContext bindingContext) => GetLogger(bindingContext);
ILogger GetLogger(BindingContext bindingContext)
{
ILoggerFactory loggerFactory = LoggerFactory.Create(
builder => builder.AddConsole());
ILogger logger = loggerFactory.CreateLogger("LoggerCategory");
return logger;
}
}

Ao chamar o método SetHandler, passe para o lambda uma instância da classe injetada
e passe uma instância da classe Binder na lista de serviços:

C#

rootCommand.SetHandler(async (fileOptionValue, logger) =>


{
await DoRootCommand(fileOptionValue!, logger);
},
fileOption, new MyCustomBinder());

O código a seguir é um programa completo que contém os exemplos anteriores:

C#

using System.CommandLine;
using System.CommandLine.Binding;
using Microsoft.Extensions.Logging;

class Program
{
static async Task Main(string[] args)
{
var fileOption = new Option<FileInfo?>(
name: "--file",
description: "An option whose argument is parsed as a
FileInfo");

var rootCommand = new RootCommand("Dependency Injection sample");


rootCommand.Add(fileOption);

rootCommand.SetHandler(async (fileOptionValue, logger) =>


{
await DoRootCommand(fileOptionValue!, logger);
},
fileOption, new MyCustomBinder());

await rootCommand.InvokeAsync("--file scl.runtimeconfig.json");


}

public static async Task DoRootCommand(FileInfo aFile, ILogger logger)


{
Console.WriteLine($"File = {aFile?.FullName}");
logger.LogCritical("Test message");
await Task.Delay(1000);
}

public class MyCustomBinder : BinderBase<ILogger>


{
protected override ILogger GetBoundValue(
BindingContext bindingContext) => GetLogger(bindingContext);

ILogger GetLogger(BindingContext bindingContext)


{
ILoggerFactory loggerFactory = LoggerFactory.Create(
builder => builder.AddConsole());
ILogger logger = loggerFactory.CreateLogger("LoggerCategory");
return logger;
}
}
}

Confira também
System.CommandLine overview
Como personalizar a ajuda em
aplicativos criados com a biblioteca
System.Commandline
Artigo • 08/06/2023

Você poderá personalizar a ajuda para um comando, opção ou argumento específico e


adicionar ou substituir as seções de ajuda inteiras.

Os exemplos neste artigo funcionam com o seguinte aplicativo de linha de comando:

Esse código requer uma diretiva using :

C#

using System.CommandLine;

C#

var fileOption = new Option<FileInfo>(


"--file",
description: "The file to print out.",
getDefaultValue: () => new FileInfo("scl.runtimeconfig.json"));
var lightModeOption = new Option<bool> (
"--light-mode",
description: "Determines whether the background color will be black or
white");
var foregroundColorOption = new Option<ConsoleColor>(
"--color",
description: "Specifies the foreground color of console output",
getDefaultValue: () => ConsoleColor.White);

var rootCommand = new RootCommand("Read a file")


{
fileOption,
lightModeOption,
foregroundColorOption
};

rootCommand.SetHandler((file, lightMode, color) =>


{
Console.BackgroundColor = lightMode ? ConsoleColor.White:
ConsoleColor.Black;
Console.ForegroundColor = color;
Console.WriteLine($"--file = {file?.FullName}");
Console.WriteLine($"File
contents:\n{file?.OpenText().ReadToEnd()}");
},
fileOption,
lightModeOption,
foregroundColorOption);

await rootCommand.InvokeAsync(args);

Sem personalização, é produzida a seguinte saída de ajuda:

Saída

Description:
Read a file

Usage:
scl [options]

Options:
--file <file> The file to
print out. [default: scl.runtimeconfig.json]
--light-mode Determines
whether the background color will be black or
white
--color Specifies the
foreground color of console output
<Black|Blue|Cyan|DarkBlue|DarkCyan|DarkGray|DarkGreen|Dark [default:
White]
Magenta|DarkRed|DarkYellow|Gray|Green|Magenta|Red|White|Ye
llow>
--version Show version
information
-?, -h, --help Show help and
usage information

Personalizar a ajuda para uma única opção ou


argumento

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.
Para personalizar o nome do argumento de uma opção, use a propriedade
ArgumentHelpName da opção. E HelpBuilder.CustomizeSymbol permite que você
personalize várias partes da saída de ajuda para um comando, opção ou argumento
(Symbol é a classe base para todos os três tipos). Com CustomizeSymbol , é possível
especificar:

O texto da primeira coluna.


O texto da segunda coluna.
A maneira como um valor padrão será descrito.

No aplicativo de exemplo, --light-mode é explicado adequadamente, mas as alterações


nas descrições das opções --file e --color serão úteis. Para --file , o argumento
pode ser identificado como um <FILEPATH> em vez de <file> . Para a opção --color é
possível encurtar a lista de cores disponíveis na coluna um, e na coluna dois você pode
adicionar um aviso de que algumas cores não funcionarão com alguns planos de fundo.

Para fazer essas alterações, exclua a linha await rootCommand.InvokeAsync(args);


mostrada no código anterior e adicione em seu lugar o seguinte código:

C#

fileOption.ArgumentHelpName = "FILEPATH";

var parser = new CommandLineBuilder(rootCommand)


.UseDefaults()
.UseHelp(ctx =>
{
ctx.HelpBuilder.CustomizeSymbol(foregroundColorOption,
firstColumnText: "--color <Black, White, Red, or Yellow>",
secondColumnText: "Specifies the foreground color. " +
"Choose a color that provides enough contrast " +
"with the background color. " +
"For example, a yellow foreground can't be read " +
"against a light mode background.");
})
.Build();

parser.Invoke(args);

O código atualizado requer diretivas using adicionais:

C#

using System.CommandLine.Builder;
using System.CommandLine.Help;
using System.CommandLine.Parsing;
O aplicativo agora produz a seguinte saída de ajuda:

Saída

Description:
Read a file

Usage:
scl [options]

Options:
--file <FILEPATH> The file to print out. [default:
CustomHelp.runtimeconfig.json]
--light-mode Determines whether the background
color will be black or white
--color <Black, White, Red, or Yellow> Specifies the foreground color.
Choose a color that provides enough contrast
with the background color. For
example, a yellow foreground can't be read
against a light mode background.
--version Show version information
-?, -h, --help Show help and usage information

Essa saída mostra que os parâmetros firstColumnText e secondColumnText dão suporte


a quebra de linha na colunas.

Adicionar ou substituir seções de ajuda


É possível adicionar ou substituir uma seção inteira da saída de ajuda. Por exemplo,
suponha que você queira adicionar alguma arte ASCII à seção de descrição usando o
pacote NuGetSpectre.Console .

Altere o layout adicionando uma chamada HelpBuilder.CustomizeLayout no lambda


passado ao método UseHelp:

C#

fileOption.ArgumentHelpName = "FILEPATH";

var parser = new CommandLineBuilder(rootCommand)


.UseDefaults()
.UseHelp(ctx =>
{
ctx.HelpBuilder.CustomizeSymbol(foregroundColorOption,
firstColumnText: "--color <Black, White, Red, or Yellow>",
secondColumnText: "Specifies the foreground color. " +
"Choose a color that provides enough contrast " +
"with the background color. " +
"For example, a yellow foreground can't be read " +
"against a light mode background.");
ctx.HelpBuilder.CustomizeLayout(
_ =>
HelpBuilder.Default
.GetLayout()
.Skip(1) // Skip the default command description
section.
.Prepend(
_ => Spectre.Console.AnsiConsole.Write(
new FigletText(rootCommand.Description!))
));
})
.Build();

await parser.InvokeAsync(args);

O código anterior requer uma diretiva using adicional:

C#

using Spectre.Console;

A classe System.CommandLine.Help.HelpBuilder.Default permite reutilizar partes da


funcionalidade de formatação de ajuda existente e redigi-las na ajuda personalizada.

A saída da ajuda agora tem esta aparência:

Saída

____ _ __ _ _
| _ \ ___ __ _ __| | __ _ / _| (_) | | ___
| |_) | / _ \ / _` | / _` | / _` | | |_ | | | | / _ \
| _ < | __/ | (_| | | (_| | | (_| | | _| | | | | | __/
|_| \_\ \___| \__,_| \__,_| \__,_| |_| |_| |_| \___|

Usage:
scl [options]

Options:
--file <FILEPATH> The file to print out. [default:
CustomHelp.runtimeconfig.json]
--light-mode Determines whether the background
color will be black or white
--color <Black, White, Red, or Yellow> Specifies the foreground color.
Choose a color that provides enough contrast
with the background color. For
example, a yellow foreground can't be read
against a light mode background.
--version Show version information
-?, -h, --help Show help and usage information
Se você quiser usar apenas uma string como o texto da seção de substituição em vez de
formatá-la com Spectre.Console , substitua o código Prepend no exemplo anterior pelo
seguinte código:

C#

.Prepend(
_ => _.Output.WriteLine("**New command description section**")

Confira também
System.CommandLine overview
Como manipular a terminação em
System.CommandLine
Artigo • 29/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Para manipular a terminação, insira uma instância CancellationToken no código do


manipulador. Esse token poderá ser passado para APIs assíncronas que você chamar de
dentro do manipulador, conforme mostrado no exemplo a seguir:

C#

static async Task<int> Main(string[] args)


{
int returnCode = 0;

var urlOption = new Option<string>("--url", "A URL.");

var rootCommand = new RootCommand("Handle termination example");


rootCommand.Add(urlOption);

rootCommand.SetHandler(async (context) =>


{
string? urlOptionValue =
context.ParseResult.GetValueForOption(urlOption);
var token = context.GetCancellationToken();
returnCode = await DoRootCommand(urlOptionValue, token);
});

await rootCommand.InvokeAsync(args);

return returnCode;
}

public static async Task<int> DoRootCommand(


string? urlOptionValue, CancellationToken cancellationToken)
{
try
{
using (var httpClient = new HttpClient())
{
await httpClient.GetAsync(urlOptionValue, cancellationToken);
}
return 0;
}
catch (OperationCanceledException)
{
Console.Error.WriteLine("The operation was aborted");
return 1;
}
}

O código anterior usa uma sobrecarga SetHandler que obtém uma instância
InvocationContext em vez de um ou mais objetos IValueDescriptor<T> . O
InvocationContext é usado para obter os objetos CancellationToken e ParseResult.

ParseResult pode fornecer valores de argumento ou opção.

Para testar o código de exemplo, execute o comando com uma URL que levará um
momento para ser carregada e, antes de concluir o carregamento, pressione Ctrl + C .
No macOS, pressione Command + Period(.) . Por exemplo:

CLI do .NET

testapp --url https://learn.microsoft.com/aspnet/core/fundamentals/minimal-


apis

Saída

The operation was aborted

As ações de cancelamento também poderão ser adicionadas diretamente usando o


método CancellationToken.Register.

Para obter mais informações sobre uma forma alternativa de definir o código de saída
do processo, consulte Definir códigos de saída.

Confira também
System.CommandLine overview
Como usar middleware no
System.CommandLine
Artigo • 08/06/2023

) Importante

Atualmente, System.CommandLine está em VERSÃO PRÉVIA, e essa documentação é


para a versão 2.0 beta 4. Algumas informações estão relacionadas a produtos de
pré-lançamento que poderão ser substancialmente modificados antes do
lançamento. A Microsoft não oferece garantias, expressas ou implícitas, das
informações aqui fornecidas.

Este artigo explica como trabalhar com middleware em aplicativos de linha de comando
criados com a biblioteca System.CommandLine . O uso de middleware é um tópico
avançado que a maioria dos usuários System.CommandLine não precisa considerar.

Introdução ao middleware
Embora cada comando tenha um manipulador para o qual System.CommandLine roteará
com base na entrada, há também um mecanismo para curto-circuitar ou alterar a
entrada antes que a lógica do aplicativo seja invocada. Entre a análise e a invocação, há
uma cadeia de responsabilidade, que você pode personalizar. Vários recursos integrados
de System.CommandLine fazem uso desse recurso. É assim que as opções --help e --
version fazem chamadas de curto-circuito para seu manipulador.

Cada chamada no pipeline pode realizar uma ação com base em ParseResult e retornar
antecipadamente ou optar por chamar o próximo item no pipeline. ParseResult pode
até ser substituído durante esta fase. A última chamada na cadeia é o manipulador do
comando especificado.

Adicionar ao pipeline de middleware


Você pode adicionar uma chamada a este pipeline chamando
CommandLineBuilderExtensions.AddMiddleware. Aqui está um exemplo de código que
habilita uma diretiva personalizada. Depois de criar um comando raiz chamado
rootCommand , o código normalmente adiciona opções, argumentos e manipuladores. Em
seguida, o middleware é adicionado:
C#

var commandLineBuilder = new CommandLineBuilder(rootCommand);

commandLineBuilder.AddMiddleware(async (context, next) =>


{
if (context.ParseResult.Directives.Contains("just-say-hi"))
{
context.Console.WriteLine("Hi!");
}
else
{
await next(context);
}
});

commandLineBuilder.UseDefaults();
var parser = commandLineBuilder.Build();
await parser.InvokeAsync(args);

No código anterior, o middleware escreve "Oi!" se a diretiva [just-say-hi] for


encontrada no resultado da análise. Quando isso acontece, o manipulador normal do
comando não é invocado. Ele não é invocado porque o middleware não chama o
delegado next .

No exemplo, context é InvocationContext, uma estrutura singleton que atua como a


"raiz" de todo o processo de manipulação de comandos. Esta é a estrutura mais
poderosa em System.CommandLine , em termos de recursos. Há dois usos principais para
ele no middleware:

Ele fornece acesso a BindingContext, Parser, Console e HelpBuilder para recuperar


dependências que um middleware requer para sua lógica customizada.
Você pode definir as propriedades InvocationResult ou ExitCode para encerrar o
processamento do comando em curto-circuito. Um exemplo é a opção --help ,
que é implementada dessa maneira.

Aqui está o programa completo, incluindo as diretivas using obrigatórias.

C#

using System.CommandLine;
using System.CommandLine.Builder;
using System.CommandLine.Parsing;

class Program
{
static async Task Main(string[] args)
{
var delayOption = new Option<int>("--delay");
var messageOption = new Option<string>("--message");

var rootCommand = new RootCommand("Middleware example");


rootCommand.Add(delayOption);
rootCommand.Add(messageOption);

rootCommand.SetHandler((delayOptionValue, messageOptionValue) =>


{
DoRootCommand(delayOptionValue, messageOptionValue);
},
delayOption, messageOption);

var commandLineBuilder = new CommandLineBuilder(rootCommand);

commandLineBuilder.AddMiddleware(async (context, next) =>


{
if (context.ParseResult.Directives.Contains("just-say-hi"))
{
context.Console.WriteLine("Hi!");
}
else
{
await next(context);
}
});

commandLineBuilder.UseDefaults();
var parser = commandLineBuilder.Build();
await parser.InvokeAsync(args);
}

public static void DoRootCommand(int delay, string message)


{
Console.WriteLine($"--delay = {delay}");
Console.WriteLine($"--message = {message}");
}
}

Aqui está um exemplo de linha de comando e a saída resultante do código anterior:

Console

myapp [just-say-hi] --delay 42 --message "Hello world!"

Saída

Hi!
Confira também
System.CommandLine overview
E/S de arquivo e de fluxo
Artigo • 27/01/2024

E/S (entrada/saída) de arquivos e fluxos refere-se à transferência de dados de ou para


uma mídia de armazenamento. No .NET, os namespaces System.IO contêm tipos que
permitem a leitura e a gravação, de maneira síncrona e assíncrona, em fluxos de dados e
arquivos. Esses namespaces também contêm tipos que executam compactação e
descompactação em arquivos e tipos que possibilitam a comunicação por meio de
pipes e portas seriais.

Um arquivo é uma coleção ordenada e nomeada de bytes com armazenamento


persistente. Ao trabalhar com arquivos, você trabalha com caminhos de diretórios,
armazenamento em disco e nomes de arquivos e diretórios. Por outro lado, um fluxo é
uma sequência de bytes que você pode usar para ler e gravar em um repositório, o qual
pode ser uma entre vários tipos de mídia de armazenamento (por exemplo, discos ou
memória). Assim como há vários repositórios diferentes de discos, há vários tipos
diferentes de fluxos diferentes de fluxos de arquivos, como os fluxos de rede, memória e
pipes.

Arquivos e diretórios
Você pode usar os tipos no namespace System.IO para interagir com arquivos e
diretórios. Por exemplo, você pode obter e definir propriedades para arquivos e
diretórios e recuperar coleções de arquivos e diretórios com base em critérios de
pesquisa.

Para convenções de nomenclatura de caminhos e os modos de expressar um caminho


de arquivo para sistemas Windows, incluindo a sintaxe de dispositivo DOS compatível
com o .NET Core 1.1 e posterior e o .NET Framework 4.6.2 e posterior, confira Formatos
de caminho de arquivo em sistemas Windows.

Aqui estão algumas classes de arquivos e diretórios comumente usadas:

File – Fornece métodos estáticos para criar, copiar, excluir, mover e abrir arquivos,
além de ajudar na criação de um objeto FileStream.

FileInfo – Fornece métodos de instâncias para criar, copiar, excluir, mover e abrir
arquivos, além de ajudar na criação de um objeto FileStream.

Directory – Fornece métodos estáticos para criar, mover e enumerar ao longo de


diretórios e subdiretórios.
DirectoryInfo – Fornece métodos de instância para criar, mover e enumerar ao
longo de diretórios e subdiretórios.

Path – Fornece métodos e propriedades para processar cadeias de caracteres de


diretório de uma maneira compatível com várias plataformas.

Você sempre deve fornecer tratamento de exceção robusto ao chamar métodos de


sistema de arquivos. Para obter mais informações, veja Tratamento de erros de E/S.

Além de usar essas classes, os usuários do Visual Basic podem usar os métodos e as
propriedades fornecidas pela classe Microsoft.VisualBasic.FileIO.FileSystem para E/S de
arquivo.

Confira Como copiar diretórios, Como criar uma listagem de diretórios e Como
enumerar diretórios e arquivos.

Fluxos
A classe base abstrata Stream oferece suporte a leitura e gravação de bytes. Todas as
classes que representam fluxos herdam da classe Stream. A classe Stream e suas classes
derivadas fornecem uma visão comum de fontes e repositórios de dados, isolando o
programador de detalhes específicos do sistema operacional e dispositivos subjacentes.

Fluxos envolvem estas três operações fundamentais:

Leitura – Transferência de dados de um fluxo para uma estrutura de dados, como


uma matriz de bytes.

Gravação – Transferência de dados para um fluxo a partir de uma fonte de dados.

Busca – Consulta e modificação da posição atual em um fluxo.

Dependendo do repositório ou da fonte de dados subjacente, os fluxos podem oferecer


suporte somente algumas dessas capacidades. Por exemplo, a classe PipeStream não
oferece suporte à operação de busca. As propriedades CanRead, CanWrite e CanSeek de
um fluxo especificam as operações às quais o fluxo oferece suporte.

Algumas classes de fluxo comumente usadas são:

FileStream – Para leitura e gravação em um arquivo.

IsolatedStorageFileStream – Para leitura e gravação em um arquivo no


armazenamento isolado.
MemoryStream – Para leitura e gravação na memória como o repositório de
backup.

BufferedStream – Para melhorar o desempenho das operações de leitura e


gravação.

NetworkStream – Para leitura e gravação via soquetes de rede.

PipeStream – Para leitura e gravação sobre pipes anônimos e nomeados.

CryptoStream – Para vincular fluxos de dados a transformações criptográficas.

Para um exemplo de como trabalhar com fluxos de forma assíncrona, confira E/S de
arquivo assíncrona.

Leitores e gravadores
O namespace System.IO também fornece tipos usados para ler caracteres codificados
de fluxos e gravá-los em fluxos. Normalmente, os fluxos são criados para a entrada e a
saída de bytes. Os tipos de leitor e de gravador tratam a conversão dos caracteres
codificados de/para bytes para que o fluxo possa concluir a operação. Cada classe de
leitor e gravador é associada a um fluxo, o qual pode ser recuperado pela propriedade
BaseStream da classe.

Algumas classes de leitores e gravadores comumente usadas são:

BinaryReader e BinaryWriter – Para leitura e gravação de tipos de dados primitivos


como valores binários.

StreamReader e StreamWriter – Para leitura e gravação de caracteres usando um


valor de codificação para converter os caracteres para/de bytes.

StringReader e StringWriter – Para leitura e gravação de caracteres e cadeias de


caracteres.

TextReader e TextWriter – Funcionam como as classes base abstratas para outros


leitores e gravadores que leem e gravam caracteres e cadeias de caracteres, mas
não dados binários.

Confira Como ler texto de um arquivo, Como gravar texto em um arquivo, Como ler
caracteres em uma cadeia de caracteres e Como gravar caracteres em uma cadeia de
caracteres.
Operações de E/S assíncronas
A leitura ou gravação de uma grande quantidade de dados pode consumir muitos
recursos. Você deve executar essas tarefas de forma assíncrona se seu aplicativo precisar
continuar respondendo ao usuário. Com as operações de E/S síncronas, o thread de
interface do usuário é bloqueado até que a operação de uso intensivo seja concluída.
Use operações de E/S assíncronas durante o desenvolvimento de aplicativos da
Microsoft Store 8.x para evitar causar a impressão de que o aplicativo parou de
funcionar.

Os membros assíncronas contêm Async em seus nomes, como os métodos


CopyToAsync, FlushAsync, ReadAsync e WriteAsync. Você usa esses métodos com as
palavras-chave async e await .

Para saber mais, confira E/S de arquivo assíncrona.

Compactação
A compactação refere-se ao processo de reduzir o tamanho de um arquivo para fins de
armazenamento. A descompactação é o processo de extrair o conteúdo de um arquivo
compactado para um formato utilizável. O namespace System.IO.Compression contém
tipos para compactar e descompactar arquivos e fluxos.

As classes a seguir são frequentemente usadas para compactar e descompactar


arquivos e fluxos:

ZipArchive – Para criar e recuperar entradas em arquivos ZIP.

ZipArchiveEntry – Para representar um arquivo compactado.

ZipFile – para criar, extrair e abrir um pacote compactado.

ZipFileExtensions – Para criar e extrair entradas em um pacote compactado.

DeflateStream – Para compactar e descompactar fluxos usando o algoritmo de


Deflate.

GZipStream – Para compactar e descompactar fluxos no formato de dados GZIP.

Confira How to: Compress and Extract Files (Como compactar e extrair arquivos).

Armazenamento isolado
Um armazenamento isolado é um mecanismo de armazenamento de dados que fornece
isolamento e segurança ao definir maneiras padronizadas de associar códigos a dados
salvos. O armazenamento fornece um sistema de arquivos virtual que é isolado por
usuário, assembly e (opcionalmente) domínio. O armazenamento isolado é
particularmente útil quando o aplicativo não tem permissão para acessar arquivos de
usuários. Você pode salvar configurações ou arquivos para seu aplicativo de modo que
ele seja controlado pela política de segurança do computador.

O armazenamento isolado não está disponível para aplicativos da Microsoft Store 8.x.
Em vez disso, use as classes de dados do aplicativo no namespace Windows.Storage.
Para saber mais, veja Dados de aplicativo.

As classes a seguir são usadas com frequência na implementação do armazenamento


isolado:

IsolatedStorage – Fornece a classe base para implementações de armazenamento


isolado.

IsolatedStorageFile – Fornece uma área de armazenamento isolado que contém


arquivos e diretórios.

IsolatedStorageFileStream – Expõe um arquivo no armazenamento isolado.

Confira Armazenamentos isolado.

Operações de E/S em aplicativos da Windows


Store
O .NET para aplicativos da Microsoft Store 8.x contém muitos dos tipos para leitura e
gravação em fluxos. No entanto, esse conjunto não inclui todos os tipos de E/S do .NET.

Algumas diferenças importantes que devem ser observadas ao usar operações de E/S
em aplicativos da Microsoft Store 8.x:

Tipos especificamente relacionados às operações de arquivo, como File, FileInfo,


Directory e DirectoryInfo, não estão incluídos no .NET para aplicativos da Microsoft
Store 8.x. Em vez disso, use os tipos no namespace Windows.Storage do Windows
Runtime, como StorageFile e StorageFolder.

O armazenamento isolado não está disponível. Use dados de aplicativo.

Use métodos assíncronos, como ReadAsync e WriteAsync, para evitar o bloqueio


do thread da interface do usuário.
Os tipos de compactação com base em caminhos ZipFile e ZipFileExtensions não
estão disponíveis. Em vez disso, use os tipos no namespace
Windows.Storage.Compression.

É possível converter entre fluxos do .NET Framework e fluxos do Windows Runtime, se


necessário. Para mais informações, confira Como converter entre fluxos do .NET
Framework e fluxos do Windows Runtime ou WindowsRuntimeStreamExtensions.

Para saber mais sobre operações de E/S em um aplicativo da Microsoft Store 8.x, confira
Guia de início rápido: leitura e gravação de arquivos.

E/S e segurança
Ao usar as classes no namespace System.IO, você deve atender aos requisitos de
segurança do sistema operacional, como ACLs (listas de controle de acesso) para
controlar o acesso a arquivos e diretórios. Esse é um requisito adicional aos requisitos
de FileIOPermission. As ACLs podem ser gerenciadas por meio de programação. Para
saber mais, confira Como adicionar ou remover entradas da lista de controle de acesso.

As políticas de segurança padrão impedem que aplicativos da Internet ou intranet


acessem arquivos no computador do usuário. Consequentemente, não use classes de
E/S que exijam um caminho para um arquivo físico ao escrever código que será baixado
via Internet ou intranet. Em vez disso, use armazenamento isolado para aplicativos .NET.

Uma verificação de segurança é executada somente quando o fluxo é construído.


Consequentemente, não abra um fluxo para depois passá-lo para código ou domínios
de aplicativos menos confiáveis.

Tópicos relacionados
Tarefas comuns de E/S
Fornece uma lista das tarefas de E/S associadas a arquivos, diretórios e fluxos, além
de links para conteúdo e exemplos relevantes para cada tarefa.

E/S de arquivo assíncrona


Descreve as vantagens de desempenho e a operação básica da E/S assíncrona.

Armazenamentos isolado
Descreve um mecanismo de armazenamento isolado que fornece isolamento e
segurança ao definir maneiras padronizadas de associar códigos aos dados salvos.
Pipes
Descreve operações de pipes anônimos e nomeados no .NET.

Arquivos mapeados em memória


Descreve arquivos mapeados na memória, os quais armazenam o conteúdo de
arquivos do disco na memória virtual. Você pode usar arquivos mapeados na
memória para editar arquivos muito grandes e para criar memória compartilhada
para a comunicação entre processos.

Comentários
Esta página foi útil?  Yes  No

Fornecer comentários sobre o produto


Formatos de caminho de arquivo em
sistemas Windows
Artigo • 02/06/2023

Membros de muitos dos tipos no namespace System.IO incluem um parâmetro path


que permite que você especifique um caminho absoluto ou relativo para um recurso do
sistema de arquivos. Em seguida, esse caminho é passado para as APIs do sistema de
arquivos do Windows. Este tópico discute os formatos de caminhos de arquivo que
podem ser usados em sistemas do Windows.

Caminhos DOS tradicionais


Um caminho DOS padrão pode consistir em três componentes:

Um volume ou letra da unidade seguidos pelo separador de volume ( : ).


Um nome de diretório. O caractere separador de diretório separa subdiretórios
dentro da hierarquia aninhada do diretório.
Um nome de arquivo opcional. O caractere separador de diretório separa o
caminho do arquivo e o nome do arquivo.

Se todos os três componentes estiverem presentes, o caminho será absoluto. Se


nenhum volume ou letra da unidade for especificado e o nome do diretório começar
com o caractere separador de diretório, o caminho será relativo na raiz da unidade
atual. Caso contrário, o caminho será relativo ao diretório atual. A tabela a seguir mostra
alguns possíveis caminhos de arquivo e diretório.

Caminho Descrição

C:\Documents\Newsletters\Summer2018.pdf Um caminho de arquivo absoluto da raiz da unidade


C: .

\Program Files\Custom Um caminho relativo da raiz da unidade atual.


Utilities\StringFinder.exe

2018\January.xlsx Um caminho relativo para um arquivo em um


subdiretório do diretório atual.

..\Publications\TravelBrochure.pdf Um caminho relativo para um arquivo em um


diretório a partir do diretório atual.

C:\Projects\apilibrary\apilibrary.sln Um caminho absoluto para um arquivo na raiz da


unidade C: .
Caminho Descrição

C:Projects\apilibrary\apilibrary.sln Um caminho relativo do diretório atual da unidade


C: .

) Importante

Observe a diferença entre os últimos dois caminhos. Ambos especificam o


especificador de volume opcional ( C: em ambos os casos), mas o primeiro começa
com a raiz do volume especificado, e o segundo não. Assim, o primeiro é um
caminho absoluto do diretório raiz da unidade C: , ao passo que o segundo é um
caminho relativo do diretório atual da unidade C: . Uso do segundo formulário
quando o primeiro é uma fonte comum de bugs que envolvem caminhos de
arquivo do Windows.

Você pode determinar se um caminho de arquivo é totalmente qualificado (ou seja, se o


caminho é independente do diretório atual e não é alterado quando o diretório atual é
alterado) chamando o método Path.IsPathFullyQualified. Esse tipo de caminho poderá
incluir segmentos de diretório relativo ( . e .. ) e ainda ser totalmente qualificado se o
caminho resolvido sempre apontar para o mesmo local.

O exemplo a seguir ilustra a diferença entre caminhos absolutos e relativos. Ele


pressupõe que o diretório D:\FY2018\ existe e que nenhum diretório atual foi definido
para D:\ no prompt de comando antes da execução do exemplo.

C#

using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;

public class Example


{
public static void Main(string[] args)
{
Console.WriteLine($"Current directory is
'{Environment.CurrentDirectory}'");
Console.WriteLine("Setting current directory to 'C:\\'");

Directory.SetCurrentDirectory(@"C:\");
string path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");
Console.WriteLine("Setting current directory to 'D:\\Docs'");
Directory.SetCurrentDirectory(@"D:\Docs");

path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");

// This will be "D:\Docs\FY2018" as it happens to match the drive of


the current directory
Console.WriteLine($"'D:FY2018' resolves to {path}");

Console.WriteLine("Setting current directory to 'C:\\'");


Directory.SetCurrentDirectory(@"C:\");

path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");

// This will be either "D:\FY2018" or "D:\FY2018\FY2018" in the


subprocess. In the sub process,
// the command prompt set the current directory before launch of our
application, which
// sets a hidden environment variable that is considered.
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");

if (args.Length < 1)
{
Console.WriteLine(@"Launching again, after setting current
directory to D:\FY2018");
Uri currentExe = new
Uri(Assembly.GetExecutingAssembly().GetName().CodeBase, UriKind.Absolute);
string commandLine = $"/C cd D:\\FY2018 & \"
{currentExe.LocalPath}\" stop";
ProcessStartInfo psi = new ProcessStartInfo("cmd", commandLine); ;
Process.Start(psi).WaitForExit();

Console.WriteLine("Sub process returned:");


path = Path.GetFullPath(@"D:\FY2018");
Console.WriteLine($"'D:\\FY2018' resolves to {path}");
path = Path.GetFullPath(@"D:FY2018");
Console.WriteLine($"'D:FY2018' resolves to {path}");
}
Console.WriteLine("Press any key to continue... ");
Console.ReadKey();
}
}
// The example displays the following output:
// Current directory is 'C:\Programs\file-paths'
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to d:\FY2018
// Setting current directory to 'D:\Docs'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\Docs\FY2018
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to d:\FY2018
// Launching again, after setting current directory to D:\FY2018
// Sub process returned:
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to d:\FY2018
// The subprocess displays the following output:
// Current directory is 'C:\'
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\FY2018\FY2018
// Setting current directory to 'D:\Docs'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\Docs\FY2018
// Setting current directory to 'C:\'
// 'D:\FY2018' resolves to D:\FY2018
// 'D:FY2018' resolves to D:\FY2018\FY2018

Caminhos UNC
Os caminhos UNC (convenção de nomenclatura universal), usados para acessar recursos
de rede, têm o seguinte formato:

Um nome do host ou servidor, que é precedido por \\ . O nome do servidor pode


ser um nome de computador NetBIOS ou um endereço IP/FQDN (IPv4 e v6 são
compatíveis).
Um nome do compartilhamento, separado do nome do host por \ . Juntos, o
servidor e o nome do compartilhamento compõem o volume.
Um nome de diretório. O caractere separador de diretório separa subdiretórios
dentro da hierarquia aninhada do diretório.
Um nome de arquivo opcional. O caractere separador de diretório separa o
caminho do arquivo e o nome do arquivo.

Veja alguns exemplos de caminhos UNC:

Caminho Descrição

\\system07\C$\ O diretório raiz da unidade C: em system07 .

\\Server2\Share\Test\Foo.txt O arquivo Foo.txt no diretório Test do volume \\Server2\Share .

Caminhos UNC devem sempre ser totalmente qualificados. Podem incluir segmentos de
diretório relativo ( . e .. ), mas esses precisam ser parte de um caminho totalmente
qualificado. É possível usar caminhos relativos somente mapeando um caminho UNC
para uma letra da unidade.
Caminhos de dispositivo DOS
O sistema operacional Windows tem um modelo de objeto unificado que aponta para
todos os recursos, incluindo arquivos. Esses caminhos de objeto podem ser acessados
na janela do console e estão expostos à camada Win32 por meio de uma pasta especial
de links simbólicos para as quais o DOS herdado e os caminhos UNC estão mapeados.
Essa pasta especial é acessada pela sintaxe do caminho de dispositivo DOS, que é uma
das opções a seguir:

\\.\C:\Test\Foo.txt \\?\C:\Test\Foo.txt

Além de identificar uma unidade pela letra, você pode identificar um volume usando a
GUID do volume. Ela assume o formato:

\\.\Volume{b75e2c83-0000-0000-0000-602f00000000}\Test\Foo.txt \\?\Volume{b75e2c83-
0000-0000-0000-602f00000000}\Test\Foo.txt

7 Observação

A sintaxe do caminho de dispositivo DOS é compatível com implementações do


.NET executadas no Windows, a partir do .NET Core 1.1 e .NET Framework 4.6.2.

O caminho de dispositivo DOS tem os seguintes componentes:

O especificador de caminho do dispositivo ( \\.\ ou \\?\ ), que identifica o


caminho como um caminho de dispositivo DOS.

7 Observação

O \\?\ é compatível com todas as versões do .NET Core, .NET 5+ e no .NET


Framework, a partir da versão 4.6.2.

Um link simbólico para o objeto de dispositivo "real" (C: no caso de um nome de


unidade ou Volume{b75e2c83-0000-0000-0000-602f00000000} no caso de um
GUID de volume).

O primeiro segmento do caminho de dispositivo DOS depois do especificador de


caminho do dispositivo identifica o volume ou a unidade. Por exemplo, \\?\C:\ e
\\.\BootPartition\ .

Há um link específico para UNCs que é chamado, não surpreendentemente, UNC .


Por exemplo:
\\.\UNC\Server\Share\Test\Foo.txt \\?\UNC\Server\Share\Test\Foo.txt

Para UNCs de dispositivo, a parte do servidor/compartilhamento forma o volume.


Por exemplo, no \\?\server1\e:\utilities\\filecomparer\ , a parte do
servidor/compartilhamento é server1\utilities . Isso é importante ao chamar um
método como Path.GetFullPath(String, String) com segmentos de diretório relativo.
Não é possível navegar além desse volume.

Os caminhos de dispositivo DOS são totalmente qualificados por definição e não


podem começar com um segmento de diretório relativo ( . ou .. ). Os diretórios atuais
nunca são inseridos no uso deles.

Exemplo: maneiras de se referir ao mesmo


arquivo
O exemplo a seguir mostra algumas maneiras de se referir ao arquivo ao usar as APIs do
namespace System.IO. O exemplo cria uma instância de um objeto FileInfo e usa suas
propriedades Name e Length para exibir o nome do arquivo e seu tamanho.

C#

using System;
using System.IO;

class Program
{
static void Main()
{
string[] filenames = {
@"c:\temp\test-file.txt",
@"\\127.0.0.1\c$\temp\test-file.txt",
@"\\LOCALHOST\c$\temp\test-file.txt",
@"\\.\c:\temp\test-file.txt",
@"\\?\c:\temp\test-file.txt",
@"\\.\UNC\LOCALHOST\c$\temp\test-file.txt",
@"\\127.0.0.1\c$\temp\test-file.txt" };

foreach (var filename in filenames)


{
FileInfo fi = new FileInfo(filename);
Console.WriteLine($"file {fi.Name}: {fi.Length:N0} bytes");
}
}
}
// The example displays output like the following:
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes
// file test-file.txt: 22 bytes

Normalização de caminho
Quase todos os caminhos passados para APIs do Windows são normalizados. Durante a
normalização, o Windows executa as seguintes etapas:

Identifica o caminho.
Aplica o diretório atual a caminhos (relativos) parcialmente qualificados.
Canoniza os separadores de diretório e componente.
Avalia os componentes do diretório relativo ( . para o diretório atual e .. para o
diretório pai).
Corta alguns caracteres.

Essa normalização ocorre de maneira implícita, mas é possível fazê-la explicitamente


chamando o método Path.GetFullPath, que encapsula uma chamada para a função
GetFullPathName(). Também é possível chamar a função GetFullPathName() do
Windows diretamente usando P/Invoke.

Identificar o caminho
O primeiro passo na normalização do caminho é identificar o tipo do caminho. Os
caminhos são classificados em uma dentre algumas categorias:

São caminhos de dispositivo, ou seja, começam com dois separadores e um ponto


de interrogação ou ponto final ( \\? ou \\. ).
São caminhos UNC, ou seja, começam com dois separadores sem um ponto de
interrogação ou ponto final.
São caminhos DOS totalmente qualificados, ou seja, começam com uma letra da
unidade, um separador de volume e um separador de componente ( C:\ ).
Designam um dispositivo herdado ( CON , LPT1 ).
Estão relacionados com a raiz da unidade atual, ou seja, começam com um
separador de componente único ( \ ).
Estão relacionados com o diretório atual de uma unidade especificada, ou seja,
começam com uma letra da unidade, um separador de volume e sem um
separador de componente ( C: ).
Estão relacionados com o diretório atual, ou seja, começam com qualquer outra
coisa ( temp\testfile.txt ).
O tipo do caminho determina se o diretório atual é aplicado de alguma maneira ou não.
Também determina qual é a "raiz" do caminho.

Manipular dispositivos herdados


Se o caminho for um dispositivo DOS herdado como CON , COM1 ou LPT1 , será
convertido em um caminho de dispositivo pelo \\.\ precedente e retornado.

Um caminho que começa com um nome do dispositivo herdado sempre é interpretado


como um dispositivo herdado pelo método Path.GetFullPath(String). Por exemplo, o
caminho de dispositivo DOS de CON.TXT é \\.\CON , e o caminho de dispositivo DOS de
COM1.TXT\file1.txt é \\.\COM1 .

Aplicar o diretório atual


Se o caminho não for totalmente qualificado, o Windows aplicará o diretório atual a ele.
O diretório atual não foi aplicado a UNCs e caminhos de dispositivo. Nem a unidade
total com o separador C:\ .

Se o caminho começar com um único separador de componente, a unidade do diretório


atual será aplicada. Por exemplo, se o caminho do arquivo for \utilities e o diretório
atual for C:\temp\ , a normalização produzirá C:\utilities .

Se o caminho começar com uma letra da unidade, um separador de volume e sem um


separador de componente, o último diretório atual definido do shell de comando para a
unidade especificada será aplicado. Se o último diretório atual não tiver sido definido, a
unidade isolada será aplicada. Por exemplo, se o caminho de arquivo for D:sources , o
diretório atual for C:\Documents\ e o último diretório atual na unidade D: for
D:\sources\ , o resultado será D:\sources\sources . Esses caminhos "relativos à unidade"

são uma fonte comum de erros lógicos de script e programas. Assumir que um caminho
iniciado com uma letra e dois-pontos não é relativo é claramente incorreto.

Se o caminho começar com algo diferente de um separador, a unidade e o diretório


atuais serão aplicados. Por exemplo, se o caminho for filecompare e o diretório atual
for C:\utilities\ , o resultado será C:\utilities\filecompare\ .

) Importante

Os caminhos relativos são perigosos em aplicativos multi-threaded (ou seja, a


maioria), porque o diretório atual tem uma configuração por processo. Qualquer
thread pode alterar o diretório atual a qualquer momento. A partir do .NET Core
2.1, é possível chamar o método Path.GetFullPath(String, String) para obter um
caminho absoluto de um caminho relativo, bem como o caminho base (o diretório
atual) que você precisa para resolvê-lo.

Canonizar separadores
Todas as barras ( / ) são convertidas no separador padrão do Windows, a barra invertida
( \ ). Se estiverem presentes, uma série de barras que segue as duas primeiras barras
serão ocultadas e exibidas como uma barra só.

Avaliar componentes relativos


Conforme o caminho é processado, quaisquer componentes ou segmentos compostos
de um ponto final ou ponto duplo ( . ou .. ) serão avaliados:

No caso do ponto final, o segmento atual será removido, pois se refere ao


diretório atual.

No caso do ponto duplo, o segmento atual e o segmento pai serão removidos,


pois o ponto duplo se refere ao diretório pai.

Os diretórios pais só serão removidos se não forem maiores que a raiz do


caminho. A raiz do caminho depende do tipo do caminho. É a unidade ( C:\ ) dos
caminhos DOS, o servidor/compartilhamento de UNCs ( \\Server\Share ) e o
prefixo do caminho de dispositivo dos caminhos de dispositivo ( \\?\ ou \\.\ ).

Cortar caracteres
Alguns outros caracteres são removidos durante a normalização junto com os
separadores e segmentos relativos removidos anteriormente:

Se um segmento terminar em ponto final, esse ponto final será removido. (Um
segmento de um ponto único ou duplo é normalizado na etapa anterior. Um
segmento de três ou mais pontos não é normalizado e, na verdade, é um nome de
arquivo/diretório válido.)

Se o caminho não terminar em um separador, todos os pontos e espaços à direita


(U+0020) serão removidos. Se o último segmento for simplesmente um ponto final
ou duplo, será aplicada a regra de componentes relativos já mencionada.
Essa regra significa que é possível criar um nome de diretório com um espaço à
direita ao adicionar um separador à direita após o espaço.

) Importante

Nunca crie um diretório ou nome de arquivo com um espaço à direita. Os


espaços à direita podem dificultar ou impossibilitar o acesso ao diretório e,
normalmente, os aplicativos apresentam falha nas tentativas de tratar
diretórios ou arquivos com nomes que contêm espaços à direita.

Ignorar a normalização
Normalmente, qualquer caminho passado para uma API do Windows é (efetivamente)
passado para a função GetFullPathName e normalizado. Há uma exceção importante:
um caminho de dispositivo que começa com um ponto de interrogação em vez de um
ponto final. A menos que o caminho comece exatamente com \\?\ (observe o uso da
barra invertida canônica), ele é normalizado.

Por que ignorar a normalização? Existem três motivos principais:

1. Para ter acesso a caminhos normalmente não disponíveis, mas legais. Um arquivo
ou diretório chamado hidden. , por exemplo, não pode ser acessado de outra
maneira.

2. Para melhorar o desempenho ignorando a normalização, se você já tiver


normalizado.

3. Somente no .NET Framework, ignorar a verificação MAX_PATH do tamanho do


caminho para permitir caminhos com mais de 259 caracteres. A maioria das APIs
permitem isso, com algumas exceções.

7 Observação

O .NET Core e o .NET 5+ tratam caminhos longos de maneira implícita e não


executam uma verificação MAX_PATH . A verificação MAX_PATH se aplica somente ao
.NET Framework.

Ignorar a normalização e as verificações de tamanho do caminho é a única diferença


entre as duas sintaxes de caminho de dispositivo. Caso contrário, elas serão idênticas.
Tenha cuidado ao ignorar a normalização, pois é fácil criar caminhos de difícil
tratamento para aplicativos "normais".
Os caminhos que começam com \\?\ ainda serão normalizados se você os passar
explicitamente para a função GetFullPathName.

É possível passar caminhos com mais de MAX_PATH caracteres para GetFullPathName


sem \\?\ . Ele dá suporte caminhos de tamanho arbitrário, até o tamanho máximo da
cadeia de caracteres que o Windows consegue tratar.

Maiúsculas, minúsculas e o sistema de arquivos


do Windows
Uma peculiaridade do sistema de arquivos do Windows que usuários e desenvolvedores
que não o utilizam consideram confusa é que os nomes do caminho e do diretório não
diferenciam maiúsculas de minúsculas. Isto é, os nomes do caminho e do diretório
refletem as cadeias de caracteres utilizadas no momento da criação. Por exemplo, a
chamada de método

C#

Directory.Create("TeStDiReCtOrY");

cria um diretório chamado TeStDiReCtOrY. Se você renomear um diretório ou arquivo


para alterar as maiúsculas e minúsculas, o nome do diretório ou do arquivo refletirá a
cadeia de caracteres usadas ao renomeá-los. Por exemplo, o código a seguir renomeia o
arquivo test.txt como Test.txt:

C#

using System.IO;

class Example
{
static void Main()
{
var fi = new FileInfo(@".\test.txt");
fi.MoveTo(@".\Test.txt");
}
}

No entanto, as comparações entre o nome do diretório e do arquivo não diferenciam


maiúsculas e minúsculas. Se você pesquisar por um arquivo com o nome "test.txt", as
APIs do sistema de arquivos do .NET ignorarão a comparação entre maiúsculas e
minúsculas. “Test.txt”, “TEST.TXT”, “test.TXT” e qualquer outra combinação de maiúsculas
e minúsculas corresponderão a "test.txt".
Tarefas comuns de E/S
Artigo • 10/05/2023

O namespace System.IO fornece várias classes que permitem que várias ações, como
leitura e gravação, sejam realizadas em arquivos, diretórios e fluxos. Para obter mais
informações, confira E/S de arquivo e fluxo.

Tarefas comuns de arquivos


Para fazer isso... Veja o exemplo neste tópico...

Criar um arquivo de texto Método File.CreateText

Método FileInfo.CreateText

Método File.Create

Método FileInfo.Create

Gravar em um arquivo de texto Como gravar texto em um arquivo

Como escrever um arquivo de texto (C++/CLI)

Ler de um arquivo de texto Como ler texto de um arquivo

Anexar texto em um arquivo Como abrir e acrescentar a um arquivo de log

Método File.AppendText

Método FileInfo.AppendText

Renomear ou mover um arquivo Método File.Move

Método FileInfo.MoveTo

Excluir um arquivo Método File.Delete

Método FileInfo.Delete

Copiar um arquivo Método File.Copy

Método FileInfo.CopyTo

Obter o tamanho de um arquivo Propriedade FileInfo.Length

Obter os atributos de um arquivo Método File.GetAttributes


Para fazer isso... Veja o exemplo neste tópico...

Definir os atributos de um arquivo Método File.SetAttributes

Determinar se um arquivo existe Método File.Exists

Ler de um arquivo binário Como ler e gravar em um arquivo de dados


recém-criado

Gravar em um arquivo binário Como ler e gravar em um arquivo de dados


recém-criado

Recuperar uma extensão de nome de arquivo Método Path.GetExtension

Recuperar o caminho totalmente qualificado Método Path.GetFullPath


de um arquivo

Recuperar o nome e a extensão do arquivo de Método Path.GetFileName


um caminho

Alterar a extensão de um arquivo Método Path.ChangeExtension

Tarefas comuns de diretório


Para fazer isso... Veja o exemplo neste tópico...

Acessar um arquivo em uma pasta especial, como Meus Como gravar texto em um arquivo
Documentos

Criar um diretório Método Directory.CreateDirectory

Propriedade FileInfo.Directory

Criar um subdiretório Método


DirectoryInfo.CreateSubdirectory

Renomear ou mover um diretório Método Directory.Move

Método DirectoryInfo.MoveTo

Copiar um diretório Como: copiar diretórios

Excluir um diretório Método Directory.Delete

Método DirectoryInfo.Delete

Ver os arquivos e subdiretórios em um diretório Como: enumerar diretórios e arquivos

Descobrir o tamanho de um diretório Classe System.IO.Directory


Para fazer isso... Veja o exemplo neste tópico...

Determinar se um diretório existe Método Directory.Exists

Confira também
E/S de arquivo e de fluxo
Compor fluxos
E/S de arquivo assíncrona

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como copiar diretórios
Artigo • 07/04/2023

Este artigo demonstra como usar classes de E/S para copiar de maneira síncrona o
conteúdo de um diretório para outro local.

Para obter um exemplo de cópia assíncrona de arquivo, confira E/S assíncrona de


arquivo.

Este exemplo copia os subdiretórios definindo o parâmetro recursive do método


CopyDirectory como true . O método CopyDirectory copia os subdiretórios

recursivamente chamando a si próprio em cada subdiretório até não existir nada mais
para copiar.

Exemplo
C#

using System.IO;

CopyDirectory(@".\", @".\copytest", true);

static void CopyDirectory(string sourceDir, string destinationDir, bool


recursive)
{
// Get information about the source directory
var dir = new DirectoryInfo(sourceDir);

// Check if the source directory exists


if (!dir.Exists)
throw new DirectoryNotFoundException($"Source directory not found:
{dir.FullName}");

// Cache directories before we start copying


DirectoryInfo[] dirs = dir.GetDirectories();

// Create the destination directory


Directory.CreateDirectory(destinationDir);

// Get the files in the source directory and copy to the destination
directory
foreach (FileInfo file in dir.GetFiles())
{
string targetFilePath = Path.Combine(destinationDir, file.Name);
file.CopyTo(targetFilePath);
}

// If recursive and copying subdirectories, recursively call this method


if (recursive)
{
foreach (DirectoryInfo subDir in dirs)
{
string newDestinationDir = Path.Combine(destinationDir,
subDir.Name);
CopyDirectory(subDir.FullName, newDestinationDir, true);
}
}
}

Confira também
FileInfo
DirectoryInfo
FileStream
E/S de arquivo e de fluxo
Tarefas comuns de E/S
E/S de arquivo assíncrona

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como enumerar diretórios e arquivos
Artigo • 10/05/2023

Coleções enumeráveis fornecem um desempenho melhor do que matrizes ao trabalhar


com coleções grandes de arquivos e diretórios. Para enumerar diretórios e arquivos, use
métodos que retornam uma coleção enumerável de nomes de diretório ou arquivo ou
seus objetos DirectoryInfo, FileInfo ou FileSystemInfo.

Caso deseje pesquisar e retornar somente os nomes de diretórios ou arquivos, use os


métodos de enumeração da classe Directory. Caso deseje pesquisar e retornar outras
propriedades de diretórios ou arquivos, use as classes DirectoryInfo e FileSystemInfo.

Use coleções enumeráveis desses métodos como o parâmetro IEnumerable<T> para


construtores de classes de coleção como List<T>.

A seguinte tabela resume os métodos que retornam coleções enumeráveis de arquivos


e diretórios:

Para pesquisar e retornar Use o método

Nomes de diretório Directory.EnumerateDirectories

Informações de diretório (DirectoryInfo) DirectoryInfo.EnumerateDirectories

Nomes de arquivo Directory.EnumerateFiles

Informações do arquivo (FileInfo) DirectoryInfo.EnumerateFiles

Nomes de entrada do sistema de arquivos Directory.EnumerateFileSystemEntries

Informações de entrada do sistema de arquivos DirectoryInfo.EnumerateFileSystemInfos


(FileSystemInfo)

Nomes de diretório e arquivo Directory.EnumerateFileSystemEntries

7 Observação

Embora você possa enumerar imediatamente todos os arquivos nos subdiretórios


de um diretório pai usando a opção AllDirectories da enumeração SearchOption
opcional, os erros UnauthorizedAccessException podem tornar a enumeração
incompleta. Capture essas exceções enumerando primeiro os diretórios e, em
seguida, os arquivos.
Exemplos: Usar a classe Directory
O exemplo a seguir usa o método Directory.EnumerateDirectories(String) para obter
uma lista dos nomes de diretório de nível superior em um caminho especificado.

C#

using System;
using System.Collections.Generic;
using System.IO;

class Program
{
private static void Main(string[] args)
{
try
{
// Set a variable to the My Documents path.
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

List<string> dirs = new List<string>


(Directory.EnumerateDirectories(docPath));

foreach (var dir in dirs)


{
Console.WriteLine($"
{dir.Substring(dir.LastIndexOf(Path.DirectorySeparatorChar) + 1)}");
}
Console.WriteLine($"{dirs.Count} directories found.");
}
catch (UnauthorizedAccessException ex)
{
Console.WriteLine(ex.Message);
}
catch (PathTooLongException ex)
{
Console.WriteLine(ex.Message);
}
}
}

O exemplo a seguir usa o método Directory.EnumerateFiles(String, String, SearchOption)


para enumerar recursivamente todos os nomes de arquivo em um diretório e
subdiretórios que correspondam a determinado padrão. Em seguida, ele lê cada linha
de cada arquivo e exibe as linhas que contêm uma cadeia de caracteres especificada,
com seus nomes de arquivo e caminhos.

C#
using System;
using System.IO;
using System.Linq;

class Program
{
static void Main(string[] args)
{
try
{
// Set a variable to the My Documents path.
string docPath =

Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

var files = from file in Directory.EnumerateFiles(docPath,


"*.txt", SearchOption.AllDirectories)
from line in File.ReadLines(file)
where line.Contains("Microsoft")
select new
{
File = file,
Line = line
};

foreach (var f in files)


{
Console.WriteLine($"{f.File}\t{f.Line}");
}
Console.WriteLine($"{files.Count().ToString()} files found.");
}
catch (UnauthorizedAccessException uAEx)
{
Console.WriteLine(uAEx.Message);
}
catch (PathTooLongException pathEx)
{
Console.WriteLine(pathEx.Message);
}
}
}

Exemplos: Usar a classe DirectoryInfo


O exemplo a seguir usa o método DirectoryInfo.EnumerateDirectories para listar uma
coleção de diretórios de nível superior cuja CreationTimeUtc é anterior a determinado
valor DateTime.

C#
using System;
using System.IO;

namespace EnumDir
{
class Program
{
static void Main(string[] args)
{
// Set a variable to the Documents path.
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

DirectoryInfo dirPrograms = new DirectoryInfo(docPath);


DateTime StartOf2009 = new DateTime(2009, 01, 01);

var dirs = from dir in dirPrograms.EnumerateDirectories()


where dir.CreationTimeUtc > StartOf2009
select new
{
ProgDir = dir,
};

foreach (var di in dirs)


{
Console.WriteLine($"{di.ProgDir.Name}");
}
}
}
}
// </Snippet1>

O exemplo a seguir usa o método DirectoryInfo.EnumerateFiles para listar todos os


arquivos cujo Length excede 10 MB. Este exemplo enumera primeiro os diretórios de
nível superior para capturar possíveis exceções de acesso não autorizado e, em seguida,
enumera os arquivos.

C#

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{
// Set a variable to the My Documents path.
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

DirectoryInfo diTop = new DirectoryInfo(docPath);


try
{
foreach (var fi in diTop.EnumerateFiles())
{
try
{
// Display each file over 10 MB;
if (fi.Length > 10000000)
{
Console.WriteLine($"
{fi.FullName}\t\t{fi.Length.ToString("N0")}");
}
}
catch (UnauthorizedAccessException unAuthTop)
{
Console.WriteLine($"{unAuthTop.Message}");
}
}

foreach (var di in diTop.EnumerateDirectories("*"))


{
try
{
foreach (var fi in di.EnumerateFiles("*",
SearchOption.AllDirectories))
{
try
{
// Display each file over 10 MB;
if (fi.Length > 10000000)
{
Console.WriteLine($"
{fi.FullName}\t\t{fi.Length.ToString("N0")}");
}
}
catch (UnauthorizedAccessException unAuthFile)
{
Console.WriteLine($"unAuthFile:
{unAuthFile.Message}");
}
}
}
catch (UnauthorizedAccessException unAuthSubDir)
{
Console.WriteLine($"unAuthSubDir:
{unAuthSubDir.Message}");
}
}
}
catch (DirectoryNotFoundException dirNotFound)
{
Console.WriteLine($"{dirNotFound.Message}");
}
catch (UnauthorizedAccessException unAuthDir)
{
Console.WriteLine($"unAuthDir: {unAuthDir.Message}");
}
catch (PathTooLongException longPath)
{
Console.WriteLine($"{longPath.Message}");
}
}
}

Confira também
E/S de arquivo e de fluxo

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Como: ler e gravar em um arquivo de
dados recém-criado
Artigo • 10/05/2023

As classes System.IO.BinaryWriter e System.IO.BinaryReader são usadas para gravar e ler


dados que não sejam cadeias de caracteres. O exemplo a seguir mostra como criar um
fluxo de arquivo vazio, gravar dados nele e ler dados dele.

O exemplo cria um arquivo de dados chamado Test.data no diretório atual, cria os


objetos BinaryWriter e BinaryReader associados e usa o objeto BinaryWriter para gravar
os inteiros de 0 a 10 em Test.data, o que deixa o ponteiro de arquivo no final do arquivo.
Em seguida, o objeto BinaryReader define o ponteiro de arquivo novamente para a
origem e lê o conteúdo especificado.

7 Observação

Se Test.data já existir no diretório atual, uma exceção IOException será gerada. Use
a opção de modo de arquivo FileMode.Create em vez de FileMode.CreateNew
para sempre criar um arquivo sem gerar uma exceção.

Exemplo
C#

using System;
using System.IO;

class MyStream
{
private const string FILE_NAME = "Test.data";

public static void Main()


{
if (File.Exists(FILE_NAME))
{
Console.WriteLine($"{FILE_NAME} already exists!");
return;
}

using (FileStream fs = new FileStream(FILE_NAME,


FileMode.CreateNew))
{
using (BinaryWriter w = new BinaryWriter(fs))
{
for (int i = 0; i < 11; i++)
{
w.Write(i);
}
}
}

using (FileStream fs = new FileStream(FILE_NAME, FileMode.Open,


FileAccess.Read))
{
using (BinaryReader r = new BinaryReader(fs))
{
for (int i = 0; i < 11; i++)
{
Console.WriteLine(r.ReadInt32());
}
}
}
}
}

// The example creates a file named "Test.data" and writes the integers 0
through 10 to it in binary format.
// It then writes the contents of Test.data to the console with each integer
on a separate line.

Confira também
BinaryReader
BinaryWriter
FileStream
FileStream.Seek
SeekOrigin
Como: enumerar diretórios e arquivos
Como: abrir um arquivo de log e acrescentar dados a ele
Como ler texto de um arquivo
Como gravar texto em um arquivo
Como: ler caracteres de uma cadeia de caracteres
Como: escrever caracteres em uma cadeia de caracteres
E/S de arquivo e de fluxo

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
be found on GitHub, where you source. Provide feedback here.
can also create and review
issues and pull requests. For  Open a documentation issue
more information, see our
contributor guide.  Provide product feedback
Como abrir um arquivo de log e
acrescentar dados a ele
Artigo • 09/05/2023

StreamWriter e StreamReader gravam caracteres e leem caracteres de fluxos. O exemplo


de código a seguir abre o arquivo log.txt para a entrada ou cria o arquivo caso ele ainda
não exista e acrescenta informações de log ao final do arquivo. Em seguida, o exemplo
grava o conteúdo do arquivo na saída padrão para exibição.

Como alternativa para esse exemplo, você pode armazenar as informações como uma
única cadeia de caracteres ou uma matriz de cadeia de caracteres e usar o método
File.WriteAllText ou File.WriteAllLines para obter a mesma funcionalidade.

7 Observação

Os usuários do Visual Basic podem optar por usar os métodos e propriedades


fornecidas pela classe Log ou pela classe FileSystem para criar ou gravar em
arquivos de log.

Exemplo
C#

using System;
using System.IO;

class DirAppend
{
public static void Main()
{
using (StreamWriter w = File.AppendText("log.txt"))
{
Log("Test1", w);
Log("Test2", w);
}

using (StreamReader r = File.OpenText("log.txt"))


{
DumpLog(r);
}
}

public static void Log(string logMessage, TextWriter w)


{
w.Write("\r\nLog Entry : ");
w.WriteLine($"{DateTime.Now.ToLongTimeString()}
{DateTime.Now.ToLongDateString()}");
w.WriteLine(" :");
w.WriteLine($" :{logMessage}");
w.WriteLine ("-------------------------------");
}

public static void DumpLog(StreamReader r)


{
string line;
while ((line = r.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
// The example creates a file named "log.txt" and writes the following lines
to it,
// or appends them to the existing "log.txt" file:

// Log Entry : <current long time string> <current long date string>
// :
// :Test1
// -------------------------------

// Log Entry : <current long time string> <current long date string>
// :
// :Test2
// -------------------------------

// It then writes the contents of "log.txt" to the console.

Confira também
StreamWriter
StreamReader
File.AppendText
File.OpenText
StreamReader.ReadLine
Como enumerar diretórios e arquivos
Como ler e gravar em um arquivo de dados recém-criado
Como ler texto de um arquivo
Como gravar texto em um arquivo
Como: ler caracteres de uma cadeia de caracteres
Como: escrever caracteres em uma cadeia de caracteres
E/S de arquivo e de fluxo
Como gravar texto em um arquivo
Artigo • 15/03/2024

Este artigo mostra diferentes maneiras de gravar um texto em um arquivo para um


aplicativo .NET.

As classes e métodos seguintes normalmente são usados para gravar texto em um


arquivo:

StreamWriter contém métodos para gravação síncrona (Write ou WriteLine) ou


assíncrona (WriteAsync e WriteLineAsync) em um arquivo.

File fornece métodos estáticos para gravar um texto em um arquivo, como


WriteAllLines e WriteAllText, ou para acrescentar um texto a um arquivo, como
AppendAllLines, AppendAllText e AppendText.

Path destina-se a cadeias de caracteres que contêm informações de caminho de


arquivo ou diretório. Ele contém o método Combine e, no .NET Core 2.1 e
posterior, os métodos Join e TryJoin. Esses métodos permitem concatenar cadeias
de caracteres para criar um caminho de arquivo ou diretório.

7 Observação

Os exemplos a seguir mostram apenas a quantidade mínima de código necessário.


Um aplicativo do mundo real geralmente fornece verificação de erros e tratamento
de exceção mais robustos.

Exemplo: gravar um texto com o StreamWriter


de forma síncrona
O exemplo a seguir mostra como usar a classe StreamWriter para gravar um texto de
forma síncrona em um novo arquivo, uma linha por vez. Como o objeto StreamWriter é
declarado e instanciado em uma instrução using , o método Dispose é invocado, o que
libera e fecha o fluxo automaticamente.

C#

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{

// Create a string array with the lines of text


string[] lines = { "First line", "Second line", "Third line" };

// Set a variable to the Documents path.


string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Write the string array to a new file named "WriteLines.txt".


using (StreamWriter outputFile = new
StreamWriter(Path.Combine(docPath, "WriteLines.txt")))
{
foreach (string line in lines)
outputFile.WriteLine(line);
}
}
}
// The example creates a file named "WriteLines.txt" with the following
contents:
// First line
// Second line
// Third line

Exemplo: acrescentar um texto com o


StreamWriter de forma síncrona
O exemplo a seguir mostra como usar a classe StreamWriter para acrescentar um texto
de forma síncrona ao arquivo de texto criado no primeiro exemplo:

C#

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{

// Set a variable to the Documents path.


string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Append text to an existing file named "WriteLines.txt".


using (StreamWriter outputFile = new
StreamWriter(Path.Combine(docPath, "WriteLines.txt"), true))
{
outputFile.WriteLine("Fourth Line");
}
}
}
// The example adds the following line to the contents of "WriteLines.txt":
// Fourth Line

Exemplo: gravar um texto com o StreamWriter


de forma assíncrona
O exemplo a seguir mostra como gravar texto de maneira assíncrona em um novo
arquivo usando a classe StreamWriter. Para invocar o método WriteAsync, a chamada de
método precisa estar dentro de um método async .

C#

using System;
using System.IO;
using System.Threading.Tasks;

class Program
{
static async Task Main()
{
// Set a variable to the Documents path.
string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Write the specified text asynchronously to a new file named


"WriteTextAsync.txt".
using (StreamWriter outputFile = new
StreamWriter(Path.Combine(docPath, "WriteTextAsync.txt")))
{
await outputFile.WriteAsync("This is a sentence.");
}
}
}
// The example creates a file named "WriteTextAsync.txt" with the following
contents:
// This is a sentence.

Exemplo: gravar e acrescentar um texto com a


classe File
O exemplo a seguir mostra como gravar texto em um novo arquivo e acrescentar novas
linhas de texto ao mesmo arquivo usando a classe File. Os métodos WriteAllText e
AppendAllLines abrem e fecham o arquivo automaticamente. Se o caminho que você
fornecer ao método WriteAllText já existir, o arquivo será substituído.

C#

using System;
using System.IO;

class Program
{
static void Main(string[] args)
{
// Create a string with a line of text.
string text = "First line" + Environment.NewLine;

// Set a variable to the Documents path.


string docPath =
Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

// Write the text to a new file named "WriteFile.txt".


File.WriteAllText(Path.Combine(docPath, "WriteFile.txt"), text);

// Create a string array with the additional lines of text


string[] lines = { "New line 1", "New line 2" };

// Append new lines of text to the file


File.AppendAllLines(Path.Combine(docPath, "WriteFile.txt"), lines);
}
}
// The example creates a file named "WriteFile.txt" with the contents:
// First line
// And then appends the following contents:
// New line 1
// New line 2

Confira também
StreamWriter
Path
File.CreateText
Como enumerar diretórios e arquivos
Como ler e gravar em um arquivo de dados recém-criado
Como abrir um arquivo de log e acrescentar dados a ele
Como ler texto de um arquivo
E/S de arquivo e de fluxo
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Como ler texto de um arquivo
Artigo • 10/05/2023

Os exemplos a seguir mostram como ler de forma síncrona e assíncrona o texto de um


arquivo de texto usando o .NET para aplicativos de área de trabalho. Nos dois exemplos,
ao criar uma instância da classe StreamReader, você fornece o caminho relativo ou
absoluto para o arquivo.

7 Observação

Esses exemplos de código não se aplicam a aplicativos Universal Windows (UWP)


porque o Windows Runtime fornece diferentes tipos de fluxos para leitura e
gravação em arquivos. Para obter um exemplo que mostra como ler o texto de um
arquivo em um aplicativo UWP, confira Início rápido: ler e gravar arquivos. Para
obter exemplos que mostram como converter entre fluxos do .NET Framework e
fluxos do Windows Runtime, veja Como converter entre fluxos do .NET
Framework e do Windows Runtime.

Exemplo: leitura síncrona em um aplicativo de


console
O exemplo a seguir mostra uma operação de leitura síncrona em um aplicativo de
console. Esse exemplo abre o arquivo de texto usando um leitor de fluxo, copia o
conteúdo para uma cadeia de caracteres e gera a cadeia de caracteres no console.

) Importante

O exemplo pressupõe que um arquivo chamado TestFile.txt já exista na mesma


pasta do aplicativo.

C#

using System;
using System.IO;

class Program
{
public static void Main()
{
try
{
// Open the text file using a stream reader.
using (var sr = new StreamReader("TestFile.txt"))
{
// Read the stream as a string, and write the string to the
console.
Console.WriteLine(sr.ReadToEnd());
}
}
catch (IOException e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}

Exemplo: leitura assíncrona em um aplicativo


WPF
O exemplo a seguir mostra uma operação de leitura assíncrona em um aplicativo WPF
(Windows Presentation Foundation).

) Importante

O exemplo pressupõe que um arquivo chamado TestFile.txt já exista na mesma


pasta do aplicativo.

C#

using System.IO;
using System.Windows;

namespace TextFiles;

/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow() => InitializeComponent();

private async void MainWindow_Loaded(object sender, RoutedEventArgs e)


{
try
{
using (var sr = new StreamReader("TestFile.txt"))
{
ResultBlock.Text = await sr.ReadToEndAsync();
}
}
catch (FileNotFoundException ex)
{
ResultBlock.Text = ex.Message;
}
}
}

Confira também
StreamReader
File.OpenText
StreamReader.ReadLine
E/S de arquivo assíncrona
Como criar uma listagem de diretório
Início rápido: ler e gravar arquivos
Como converter entre fluxos do .NET Framework e do Windows Runtime
Como ler e gravar em um arquivo de dados recém-criado
Como abrir um arquivo de log e acrescentar dados a ele
Como gravar texto em um arquivo
Como: ler caracteres de uma cadeia de caracteres
Como: escrever caracteres em uma cadeia de caracteres
E/S de arquivo e de fluxo
Como ler caracteres de uma cadeia de
caracteres
Artigo • 09/05/2023

Os exemplos de código a seguir mostram como ler caracteres de forma síncrona ou


assíncrona de uma cadeia de caracteres.

Exemplo: ler caracteres de modo síncrono


Este exemplo lê 13 caracteres de forma síncrona de uma cadeia de caracteres,
armazena-os em uma matriz e exibe-os. Em seguida, o exemplo lê os caracteres
restantes na cadeia de caracteres, armazena-os na matriz começando pelo sexto
elemento e exibe o conteúdo da matriz.

C#

using System;
using System.IO;

public class CharsFromStr


{
public static void Main()
{
string str = "Some number of characters";
char[] b = new char[str.Length];

using (StringReader sr = new StringReader(str))


{
// Read 13 characters from the string into the array.
sr.Read(b, 0, 13);
Console.WriteLine(b);

// Read the rest of the string starting at the current string


position.
// Put in the array starting at the 6th array member.
sr.Read(b, 5, str.Length - 13);
Console.WriteLine(b);
}
}
}
// The example has the following output:
//
// Some number o
// Some f characters
Exemplo: ler caracteres de modo assíncrono
O próximo exemplo é o código por trás de um aplicativo WPF. Na carga de janela, o
exemplo lê todos os caracteres de forma assíncrona de um controle TextBox e os
armazena em uma matriz. Em seguida, ele grava cada letra ou caractere de espaço em
branco de forma assíncrona em uma linha separada de um controle TextBlock.

C#

using System;
using System.Text;
using System.Windows;
using System.IO;

namespace StringReaderWriter
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void Window_Loaded(object sender, RoutedEventArgs e)


{
char[] charsRead = new char[UserInput.Text.Length];
using (StringReader reader = new StringReader(UserInput.Text))
{
await reader.ReadAsync(charsRead, 0, UserInput.Text.Length);
}

StringBuilder reformattedText = new StringBuilder();


using (StringWriter writer = new StringWriter(reformattedText))
{
foreach (char c in charsRead)
{
if (char.IsLetter(c) || char.IsWhiteSpace(c))
{
await writer.WriteLineAsync(char.ToLower(c));
}
}
}
Result.Text = reformattedText.ToString();
}
}
}
Confira também
StringReader
StringReader.Read
E/S de arquivo assíncrona
Como criar uma listagem de diretório
Como ler e gravar em um arquivo de dados recém-criado
Como abrir um arquivo de log e acrescentar dados a ele
Como ler texto de um arquivo
Como gravar texto em um arquivo
Como escrever caracteres em uma cadeia de caracteres
E/S de arquivo e de fluxo
Como gravar caracteres em uma cadeia
de caracteres
Artigo • 07/04/2023

Os exemplos de código a seguir gravam caracteres de forma síncrona ou assíncrona de


uma matriz de caracteres em uma cadeia de caracteres.

Exemplo: gravar caracteres de modo síncrono


em um aplicativo de console
O exemplo a seguir usa um StringWriter para gravar cinco caracteres de forma síncrona
em um objeto StringBuilder.

C#

using System;
using System.IO;
using System.Text;

public class CharsToStr


{
public static void Main()
{
StringBuilder sb = new StringBuilder("Start with a string and add
from ");
char[] b = { 'c', 'h', 'a', 'r', '.', ' ', 'B', 'u', 't', ' ', 'n',
'o', 't', ' ', 'a', 'l', 'l' };

using (StringWriter sw = new StringWriter(sb))


{
// Write five characters from the array into the StringBuilder.
sw.Write(b, 0, 5);
Console.WriteLine(sb);
}
}
}
// The example has the following output:
//
// Start with a string and add from char.
Exemplo: gravar caracteres de modo assíncrono
em um aplicativo WPF
O próximo exemplo é o código por trás de um aplicativo WPF. Na carga de janela, o
exemplo lê todos os caracteres de forma assíncrona de um controle TextBox e os
armazena em uma matriz. Em seguida, ele grava cada letra ou caractere de espaço em
branco de forma assíncrona em uma linha separada de um controle TextBlock.

C#

using System;
using System.Text;
using System.Windows;
using System.IO;

namespace StringReaderWriter
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void Window_Loaded(object sender, RoutedEventArgs e)


{
char[] charsRead = new char[UserInput.Text.Length];
using (StringReader reader = new StringReader(UserInput.Text))
{
await reader.ReadAsync(charsRead, 0, UserInput.Text.Length);
}

StringBuilder reformattedText = new StringBuilder();


using (StringWriter writer = new StringWriter(reformattedText))
{
foreach (char c in charsRead)
{
if (char.IsLetter(c) || char.IsWhiteSpace(c))
{
await writer.WriteLineAsync(char.ToLower(c));
}
}
}
Result.Text = reformattedText.ToString();
}
}
}
Confira também
StringWriter
StringWriter.Write
StringBuilder
E/S de arquivo e de fluxo
E/S de arquivo assíncrona
Como enumerar diretórios e arquivos
Como ler e gravar em um arquivo de dados recém-criado
Como abrir um arquivo de log e acrescentar dados a ele
Como ler texto de um arquivo
Como gravar texto em um arquivo
Como ler caracteres de uma cadeia de caracteres
Como adicionar ou remover entradas da
Lista de Controle de Acesso (somente
.NET Framework)
Artigo • 10/05/2023

Para adicionar entradas ACL (lista de controle de acesso) a um arquivo ou um diretório


ou removê-las de um arquivo ou um diretório, obtenha o objeto FileSecurity ou
DirectorySecurity no arquivo ou no diretório. Modifique o objeto e, em seguida,
aplique-o novamente ao arquivo ou ao diretório.

Adicionar ou remover uma entrada ACL de um


arquivo
1. Chame o método File.GetAccessControl para obter um objeto FileSecurity que
contém as entradas ACL atuais de um arquivo.

2. Adicionar ou remover entradas de ACL do objeto FileSecurity retornado da etapa


1.

3. Para aplicar as alterações, passe o objeto FileSecurity para o método


File.SetAccessControl.

Adicionar ou remover uma entrada ACL de um


diretório
1. Chame o método Directory.GetAccessControl para obter um objeto
DirectorySecurity que contém as entradas ACL atuais de um diretório.

2. Adicionar ou remover entradas de ACL do objeto DirectorySecurity retornado da


etapa 1.

3. Para aplicar as alterações, passe o objeto DirectorySecurity para o método


Directory.SetAccessControl.

Exemplo
Você precisará usar uma conta válida de grupo ou de usuário para executar este
exemplo. O exemplo usa um objeto File. Use o mesmo procedimento para as classes
FileInfo, Directory e DirectoryInfo.

C#

using System;
using System.IO;
using System.Security.AccessControl;

namespace FileSystemExample
{
class FileExample
{
public static void Main()
{
try
{
string fileName = "test.xml";

Console.WriteLine("Adding access control entry for "


+ fileName);

// Add the access control entry to the file.


AddFileSecurity(fileName, @"DomainName\AccountName",
FileSystemRights.ReadData, AccessControlType.Allow);

Console.WriteLine("Removing access control entry from "


+ fileName);

// Remove the access control entry from the file.


RemoveFileSecurity(fileName, @"DomainName\AccountName",
FileSystemRights.ReadData, AccessControlType.Allow);

Console.WriteLine("Done.");
}
catch (Exception e)
{
Console.WriteLine(e);
}
}

// Adds an ACL entry on the specified file for the specified


account.
public static void AddFileSecurity(string fileName, string account,
FileSystemRights rights, AccessControlType controlType)
{

// Get a FileSecurity object that represents the


// current security settings.
FileSecurity fSecurity = File.GetAccessControl(fileName);

// Add the FileSystemAccessRule to the security settings.


fSecurity.AddAccessRule(new FileSystemAccessRule(account,
rights, controlType));
// Set the new access settings.
File.SetAccessControl(fileName, fSecurity);
}

// Removes an ACL entry on the specified file for the specified


account.
public static void RemoveFileSecurity(string fileName, string
account,
FileSystemRights rights, AccessControlType controlType)
{

// Get a FileSecurity object that represents the


// current security settings.
FileSecurity fSecurity = File.GetAccessControl(fileName);

// Remove the FileSystemAccessRule from the security settings.


fSecurity.RemoveAccessRule(new FileSystemAccessRule(account,
rights, controlType));

// Set the new access settings.


File.SetAccessControl(fileName, fSecurity);
}
}
}
Como compactar e extrair arquivos
Artigo • 09/05/2023

O namespace System.IO.Compression contém as seguintes classes para compactar e


descompactar arquivos e fluxos. Use também esses tipos para ler e modificar o
conteúdo de um arquivo compactado:

ZipFile
ZipArchive
ZipArchiveEntry
DeflateStream
GZipStream

Os exemplos a seguir mostram algumas das operações que você pode executar com
arquivos compactados. Estes exemplos exigem que os seguintes pacotes NuGet sejam
adicionados ao seu projeto:

System.IO.Compression
System.IO.Compression.ZipFile

Se você estiver usando .NET Framework, adicione referências a essas duas bibliotecas ao
seu projeto:

System.IO.Compression
System.IO.Compression.FileSystem

Exemplo 1: Criar e extrair um arquivo .zip


O exemplo a seguir mostra como criar e extrair um arquivo .zip compactado usando a
classe ZipFile. O exemplo compacta o conteúdo de uma pasta em um novo arquivo .zip
e o extrai para uma nova pasta.

Para executar a amostra, crie uma pasta Iniciar na pasta do programa e popule-a com
arquivos a serem zipados.

C#

using System;
using System.IO.Compression;

class Program
{
static void Main(string[] args)
{
string startPath = @".\start";
string zipPath = @".\result.zip";
string extractPath = @".\extract";

ZipFile.CreateFromDirectory(startPath, zipPath);

ZipFile.ExtractToDirectory(zipPath, extractPath);
}
}

Exemplo 2: Extrair extensões de arquivo


específicas
O exemplo a seguir itera pelo conteúdo de um arquivo .zip existente e extrai arquivos
com uma extensão .txt. Ele usa a classe ZipArchive para acessar o arquivo .zip, e a classe
ZipArchiveEntry para inspecionar as entradas individuais. O método de extensão
ExtractToFile para o objeto ZipArchiveEntry está disponível na classe
System.IO.Compression.ZipFileExtensions.

Para executar a amostra, coloque um arquivo .zip chamado result.zip na pasta do


programa. Quando solicitado, forneça um nome de pasta na qual extrair.

) Importante

Ao descompactar os arquivos, você precisa procurar caminhos de arquivos


maliciosos que possam escapar do diretório no qual deseja descompactar. Isso é
conhecido como um ataque de passagem de caminho. O exemplo a seguir
demonstra como verificar caminhos de arquivos maliciosos e fornece uma maneira
segura de descompactação.

C#

using System;
using System.IO;
using System.IO.Compression;

class Program
{
static void Main(string[] args)
{
string zipPath = @".\result.zip";

Console.WriteLine("Provide path where to extract the zip file:");


string extractPath = Console.ReadLine();
// Normalizes the path.
extractPath = Path.GetFullPath(extractPath);

// Ensures that the last character on the extraction path


// is the directory separator char.
// Without this, a malicious zip file could try to traverse outside
of the expected
// extraction path.
if (!extractPath.EndsWith(Path.DirectorySeparatorChar.ToString(),
StringComparison.Ordinal))
extractPath += Path.DirectorySeparatorChar;

using (ZipArchive archive = ZipFile.OpenRead(zipPath))


{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.FullName.EndsWith(".txt",
StringComparison.OrdinalIgnoreCase))
{
// Gets the full path to ensure that relative segments
are removed.
string destinationPath =
Path.GetFullPath(Path.Combine(extractPath, entry.FullName));

// Ordinal match is safest, case-sensitive volumes can


be mounted within volumes that
// are case-insensitive.
if (destinationPath.StartsWith(extractPath,
StringComparison.Ordinal))
entry.ExtractToFile(destinationPath);
}
}
}
}
}

Exemplo 3: adicionar um arquivo a um .zip


existente
O exemplo a seguir usa a classe ZipArchive para acessar um arquivo .zip existente e
adiciona um arquivo a ele. O novo arquivo é compactado quando você o adiciona ao
arquivo .zip.

C#

using System;
using System.IO;
using System.IO.Compression;

namespace ConsoleApplication
{
class Program
{
static void Main(string[] args)
{
using (FileStream zipToOpen = new
FileStream(@"c:\users\exampleuser\release.zip", FileMode.Open))
{
using (ZipArchive archive = new ZipArchive(zipToOpen,
ZipArchiveMode.Update))
{
ZipArchiveEntry readmeEntry =
archive.CreateEntry("Readme.txt");
using (StreamWriter writer = new
StreamWriter(readmeEntry.Open()))
{
writer.WriteLine("Information about this
package.");
writer.WriteLine("========================");
}
}
}
}
}
}

Exemplo 4: Compactar e descompactar


arquivos .gz
Você também pode usar as classes GZipStream e DeflateStream para compactar e
descompactar dados. Elas usam o mesmo algoritmo de compactação. Você pode
descompactar objetos GZipStream que são gravados em um arquivo .gz usando muitas
ferramentas comuns. O exemplo a seguir mostra como compactar e descompactar um
diretório de arquivos usando a classe GZipStream:

C#

using System;
using System.IO;
using System.IO.Compression;

public class Program


{
private static string directoryPath = @".\temp";
public static void Main()
{
DirectoryInfo directorySelected = new DirectoryInfo(directoryPath);
Compress(directorySelected);
foreach (FileInfo fileToDecompress in
directorySelected.GetFiles("*.gz"))
{
Decompress(fileToDecompress);
}
}

public static void Compress(DirectoryInfo directorySelected)


{
foreach (FileInfo fileToCompress in directorySelected.GetFiles())
{
using (FileStream originalFileStream =
fileToCompress.OpenRead())
{
if ((File.GetAttributes(fileToCompress.FullName) &
FileAttributes.Hidden) != FileAttributes.Hidden &
fileToCompress.Extension != ".gz")
{
using (FileStream compressedFileStream =
File.Create(fileToCompress.FullName + ".gz"))
{
using (GZipStream compressionStream = new
GZipStream(compressedFileStream,
CompressionMode.Compress))
{
originalFileStream.CopyTo(compressionStream);
}
}
FileInfo info = new FileInfo(directoryPath +
Path.DirectorySeparatorChar + fileToCompress.Name + ".gz");
Console.WriteLine($"Compressed {fileToCompress.Name}
from {fileToCompress.Length.ToString()} to {info.Length.ToString()}
bytes.");
}
}
}
}

public static void Decompress(FileInfo fileToDecompress)


{
using (FileStream originalFileStream = fileToDecompress.OpenRead())
{
string currentFileName = fileToDecompress.FullName;
string newFileName =
currentFileName.Remove(currentFileName.Length -
fileToDecompress.Extension.Length);

using (FileStream decompressedFileStream =


File.Create(newFileName))
{
using (GZipStream decompressionStream = new
GZipStream(originalFileStream, CompressionMode.Decompress))
{
decompressionStream.CopyTo(decompressedFileStream);
Console.WriteLine($"Decompressed:
{fileToDecompress.Name}");
}
}
}
}
}

Confira também
ZipArchive
ZipFile
ZipArchiveEntry
DeflateStream
GZipStream
E/S de arquivo e de fluxo
Redigir fluxos
Artigo • 07/04/2023

Um repositório de backup é uma mídia de armazenamento, como um disco ou uma


memória. Cada repositório de backup diferente implementa seu próprio fluxo como
uma implementação da classe Stream.

Cada tipo de fluxo lê e grava bytes de e para seu repositório de backup específico. Os
fluxos que se conectam aos repositórios de backup são chamados de fluxos base. Os
fluxos base têm construtores com os parâmetros necessários para conectar o fluxo ao
repositório de backup. Por exemplo, FileStream tem construtores que especificam um
parâmetro de caminho, o qual especifica como o arquivo será compartilhado por
processos.

O design das classes System.IO fornece uma composição de fluxo simplificada. Você
pode anexar os fluxos base a um ou mais fluxos de passagem que fornecem a
funcionalidade desejada. Você pode anexar um leitor ou um gravador ao final da cadeia,
de forma que os tipos preferidos possam ser lidos ou gravados com facilidade.

Os exemplos de código a seguir criam um FileStream em torno do MyFile.txt existente


para armazenar em buffer MyFile.txt. Observe que FileStreams são armazenados em
buffer por padrão.

) Importante

Os exemplos pressupõem que um arquivo chamado MyFile.txt já exista na mesma


pasta do aplicativo.

Exemplo: Usar StreamReader


O exemplo a seguir cria um StreamReader para ler caracteres do FileStream, que é
passado para o StreamReader como seu argumento construtor. Em seguida,
StreamReader.ReadLine lê até StreamReader.Peek não encontrar mais caracteres.

C#

using System;
using System.IO;

public class CompBuf


{
private const string FILE_NAME = "MyFile.txt";
public static void Main()
{
if (!File.Exists(FILE_NAME))
{
Console.WriteLine($"{FILE_NAME} does not exist!");
return;
}
FileStream fsIn = new FileStream(FILE_NAME, FileMode.Open,
FileAccess.Read, FileShare.Read);
// Create an instance of StreamReader that can read
// characters from the FileStream.
using (StreamReader sr = new StreamReader(fsIn))
{
string input;
// While not at the end of the file, read lines from the file.
while (sr.Peek() > -1)
{
input = sr.ReadLine();
Console.WriteLine(input);
}
}
}
}

Exemplo: Usar BinaryReader


O exemplo a seguir cria um BinaryReader para ler bytes do FileStream, que é passado
para o BinaryReader como seu argumento construtor. Em seguida, ReadByte lê até
PeekChar não encontrar mais bytes.

C#

using System;
using System.IO;

public class ReadBuf


{
private const string FILE_NAME = "MyFile.txt";

public static void Main()


{
if (!File.Exists(FILE_NAME))
{
Console.WriteLine($"{FILE_NAME} does not exist.");
return;
}
FileStream f = new FileStream(FILE_NAME, FileMode.Open,
FileAccess.Read, FileShare.Read);
// Create an instance of BinaryReader that can
// read bytes from the FileStream.
using (BinaryReader br = new BinaryReader(f))
{
byte input;
// While not at the end of the file, read lines from the file.
while (br.PeekChar() > -1 )
{
input = br.ReadByte();
Console.WriteLine(input);
}
}
}
}

Confira também
StreamReader
StreamReader.ReadLine
StreamReader.Peek
FileStream
BinaryReader
BinaryReader.ReadByte
BinaryReader.PeekChar
Fazer a conversão entre fluxos do .NET
Framework e fluxos do Windows
Runtime (somente Windows)
Artigo • 28/03/2023

O .NET Framework para aplicativos UWP é um subconjunto do .NET Framework


completo. Devido a requisitos de segurança e outros requisitos dos aplicativos UWP,
não é possível usar o conjunto completo de APIs do .NET Framework para abrir e ler
arquivos. Para obter mais informações, confira Visão geral do .NET para aplicativos UWP.
No entanto, talvez você queira usar APIs do .NET Framework para outras operações de
manipulação de fluxos. Para manipular esses fluxos, faça a conversão entre um tipo de
fluxo do .NET Framework, como MemoryStream ou FileStream, e um fluxo do Windows
Runtime, como IInputStream, IOutputStream ou IRandomAccessStream.

A classe System.IO.WindowsRuntimeStreamExtensions contém métodos que tornam


fáceis essas conversões. No entanto, as diferenças subjacentes entre os fluxos do .NET
Framework e os fluxos do Windows Runtime afetam os resultados do uso desses
métodos, conforme descrito nas seguintes seções:

Converter um fluxo do Windows Runtime em


um fluxo do .NET Framework
Para converter de um fluxo do Windows Runtime para um fluxo do .NET Framework, use
um dos seguintes métodos System.IO.WindowsRuntimeStreamExtensions:

WindowsRuntimeStreamExtensions.AsStream converte um fluxo de acesso


aleatório no Windows Runtime para um fluxo gerenciado no subconjunto do .NET
para aplicativos UWP.

WindowsRuntimeStreamExtensions.AsStreamForWrite converte um fluxo de saída


no Windows Runtime para um fluxo gerenciado no subconjunto do .NET para
aplicativos UWP.

WindowsRuntimeStreamExtensions.AsStreamForRead converte um fluxo de


entrada no Windows Runtime para um fluxo gerenciado no subconjunto do .NET
para aplicativos UWP.

O Windows Runtime oferece tipos de fluxo que dão suporte ao acesso somente leitura,
somente gravação ou de leitura e gravação. Essas funcionalidades são mantidas quando
você converte um fluxo do Windows Runtime em um fluxo do .NET Framework. Além
disso, se você converter um fluxo do Windows Runtime para um fluxo do .NET
Framework e convertê-lo de volta, você obterá instância original do Windows Runtime
outra vez.

É uma melhor prática usar o método de conversão correspondente às funcionalidades


do fluxo do Windows Runtime que você deseja converter. No entanto, como
IRandomAccessStream é legível e gravável (implementa IOutputStream e IInputStream),
os métodos de conversão mantêm as funcionalidades do fluxo original. Por exemplo, o
uso de WindowsRuntimeStreamExtensions.AsStreamForRead para converter um
IRandomAccessStream não limita o fluxo convertido do .NET Framework a ser legível.
Ele também é gravável.

Converter um fluxo de acesso aleatório do


Windows Runtime em um fluxo do .NET
Framework
Para converter de um fluxo de acesso aleatório do Windows Runtime para um fluxo do
.NET Framework, use o método WindowsRuntimeStreamExtensions.AsStream.

O exemplo de código a seguir solicita que você selecione um arquivo, abre-o com as
APIs do Windows Runtime e, em seguida, converte-o em um fluxo do .NET Framework.
Ele lê o fluxo e os coloca em um bloco de texto. Você normalmente manipulará o fluxo
com as APIs do .NET Framework antes de produzir os resultados.

C#

// Create a file picker.


FileOpenPicker picker = new FileOpenPicker();
picker.SuggestedStartLocation = PickerLocationId.DocumentsLibrary;
picker.ViewMode = PickerViewMode.List;
picker.FileTypeFilter.Add(".txt");

// Show picker, enabling user to pick one file.


StorageFile result = await picker.PickSingleFileAsync();
if (result != null)
{
try
{
// Retrieve the stream. This method returns a
IRandomAccessStreamWithContentType.
var stream = await result.OpenReadAsync();

// Convert the stream to a .NET stream using AsStream, pass to a


// StreamReader and read the stream.
using (StreamReader sr = new StreamReader(stream.AsStream()))
{
TextBlock1.Text = sr.ReadToEnd();
}
}
catch (Exception ex)
{
// ...
}
}

Converter um fluxo do .NET Framework em um


fluxo do Windows Runtime
Para converter de um fluxo do .NET Framework para um fluxo do Windows Runtime, use
um dos seguintes métodos System.IO.WindowsRuntimeStreamExtensions:

WindowsRuntimeStreamExtensions.AsInputStream converte um fluxo gerenciado


no subconjunto do .NET para aplicativos UWP em um fluxo de entrada no
Windows Runtime.

WindowsRuntimeStreamExtensions.AsOutputStream converte um fluxo gerenciado


no subconjunto .NET para aplicativos UWP em um fluxo de saída no Windows
Runtime.

WindowsRuntimeStreamExtensions.AsRandomAccessStream converte um fluxo


gerenciado no .NET para aplicativos UWP em um fluxo de acesso aleatório que
pode ser usado pelo Windows Runtime para leitura ou gravação.

Quando você converte um fluxo do .NET Framework em um fluxo do Windows Runtime,


as funcionalidades do fluxo convertido dependem do fluxo original. Por exemplo, se o
fluxo original tiver suporte à leitura e à gravação e você chamar
WindowsRuntimeStreamExtensions.AsInputStream para converter o fluxo, o tipo
retornado será um IRandomAccessStream . IRandomAccessStream implementa
IInputStream e IOutputStream e dá suporte à leitura e à gravação.

Os fluxos do .NET Framework não dão suporte à clonagem, mesmo após a conversão.
Se você converte um fluxo do .NET Framework em um fluxo do Windows Runtime e
chama GetInputStreamAt ou GetOutputStreamAt, que chama CloneStream, ou se você
chama CloneStream diretamente, ocorre uma exceção.
Converter um fluxo do .NET Framework em um
fluxo de acesso aleatório do Windows Runtime
Para converter um fluxo do .NET Framework em um fluxo de acesso aleatório do
Windows Runtime, use o método AsRandomAccessStream, conforme mostrado no
seguinte exemplo:

) Importante

Verifique se o fluxo do .NET Framework que você está usando dá suporte à busca
ou copie-o para um fluxo que dê esse suporte. Você pode usar a propriedade
Stream.CanSeek para determinar isso.

C#

// Create an HttpClient and access an image as a stream.


var client = new HttpClient();
Stream stream = await client.GetStreamAsync("https://learn.microsoft.com/en-
us/dotnet/images/hub/featured-1.png");
// Create a .NET memory stream.
var memStream = new MemoryStream();
// Convert the stream to the memory stream, because a memory stream supports
seeking.
await stream.CopyToAsync(memStream);
// Set the start position.
memStream.Position = 0;
// Create a new bitmap image.
var bitmap = new BitmapImage();
// Set the bitmap source to the stream, which is converted to a
IRandomAccessStream.
bitmap.SetSource(memStream.AsRandomAccessStream());
// Set the image control source to the bitmap.
Image1.Source = bitmap;

Confira também
Início Rápido: Ler e gravar um arquivo (Windows)
Visão geral dos aplicativos .NET para Windows Store
APIs do .NET para aplicativos da Windows Store
E/S de arquivo assíncrona
Artigo • 09/05/2023

Operações assíncronas permitem que você execute operações de E/S que consomem
muitos recursos sem bloquear o thread principal. Essa consideração sobre o
desempenho é particularmente importante em um aplicativo da Microsoft Store 8.x ou
aplicativo de desktop em que uma operação demorada de fluxo pode bloquear o
thread de interface do usuário e fazer seu aplicativo parecer como se não estivesse
funcionando.

Do .NET Framework 4.5 em diante, os tipos de E/S incluem métodos assíncronos para
simplificar operações assíncronas. Um método assíncrono contém Async em seu nome,
como ReadAsync, WriteAsync, CopyToAsync, FlushAsync, ReadLineAsync e
ReadToEndAsync. Esses métodos assíncronos são implementados em classes de fluxo,
como Stream, FileStream e MemoryStream, e nas classes que são usadas para leitura ou
gravação em fluxos, como TextReader e TextWriter.

No .NET Framework 4, e em versões anteriores, você precisa usar métodos como


BeginRead e EndRead para implementar operações de E/S assíncronas. Esses métodos
ainda estão disponíveis nas versões atuais do .NET para dar suporte ao código herdado;
no entanto, os métodos assíncronos ajudarão você a implementar operações de E/S
assíncronas com mais facilidade.

C# e Visual Basic têm duas palavras-chave cada para a programação assíncrona:

Async (Visual Basic) ou modificador async (C#), que é usado para marcar um

método que contém uma operação assíncrona.

Await (Visual Basic) ou operador await (C#), que é aplicado ao resultado de um


método assíncrono.

Para implementar operações de E/S assíncronas, use essas palavras-chave em conjunto


com os métodos assíncronos, conforme mostrado nos exemplos a seguir. Para obter
mais informações, confira Programação assíncrona com async e await (C#) ou
Programação assíncrona com Async e Await (Visual Basic).

O exemplo a seguir demonstra como usar dois objetos FileStream para copiar arquivos
de forma assíncrona de um diretório para outro. Observe que o manipulador de eventos
Click para o controle Button está marcado com o modificador async , pois ele chama um
método assíncrono.

C#
using System;
using System.Threading.Tasks;
using System.Windows;
using System.IO;

namespace WpfApplication
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}

private async void Button_Click(object sender, RoutedEventArgs e)


{
string startDirectory = @"c:\Users\exampleuser\start";
string endDirectory = @"c:\Users\exampleuser\end";

foreach (string filename in


Directory.EnumerateFiles(startDirectory))
{
using (FileStream sourceStream = File.Open(filename,
FileMode.Open))
{
using (FileStream destinationStream =
File.Create(Path.Combine(endDirectory, Path.GetFileName(filename))))
{
await sourceStream.CopyToAsync(destinationStream);
}
}
}
}
}
}

O exemplo a seguir é semelhante ao anterior, mas usa objetos StreamReader e


StreamWriter para ler e gravar o conteúdo de um arquivo de texto de forma assíncrona.

C#

private async void Button_Click(object sender, RoutedEventArgs e)


{
string UserDirectory = @"c:\Users\exampleuser\";

using (StreamReader SourceReader = File.OpenText(UserDirectory +


"BigFile.txt"))
{
using (StreamWriter DestinationWriter =
File.CreateText(UserDirectory + "CopiedFile.txt"))
{
await CopyFilesAsync(SourceReader, DestinationWriter);
}
}
}

public async Task CopyFilesAsync(StreamReader Source, StreamWriter


Destination)
{
char[] buffer = new char[0x1000];
int numRead;
while ((numRead = await Source.ReadAsync(buffer, 0, buffer.Length)) !=
0)
{
await Destination.WriteAsync(buffer, 0, numRead);
}
}

O exemplo a seguir mostra o arquivo code-behind e o arquivo XAML que são usados
para abrir um arquivo como um Stream em um aplicativo da Microsoft Store 8.x, e ler
seu conteúdo usando uma instância da classe StreamReader. Ele usa os métodos
assíncronos para abrir o arquivo como um fluxo e ler seu conteúdo.

C#

using System;
using System.IO;
using System.Text;
using Windows.Storage.Pickers;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;

namespace ExampleApplication
{
public sealed partial class BlankPage : Page
{
public BlankPage()
{
this.InitializeComponent();
}

private async void Button_Click_1(object sender, RoutedEventArgs e)


{
StringBuilder contents = new StringBuilder();
string nextLine;
int lineCounter = 1;

var openPicker = new FileOpenPicker();


openPicker.SuggestedStartLocation =
PickerLocationId.DocumentsLibrary;
openPicker.FileTypeFilter.Add(".txt");
StorageFile selectedFile = await
openPicker.PickSingleFileAsync();
using (StreamReader reader = new StreamReader(await
selectedFile.OpenStreamForReadAsync()))
{
while ((nextLine = await reader.ReadLineAsync()) != null)
{
contents.AppendFormat("{0}. ", lineCounter);
contents.Append(nextLine);
contents.AppendLine();
lineCounter++;
if (lineCounter > 3)
{
contents.AppendLine("Only first 3 lines shown.");
break;
}
}
}
DisplayContentsBlock.Text = contents.ToString();
}
}
}

XAML

<Page
x:Class="ExampleApplication.BlankPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:ExampleApplication"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<StackPanel Background="{StaticResource ApplicationPageBackgroundBrush}"


VerticalAlignment="Center" HorizontalAlignment="Center">
<TextBlock Text="Display lines from a file."></TextBlock>
<Button Content="Load File" Click="Button_Click_1"></Button>
<TextBlock Name="DisplayContentsBlock"></TextBlock>
</StackPanel>
</Page>

Confira também
Stream
E/S de arquivo e de fluxo
Programação assíncrona com async e await (C#)
Programação assíncrona com Async e Await (Visual Basic)
Tratamento de erros de E/S no .NET
Artigo • 06/04/2023

Além das exceções que podem ser geradas em qualquer chamada de método (como
um OutOfMemoryException quando um sistema sofre estresse ou um
NullReferenceException devido a um erro do programador), os métodos do sistema de
arquivos do .NET podem gerar as seguintes exceções:

System.IO.IOException, a classe base de todos os tipos de exceção System.IO. Ela é


gerada para erros cujos códigos retornados do sistema operacional não mapeiam
diretamente para outro tipo de exceção.
System.IO.FileNotFoundException.
System.IO.DirectoryNotFoundException.
DriveNotFoundException.
System.IO.PathTooLongException.
System.OperationCanceledException.
System.UnauthorizedAccessException.
System.ArgumentException, gerada para caracteres de caminho inválido no .NET
Framework e no .NET Core 2.0 e versões anteriores.
System.NotSupportedException, gerada para dois-pontos inválidos no .NET
Framework.
System.Security.SecurityException, gerada para aplicativos em execução em
confiança limitada que não têm as permissões necessárias somente no .NET
Framework. (Confiança total é o padrão no .NET Framework.)

Mapeando códigos de erro para exceções


Como o sistema de arquivos é um recurso de sistema operacional, os métodos de E/S
no .NET Core e no .NET Framework encapsulam chamadas ao sistema operacional
subjacente. Quando ocorre um erro de E/S no código executado pelo sistema
operacional, ele retorna informações do erro para o método de E/S do .NET. Em
seguida, o método converte as informações do erro, normalmente na forma de um
código de erro, em um tipo de exceção do .NET. Na maioria dos casos, ele faz isso
convertendo diretamente o código de erro em seu tipo de exceção correspondente; ele
não realiza nenhum mapeamento especial do erro com base no contexto da chamada
de método.

Por exemplo, no sistema operacional Windows, uma chamada de método que retorna
um código de erro ERROR_FILE_NOT_FOUND (ou 0x02) é mapeada para um
FileNotFoundException e um código de erro ERROR_PATH_NOT_FOUND (ou 0x03) é
mapeado para um DirectoryNotFoundException.

No entanto, as condições precisas sob as quais o sistema operacional retorna códigos


de erro específicos geralmente não são documentadas ou são mal documentadas. Em
decorrência disso, podem ocorrer exceções inesperadas. Por exemplo, como você está
trabalhando com um diretório em vez de um arquivo, você esperaria que fornecer um
caminho de diretório inválido para o construtor DirectoryInfo gerasse uma
DirectoryNotFoundException. No entanto, isso também pode gerar uma
FileNotFoundException.

Tratamento de exceções em operações de E/S


Por causa dessa dependência do sistema operacional, condições de exceção idênticas
(como o erro de diretório não encontrado em nosso exemplo) podem resultar na
geração de toda uma classe de exceções de E/S pelo método de E/S. Isso significa que,
ao chamar APIs de E/S, seu código deve estar preparado para lidar com a maioria ou
com todas essas exceções, conforme mostrado na tabela a seguir:

Tipo de exceção .NET Core/.NET 5+ .NET Framework

IOException Sim Sim

FileNotFoundException Sim Sim

DirectoryNotFoundException Sim Sim

DriveNotFoundException Sim Sim

PathTooLongException Sim Sim

OperationCanceledException Sim Sim

UnauthorizedAccessException Sim Sim

ArgumentException .NET Core 2.0 e anterior Sim

NotSupportedException Não Sim

SecurityException No Confiança limitada apenas

Tratamento IOException
Como a classe base para exceções no namespace System.IO, a IOException também é
gerada para qualquer código de erro que não é mapeado para um tipo de exceção
predefinido. Isso significa que ela pode ser gerada por uma operação de E/S.

) Importante

Como IOException é a classe base dos outros tipos de exceção no namespace


System.IO, você deve tratar em um bloco catch depois de lidar com as outras
exceções relacionadas a E/S.

Além disso, do .NET Core 2.1 em diante, as verificações de validação da exatidão do


caminho (por exemplo, para garantir que os caracteres inválidos não estejam presentes
em um caminho) foram removidas, e o runtime gera uma exceção mapeada de um
código de erro do sistema operacional, em vez do seu próprio código de validação. A
exceção com maior probabilidade de ser gerada nesse caso é uma IOException, embora
também possa ser gerado qualquer outro tipo de exceção.

Observe que, no seu código de tratamento de exceções, você deve sempre tratar a
IOException por último. Caso contrário, como ela é a classe base de todas as outras
exceções de E/S, os blocos catch de classes derivadas não serão avaliados.

No caso de um IOException, é possível obter outras informações de erro na propriedade


IOException.HResult. Para converter o valor HResult em um código de erro do Win32,
remova os 16 bits superiores do valor de 32 bits. A tabela a seguir lista os códigos de
erro que podem ser encapsulados em um IOException.

HResult Constante Descrição

ERROR_SHARING_VIOLATION 32 O nome do arquivo está ausente ou o arquivo ou


diretório está em uso.

ERROR_FILE_EXISTS 80 O arquivo já existe.

ERROR_INVALID_PARAMETER 87 Um argumento fornecido para o método é inválido.

ERROR_ALREADY_EXISTS 183 O arquivo ou diretório já existe.

É possível lidar com eles usando uma cláusula When em uma instrução catch, como o
exemplo a seguir mostra.

C#

using System;
using System.IO;
using System.Text;

class Program
{
static void Main()
{
var sw = OpenStream(@".\textfile.txt");
if (sw is null)
return;
sw.WriteLine("This is the first line.");
sw.WriteLine("This is the second line.");
sw.Close();
}

static StreamWriter? OpenStream(string path)


{
if (path is null)
{
Console.WriteLine("You did not supply a file path.");
return null;
}

try
{
var fs = new FileStream(path, FileMode.CreateNew);
return new StreamWriter(fs);
}
catch (FileNotFoundException)
{
Console.WriteLine("The file or directory cannot be found.");
}
catch (DirectoryNotFoundException)
{
Console.WriteLine("The file or directory cannot be found.");
}
catch (DriveNotFoundException)
{
Console.WriteLine("The drive specified in 'path' is invalid.");
}
catch (PathTooLongException)
{
Console.WriteLine("'path' exceeds the maximum supported path
length.");
}
catch (UnauthorizedAccessException)
{
Console.WriteLine("You do not have permission to create this
file.");
}
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 32)
{
Console.WriteLine("There is a sharing violation.");
}
catch (IOException e) when ((e.HResult & 0x0000FFFF) == 80)
{
Console.WriteLine("The file already exists.");
}
catch (IOException e)
{
Console.WriteLine($"An exception occurred:\nError code: " +
$"{e.HResult & 0x0000FFFF}\nMessage:
{e.Message}");
}
return null;
}
}

Confira também
Tratando e gerando exceções no .NET
Tratamento de exceções (biblioteca de tarefas paralelas)
Práticas recomendadas para exceções
Como usar exceções específicas em um bloco catch
Armazenamento isolado
Artigo • 09/05/2023

Para aplicativos desktop, o armazenamento isolado é um mecanismo de


armazenamento de dados que proporciona isolamento e segurança ao definir formas
padronizadas de associar código a dados salvos. A padronização também fornece
outros benefícios. Os administradores podem usar as ferramentas desenvolvidas para
manipular armazenamentos isolados para configurar espaço de armazenamento de
arquivos, definir políticas de segurança e excluir dados não utilizados. Com
armazenamentos isolados, seu código não precisa mais de caminhos exclusivos para
especificar locais seguros na sistema de arquivos e os dados são protegidos de outros
aplicativos que só têm acesso a armazenamentos isolados. Informações embutidas em
código que indicam onde a área de armazenamento de um aplicativo se encontra são
desnecessárias.

) Importante

O armazenamento isolado não está disponível para aplicativos da Store do


Windows 8.x. Em vez disso, use as classes de dados de aplicativos nos namespaces
Windows.Storage incluídos na API do Windows Runtime para armazenar dados e

arquivos locais. Para saber mais, confira Dados de aplicativo no Centro de


Desenvolvimento do Windows.

Compartimentos e Repositórios de Dados


Quando um aplicativo armazena dados em um arquivo, o nome do arquivo e o local de
armazenamento devem ser escolhidos cuidadosamente para minimizar a possibilidade
de o local de armazenamento ser conhecido por outro aplicativo e, portanto, tornar-se
vulnerável a corrompimento. Sem um sistema padrão para gerenciar esses problemas, a
improvisação de técnicas que minimizam conflitos de armazenamento pode ser
complexo e os resultados podem não ser confiáveis.

Com armazenamento isolado, os dados são sempre isolados pelo usuário e pelo
assembly. Credenciais como a origem ou o nome forte do assembly determinam a
identidade do assembly. Os dados também podem ser isolados por domínio de
aplicativo, usando credenciais semelhantes.

Quando você usa armazenamento isolado, os aplicativos salvam dados em um


compartimento de dados específico que está associado a algum aspecto da identidade
do código, como seu site, o editor ou a assinatura. O compartimento de dados é uma
abstração, não um local específico de armazenamento; ele consiste em um ou mais
arquivos de armazenamento isolados, chamados de repositórios, que contêm os locais
reais dos diretórios em que os dados estão armazenados. Por exemplo, um aplicativo
pode ter um compartimento de dados associado a ele, e um diretório de sistema de
arquivos implementaria o repositório que de fato preserva os dados para o aplicativo.
Os dados salvos no repositório podem ser qualquer tipo de dados, desde informações
de preferências do usuário até o estado do aplicativo. Para o desenvolvedor, o local do
compartimento de dados é transparente. Os repositórios normalmente residem no
cliente, mas um aplicativo de servidor pode usar repositórios isolados para armazenar
informações que personificam o usuário em cujo nome ele está funcionando.
Armazenamentos isolados também podem armazenar informações em um servidor com
um perfil móvel de usuário para que as informações viajem com o usuário móvel.

Cotas para armazenamento isolado


Uma cota é um limite na quantidade de armazenamento isolado que pode ser usada. A
cota inclui bytes de espaço de arquivo, bem como a sobrecarga associada ao diretório e
outras informações no repositório. O armazenamento isolado usa cotas de permissão,
que são limites de armazenamento definidos com o uso de objetos
IsolatedStoragePermission. Se você tentar gravar os dados que excedam a cota, uma
exceção IsolatedStorageException será gerada. Uma política de segurança, a qual pode
ser modificada com a .NET Framework Configuration Tool (Mscorcfg.msc), determina
quais permissões são concedidas ao código. O código que recebeu
IsolatedStoragePermission é restrito a não usar mais armazenamento do que o
permitido pela propriedade UserQuota. No entanto, porque o código pode ignorar
cotas de permissão ao apresentar identidades de usuário diferentes, as permissões de
cotas servem mais como diretrizes de como o código deve se comportar em vez de
atuar como um limite firme no comportamento do código.

As cotas não são aplicadas em repositórios móveis. Devido a isso, um nível um pouco
mais alto de permissão é necessário para que o código as use. Os valores de
enumeração AssemblyIsolationByRoamingUser e DomainIsolationByRoamingUser
especificam uma permissão para usar o armazenamento isolado para um usuário móvel.

Acesso Seguro
Usar o armazenamento isolado permite que aplicativos parcialmente confiáveis
armazenem dados de uma maneira que é controlada pela política de segurança do
computador. Isto é especialmente útil para componentes baixados que um usuário
talvez queira executar com cautela. Uma política de segurança raramente concede esse
tipo de permissão de código quando você acessa o sistema de arquivos usando
mecanismos padrão de E/S. No entanto, por padrão, código executado do computador
local, de uma rede local ou da Internet recebe a permissão para usar o armazenamento
isolado.

Os administradores podem limitar a quantidade de armazenamento isolado que um


aplicativo ou usuário tem disponível com base em um nível de confiança apropriado.
Além disso, os administradores podem remover todos os dados persistentes de um
usuário. Para criar ou acessar um armazenamento isolado, o código deve receber a
permissão IsolatedStorageFilePermission apropriada.

Para acessar um armazenamento isolado, o código deve ter todos os direitos do sistema
operacional da plataforma nativa necessários. As listas de controle de acesso (ACLs) que
controlam quais usuários têm os direitos necessários para usar o sistema de arquivos
devem ser satisfeitas. Os aplicativos .NET já possuem direitos do sistema operacional
para acessar armazenamentos isolados, a menos que eles executem a personificação
(referente à plataforma). Nesse caso, o aplicativo é responsável por garantir que a
identidade do usuário personificado tenha os direitos adequados do sistema
operacional para acessar o armazenamento isolado. Esse acesso oferece uma maneira
conveniente para que códigos que são executados ou baixados da Web possam ler e
gravar em uma área de armazenamento relacionada a um usuário específico.

Para controlar o acesso ao armazenamento isolado, o Common Language Runtime


utiliza objetos IsolatedStorageFilePermission. Cada objeto tem propriedades que
especificam os seguintes valores:

Utilização permitida, que indica o tipo de acesso que é permitido. Os valores são
membros da enumeração IsolatedStorageContainment. Para obter mais
informações sobre esses valores, consulte a tabela na próxima seção.

Cota de armazenamento, conforme descrito na seção anterior.

O runtime exige a permissão IsolatedStorageFilePermission quando o código tenta abrir


um repositório pela primeira vez. Ele decide se essa permissão será concedida com base
em quanto do código é confiável. Se a permissão é concedida, os valores de cota de
armazenamento e de utilização permitida são determinados por política de segurança e
pela solicitação do código para IsolatedStorageFilePermission. A política de segurança é
definida com o auxílio da .NET Framework Configuration Tool (Mscorcfg.msc). Todos os
chamadores na pilha de chamadas são verificados para garantir que cada chamador
tenha pelo menos a utilização permitida apropriada. O runtime também verifica a cota
imposta no código que abriu ou criou o depósito no qual o arquivo será salvo. Se essas
condições forem atendidas, a permissão será concedida. A cota é verificada novamente
sempre que um arquivo é gravado no repositório.

O código do aplicativo não é obrigado a solicitar permissão porque o Common


Language Runtime concederá qualquer IsolatedStorageFilePermission apropriada com
base na política de segurança. No entanto, existem bons motivos para solicitar as
permissões específicas de que seu aplicativo necessita, incluindo
IsolatedStorageFilePermission.

Uso permitido e riscos de segurança


O uso permitido especificado por IsolatedStorageFilePermission determina o grau ao
qual o código terá permissão para criar e usar o armazenamento isolado. A tabela a
seguir mostra como o uso permitido especificado na permissão corresponde a tipos de
isolamento e resume os riscos de segurança associados a cada uso permitido.

Uso permitido Tipos de isolamento Impacto de segurança

None Nenhum armazenamento Não há impacto de segurança.


isolado é permitido.

DomainIsolationByUser Isolamento por usuário, Esse nível de permissão deixa


domínio e assembly Cada recursos abertos para
assembly possui um sub- superutilização não autorizada,
repositório separado embora cotas impostas tornem
dentro do domínio. isso mais difícil. Isso é
Repositórios que usam denominado um ataque de
essa permissão também negação de serviço.
são implicitamente
isolados por computador.

DomainIsolationByRoamingUser Igual a Como as cotas devem ser


DomainIsolationByUser , desativadas, os recursos de
mas o armazenamento é armazenamento são mais
salvo em um local que irá vulneráveis a um ataque de
transitar se os perfis de negação de serviço.
usuário móvel estiverem
ativados e as cotas não
forem aplicadas.
Uso permitido Tipos de isolamento Impacto de segurança

AssemblyIsolationByUser Isolamento por usuário e As cotas serão aplicadas nesse


assembly Repositórios nível para ajudar a evitar um
que usam essa permissão ataque de negação de serviço. O
também são mesmo assembly no outro
implicitamente isolados domínio pode acessar esse
por computador. repositório, abrindo a
possibilidade de vazamento de
informações entre aplicativos.

AssemblyIsolationByRoamingUser Igual a O mesmo que em


AssemblyIsolationByUser , AssemblyIsolationByUser , mas
mas o armazenamento é sem as cotas, o risco de um
salvo em um local que irá ataque de negação de serviço
transitar se os perfis de aumenta.
usuário móvel estiverem
ativados e as cotas não
forem aplicadas.

AdministerIsolatedStorageByUser Isolamento por usuário. O acesso com essa permissão


Normalmente, apenas permite que o código exiba ou
ferramentas exclua qualquer um dos arquivos
administrativas ou de ou diretórios de armazenamento
depuração usam esse isolados do usuário
nível de permissão. (independentemente do
isolamento do assembly). Riscos
incluem, mas não estão limitados
a, vazamento de informações e
perda de dados.

UnrestrictedIsolatedStorage Isolamento por todos os Esta permissão cria o potencial


usuários, domínios e de um comprometimento total
assemblies. de todos os repositórios isolados
Normalmente, apenas para todos os usuários.
ferramentas
administrativas ou de
depuração usam esse
nível de permissão.

Segurança de componentes de armazenamento


isolados em relação a dados não confiáveis
Esta seção é aplicável às estruturas a seguir:

.NET Framework (todas as versões)


.NET Core 2.1+
.NET 5+

O .NET Framework e .NET Core oferecem armazenamento isolado como um mecanismo


para persistir dados para um usuário, um aplicativo ou um componente. Esse é um
componente herdado projetado principalmente para cenários de Segurança de Acesso
do Código, agora preteridos.

Várias APIs de armazenamento isoladas e ferramentas podem ser usadas para ler os
dados entre limites de confiança. Por exemplo, a leitura dos dados de um escopo amplo
de computador pode agregar dados de outras contas de usuário possivelmente menos
confiáveis no computador. Componentes ou aplicativos lidos de escopos amplos de
computador de armazenamento isolado devem estar cientes das consequências da
leitura desses dados.

APIs sensíveis à segurança que podem ser lidas no


escopo amplo de computador
Componentes ou aplicativos que chamam qualquer uma das seguintes APIs lidas do
escopo amplo de computador:

IsolatedStorageFile.GetEnumerator, passando um escopo que inclui o sinalizador


IsolatedStorageScope.Machine
IsolatedStorageFile.GetMachineStoreForApplication
IsolatedStorageFile.GetMachineStoreForAssembly
IsolatedStorageFile.GetMachineStoreForDomain
IsolatedStorageFile.GetStore, passando um escopo que inclui o sinalizador
IsolatedStorageScope.Machine
IsolatedStorageFile.Remove, passando um escopo que inclui o sinalizador
IsolatedStorageScope.Machine

A ferramenta de armazenamento isolado storeadm.exe será afetada se for chamada com


o comutador /machine , conforme mostrado no seguinte código:

txt

storeadm.exe /machine [any-other-switches]

A ferramenta de armazenamento isolada é fornecida como parte do Visual Studio e do


SDK do .NET Framework.

Se o aplicativo não envolver chamadas para as APIs anteriores ou se o fluxo de trabalho


não envolver chamadas storeadm.exe dessa maneira, este documento não se aplicará.
Impacto em ambientes multiusuários
Conforme mencionado anteriormente, o impacto na segurança dessas APIs resulta da
leitura de dados gravados em um ambiente de confiança em um ambiente de confiança
diferente. O armazenamento isolado geralmente usa um dos três locais para ler e gravar
dados:

1. %LOCALAPPDATA%\IsolatedStorage\ : por exemplo, C:\Users\


<username>\AppData\Local\IsolatedStorage\ para o escopo User .
2. %APPDATA%\IsolatedStorage\ : por exemplo, C:\Users\
<username>\AppData\Roaming\IsolatedStorage\ para o escopo User|Roaming .

3. %PROGRAMDATA%\IsolatedStorage\ : por exemplo, C:\ProgramData\IsolatedStorage\


para o escopo Machine .

Os dois primeiros locais são isolados por usuário. O Windows garante que diferentes
contas de usuário no mesmo computador não possam acessar as pastas de perfil do
usuário umas das outras. Duas contas de usuário diferentes que usam armazenamentos
User ou User|Roaming não verão os dados umas das outras e não podem interferir nos
dados umas das outras.

O terceiro local é compartilhado em todas as contas de usuário no computador. Contas


diferentes podem ler e gravar nesse local e podem ver os dados umas das outras.

Os caminhos anteriores podem ser diferentes com base na versão do Windows em uso.

Agora considere um sistema multiusuário com dois usuários registrados, Mallory e Bob.
Mallory consegue acessar o diretório C:\Users\Mallory\ do perfil de usuário e pode
acessar o local de armazenamento compartilhado amplo do computador
C:\ProgramData\IsolatedStorage\ . Ela não pode acessar o diretório C:\Users\Bob\ do

perfil de usuário do Bob.

Se Mallory quiser atacar Bob, ela pode gravar dados no local de armazenamento amplo
do computador e tentar influenciar Bob a ler do repositório amplo do computador.
Quando Bob executa um aplicativo que lê deste repositório, esse aplicativo funcionará
nos dados que o Mallory colocou lá, mas de dentro do contexto da conta de usuário do
Bob. O restante deste documento contempla vários vetores de ataque e quais etapas os
aplicativos podem executar para minimizar o risco desses ataques.

7 Observação

Para que esse ataque ocorra, Mallory requer:


Uma conta de usuário no computador.
A capacidade de colocar um arquivo em um local conhecido no sistema de
arquivos.
Saber que Bob executará em algum momento um aplicativo que tenta ler
esses dados.

Não são vetores de ameaças que se aplicam a ambientes de área de trabalho


padrão de usuário único, como computadores domésticos ou estações de trabalho
corporativas de funcionário único.

Elevação de privilégio
Um ataque de elevação de privilégio ocorre quando o aplicativo de Bob lê o arquivo de
Mallory e tenta executar uma ação automaticamente com base no conteúdo dessa
carga. Considere um aplicativo que lê o conteúdo de um script de inicialização do
repositório amplo de computador e passa esse conteúdo para Process.Start . Se
Mallory puder colocar um script mal-intencionado no repositório amplo de computador,
quando Bob iniciar seu aplicativo:

O aplicativo analisa e inicia o script mal-intencionado de Mallory sob o contexto do


perfil de usuário de Bob.
Mallory obtém acesso à conta do Bob no computador local.

Negação de serviço
Um ataque de negação de serviço ocorre quando o aplicativo de Bob lê o arquivo de
Mallory e falha ou, de outra forma, para de funcionar corretamente. Considere
novamente o aplicativo mencionado anteriormente, que tenta analisar um script de
inicialização do repositório amplo de computador. Se Mallory puder colocar um arquivo
com conteúdo malformado no repositório amplo de computador, ela poderá:

Fazer com que o aplicativo de Bob gere uma exceção no início do caminho de
inicialização.
Impedir que o aplicativo seja iniciado com êxito devido à exceção.

Ela então negou a Bob a capacidade de inicializar o aplicativo sob sua própria conta de
usuário.

Divulgação de informações confidenciais


Um ataque de divulgação de informações ocorre quando Mallory pode enganar Bob
para divulgar o conteúdo de um arquivo ao qual Mallory normalmente não tem acesso.
Considere que Bob possui um arquivo secreto C:\Users\Bob\secret.txt que Mallory quer
ler. Ela sabe o caminho para este arquivo, mas não pode lê-lo porque o Windows a
proíbe de obter acesso ao diretório do perfil de usuário de Bob.

Em vez disso, Mallory coloca um link físico no repositório amplo de computador. Esse é
um tipo especial de arquivo que não possui conteúdo, apontando para outro arquivo no
disco. Ao tentar ler o arquivo do link físico, o conteúdo do arquivo direcionado pelo link
será lido. Depois de criar o link físico, Mallory ainda não consegue ler o conteúdo do
arquivo porque não tem acesso ao destino ( C:\Users\Bob\secret.txt ) do link. No
entanto, Bob tem acesso a esse arquivo.

Quando o aplicativo de Bob lê do repositório amplo de computador, ele lê


inadvertidamente o conteúdo de seu arquivo secret.txt , como se o próprio arquivo
estivesse presente no repositório amplo de computador. Quando o aplicativo de Bob é
encerrado, se ele tentar salvar o arquivo novamente no repositório amplo de
computador, ele acabará inserindo uma cópia real do arquivo no diretório
*C:\ProgramData\IsolatedStorage*. Como esse diretório é legível por qualquer usuário
no computador, o Mallory agora consegue ler o conteúdo do arquivo.

Práticas recomendadas para se defender contra esses


ataques
Importante: se o ambiente tiver vários usuários mutuamente não confiáveis, não chame
a API IsolatedStorageFile.GetEnumerator(IsolatedStorageScope.Machine) nem invoque
a ferramenta storeadm.exe /machine /list . Ambos pressupõem que estão operando em
dados confiáveis. Se um invasor puder propagar uma carga mal-intencionada no
repositório amplo de computador, essa carga poderá gerar uma elevação do ataque de
privilégios no contexto do usuário que executa esses comandos.

Se estiver operando em um ambiente de vários usuários, reconsidere o uso de recursos


de armazenamento isolados direcionados ao escopo do Computador. Se um aplicativo
precisar ler dados de um local amplo de computador, prefira ler os dados de um local
que seja gravável somente por contas de administrador. O diretório %PROGRAMFILES% e o
hive do registro HKLM são exemplos de locais graváveis somente por administradores e
legíveis por todos. Os dados lidos desses locais são, portanto, considerados confiáveis.

Se um aplicativo precisar usar o escopo do Computador em um ambiente de vários


usuários, valide o conteúdo de qualquer arquivo lido no repositório amplo de
computador. Se o aplicativo desserializar grafos de objeto desses arquivos, considere o
uso de serializadores mais seguros, como XmlSerializer em vez de serializadores
perigosos como BinaryFormatter ou NetDataContractSerializer . Tenha cuidado com
grafos de objetos profundamente aninhados ou grafos de objetos que executam a
alocação de recursos com base no conteúdo do arquivo.

Locais de Armazenamento Isolados


Às vezes é útil verificar uma alteração em um armazenamento usando o sistema de
arquivos do sistema operacional. Talvez você também queira saber o local dos arquivos
de armazenamento isolados. Esse local é diferente dependendo do sistema operacional.
A tabela a seguir mostra os locais raiz em que um armazenamento isolado é criado em
alguns sistemas operacionais comuns. Procure diretórios Microsoft\IsolatedStorage
neste local raiz. Você deve alterar as configurações de pasta para mostrar arquivos e
pastas ocultos para ver um armazenamento isolado no sistema de arquivos.

Sistema operacional Localização no sistema de arquivos

Windows 2000, Windows XP, Windows Server Repositórios com suporte a uso móvel =
2003 (atualização do Windows NT 4.0)
<SYSTEMROOT>\Perfis\<usuário>\Dados de
aplicativos

Repositórios não móveis =

<SYSTEMROOT>\Perfis\
<usuário>\Configurações locais\Dados de
aplicativos

Windows 2000 – Instalação limpa (e Repositórios com suporte a uso móvel =


atualizações do Windows 98 e Windows NT
3.51) <SYSTEMDRIVE>\Documentos e
configurações\<usuário>\Dados de aplicativos

Repositórios não móveis =

<SYSTEMDRIVE>\Documentos e
configurações\<usuário>\Configurações
locais\Dados de aplicativos
Sistema operacional Localização no sistema de arquivos

Windows XP, Windows Server 2003 – Instalação Repositórios com suporte a uso móvel =
limpa (e atualizações do Windows 2000 e
Windows 98) <SYSTEMDRIVE>\Documentos e
configurações\<usuário>\Dados de aplicativos

Repositórios não móveis =

<SYSTEMDRIVE>\Documentos e
configurações\<usuário>\Configurações
locais\Dados de aplicativos

Windows 8, Windows 7, Windows Server 2008, Repositórios com suporte a uso móvel =
Windows Vista
<SYSTEMDRIVE>\Usuários\
<usuário>\AppData\Roaming

Repositórios não móveis =

<SYSTEMDRIVE>\Usuários\
<usuário>\AppData\Local

Criando, Enumerando e Excluindo


Armazenamento Isolado
O .NET fornece três classes no namespace System.IO.IsolatedStorage para ajudar a
executar as tarefas que envolvem o armazenamento isolado:

IsolatedStorageFile, que é derivado de System.IO.IsolatedStorage.IsolatedStorage e


fornece gerenciamento básico de assemblies armazenados e arquivos de
aplicativos. Uma instância da classe IsolatedStorageFile representa um único
repositório localizado no sistema de arquivos.

IsolatedStorageFileStream deriva de System.IO.FileStream e fornece acesso aos


arquivos em um repositório.

IsolatedStorageScope é uma enumeração que permite que você crie e selecione


um repositório com o tipo de isolamento apropriado.

As classes de armazenamento isolado permitem que você crie, enumere e exclua o


armazenamento isolado. Os métodos para a execução dessas tarefas estão disponíveis
através do objeto IsolatedStorageFile. Algumas operações exigem que você tenha a
permissão IsolatedStorageFilePermission que representa o direito para administrar o
armazenamento isolado; você também pode precisar ter direitos do sistema operacional
para acessar o arquivo ou diretório.

Para uma série de exemplos que demonstram tarefas de armazenamento isoladas


comuns, confira os tópicos listados em Tópicos relacionados.

Cenários para armazenamento isolado


O armazenamento isolado é útil em muitas situações, inclusive nestes quatro cenários:

Controles baixados. Controles de código gerenciado baixados da Internet não têm


permissão para escrever no disco rígido por meio de classes normais de E/S, mas
eles podem usar o armazenamento isolado para persistir as configurações do
usuário e o estado dos aplicativos.

Armazenamento de componentes compartilhado. Componentes que são


compartilhados entre aplicativos podem usar o armazenamento isolado para
fornecer acesso controlado a repositórios de dados.

Armazenamento de servidor. Aplicativos de servidor podem usar o


armazenamento isolado para fornecer armazenamento individual para um grande
número de usuários que estão realizando solicitações ao aplicativo. Pelo fato do
armazenamento isolado ser sempre segredado por usuário, o servidor deve
personificar o usuário que fez a solicitação. Nesse caso, os dados são isolados com
base na identidade do principal, que é a mesma identidade que o aplicativo usa
para fazer a distinção entre os seus usuários.

Roaming. Os aplicativos também podem usar o armazenamento isolado com perfis


de usuários móveis. Isso permite que repositórios isolados de um usuário façam
roaming com o perfil.

Não use um armazenamento isolado nas seguintes situações:

Para armazenar segredos de alto valor, como chaves sem criptografia ou senhas,
pois o armazenamento isolado não é protegido contra código altamente confiável,
código não gerenciado ou usuários confiáveis do computador.

Para armazenar código.

Para armazenar configurações e opções de implantação, as quais são controladas


pelos administradores. (As preferências do usuário não são consideradas como
definições de configuração, pois os administradores não as controlam.)
Muitos aplicativos usam bancos de dados para armazenar e isolar os dados. Nesse caso
uma ou mais linhas em um banco de dados podem representar o armazenamento para
um usuário específico. Você pode optar por usar armazenamento isolado em vez de um
banco de dados quando o número de usuários é pequeno, quando as despesas no uso
de um banco de dados é significativa ou quando não existe nenhum recurso de banco
de dados. Além disso, quando o aplicativo requer um armazenamento que seja mais
flexível e complexo do que aquele fornecido por uma linha em um banco de dados, o
armazenamento isolado pode fornecer uma alternativa viável.

Artigos relacionados
Título Descrição

Tipos de isolamento Descreve os diferentes tipos de isolamento.

Como: Obter repositórios para o Fornece um exemplo de uso da classe IsolatedStorageFile


armazenamento isolado para obter um armazenamento isolado por usuário e
assembly.

Como: Enumerar repositórios Mostra como usar o método


para o armazenamento isolado IsolatedStorageFile.GetEnumerator para calcular o tamanho
de todo o armazenamento isolado para o usuário.

Como: Excluir repositórios no Mostra como usar o método IsolatedStorageFile.Remove de


armazenamento isolado duas maneiras diferentes para excluir repositórios isolados.

Como: Prever condições de Mostra como a medir o espaço restante em um


espaço insuficiente com o armazenamento isolado.
armazenamento isolado

Como: Criar arquivos e diretórios Fornece alguns exemplos de criação de arquivos e diretórios
no armazenamento isolado em um repositório isolado.

Como: Localizar arquivos e Demonstra como ler a estrutura de diretórios e arquivos no


diretórios existentes no armazenamento isolado.
armazenamento isolado

Como: Ler e gravar em arquivos Fornece um exemplo de gravação de uma cadeia de


no armazenamento isolado caracteres em um arquivo de armazenamento isolado,
seguida por sua leitura de volta.

Como: Excluir arquivos e Demonstra como excluir arquivos e diretórios isolados.


diretórios no armazenamento
isolado

E/S de arquivo e de fluxo Explica como você pode executar acesso síncrono e
assíncrono a fluxos de dados e arquivos.
Referência
System.IO.IsolatedStorage.IsolatedStorage

System.IO.IsolatedStorage.IsolatedStorageFile

System.IO.IsolatedStorage.IsolatedStorageFileStream

System.IO.IsolatedStorage.IsolatedStorageScope
Tipos de isolamento
Artigo • 07/04/2023

O acesso ao armazenamento isolado é sempre restrito ao usuário que o criou. Para


implementar esse tipo de isolamento, o common language runtime usa a mesma noção
de identidade de usuário que o sistema operacional reconhece, que é a identidade
associada ao processo no qual o código está em execução quando o armazenamento é
aberto. Essa é uma identidade de usuário autenticado, mas a representação pode fazer
com que a identidade do usuário atual seja alterada dinamicamente.

O acesso ao armazenamento isolado também é restrito de acordo com a identidade


associada ao domínio do aplicativo e ao assembly ou apenas ao assembly. O runtime
obtém essas identidades das seguintes maneiras:

A identidade de domínio representa a evidência do aplicativo, que, no caso de um


aplicativo Web, pode ser a URL completa. Para código hospedado pelo shell, a
identidade de domínio pode ser baseada no caminho de diretório de aplicativo.
Por exemplo, se o executável é executado do caminho C:\Office\MyApp.exe, a
identidade de domínio é C:\Office\MyApp.exe.

A identidade do assembly é a evidência do assembly. Pode vir de uma assinatura


digital de criptografia, que pode ser o nome forte do assembly, o editor de
software do assembly ou sua identidade de URL. Se um assembly tiver um nome
forte e uma identidade do editor de software, a identidade do editor de software
será usada. Se o assembly vier da Internet e não estiver assinado, a identidade de
URL será usada. Para saber mais sobre assemblies e nomes fortes, confira
Programação com assemblies.

Os armazenamentos móveis migram com um usuário que tem um perfil de usuário


móvel. Os arquivos são gravados em um diretório de rede e são baixados em
qualquer computador em que o usuário faz logon. Para saber mais sobre perfis de
usuário móvel, confira IsolatedStorageScope.Roaming.

Combinando os conceitos de usuário, domínio e identidade de assembly, o


armazenamento isolado pode isolar dados das seguintes maneiras, cada uma das quais
tem seus próprios cenários de uso:

Isolamento por usuário e assembly

Isolamento por usuário, domínio e assembly

Um desses isolamentos pode ser combinado a um perfil de usuário móvel. Para saber
mais, confira a seção Armazenamento isolado e roaming.
A seguinte ilustração demonstra como os repositórios são isolados em escopos
diferentes:

Observe que, exceto para armazenamentos móveis, o armazenamento isolado é sempre


implicitamente isolado por computador, pois usa os recursos de armazenamento locais
para um determinado computador.

) Importante

O armazenamento isolado não está disponível para aplicativos da Store do


Windows 8.x. Em vez disso, use as classes de dados de aplicativos nos namespaces
Windows.Storage incluídos na API do Windows Runtime para armazenar dados e

arquivos locais. Para saber mais, confira Dados de aplicativo no Centro de


Desenvolvimento do Windows.

Isolamento por usuário e assembly


Quando o assembly que usa o repositório de dados precisa ser acessível de qualquer
domínio de aplicativo, o isolamento por usuário e assembly é apropriado. Normalmente,
nessa situação, o armazenamento isolado é usado para armazenar dados que se
aplicam a vários aplicativos e não estão vinculados a um aplicativo específico, como o
nome do usuário ou informações de licença. Para acessar o armazenamento isolado por
usuário e assembly, o código deve ser confiável para transferir informações entre
aplicativos. Normalmente, o isolamento pelo usuário e assembly é permitido em
intranets, mas não na Internet. Chamar o método estático IsolatedStorageFile.GetStore e
passar um usuário e um assembly IsolatedStorageScope retorna um armazenamento
com esse tipo de isolamento.

O exemplo de código a seguir recupera um armazenamento que é isolado por usuário e


assembly. O repositório pode ser acessado por meio do objeto isoFile .
C#

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);

Para obter um exemplo que usa os parâmetros de evidência, confira


GetStore(IsolatedStorageScope, Evidence, Type, Evidence, Type).

O método GetUserStoreForAssembly está disponível como um atalho, conforme


mostrado no exemplo de código a seguir. Este atalho não pode ser usado para abrir
repositórios compatíveis com roaming. Use GetStore nesses casos.

C#

IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForAssembly();

Isolamento por usuário, domínio e assembly


Se o aplicativo usa um assembly de terceiros que exige um armazenamento de dados
privados, você pode usar o armazenamento isolado para armazenar os dados privados.
O isolamento por usuário, domínio e assembly garante que somente o código em
determinado assembly possa acessar os dados e somente quando o assembly for usado
pelo aplicativo que estava em execução quando o assembly criou o repositório e
somente quando o usuário para quem o armazenamento foi criado executar o
aplicativo. O isolamento por usuário, domínio e assembly impede que o assembly de
terceiros vaze dados para outros aplicativos. Esse tipo de isolamento deverá ser a opção
padrão, se você souber que deseja usar o armazenamento isolado e não tiver certeza de
qual tipo de isolamento usar. Chamar o método estático GetStore de IsolatedStorageFile
e passar um usuário, domínio e assembly IsolatedStorageScope retorna um
armazenamento com esse tipo de isolamento.

O exemplo de código a seguir recupera um armazenamento isolado por usuário,


domínio e assembly. O repositório pode ser acessado por meio do objeto isoFile .

C#

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Domain |
IsolatedStorageScope.Assembly, null, null);
Outro método está disponível como um atalho, conforme mostrado no exemplo de
código a seguir. Este atalho não pode ser usado para abrir repositórios compatíveis com
roaming. Use GetStore nesses casos.

C#

IsolatedStorageFile isoFile = IsolatedStorageFile.GetUserStoreForDomain();

Armazenamento isolado e roaming


Os perfis de usuário móvel são um recurso do Windows que permite que um usuário
configure uma identidade em uma rede e use essa identidade para fazer logon em
qualquer computador da rede, mantendo todas as configurações personalizadas. Um
assembly que usa o armazenamento isolado pode especificar que o armazenamento
isolado do usuário deve migrar com o perfil de usuário móvel. O roaming pode ser
usado em conjunto com o isolamento por usuário e assembly ou com o isolamento por
usuário, domínio e assembly. Se um escopo móvel não for usado, os repositórios não
entrarão em roaming, mesmo que um perfil de usuário móvel seja usado.

O exemplo de código a seguir recupera um armazenamento móvel isolado por usuário


e assembly. O repositório pode ser acessado por meio do objeto isoFile .

C#

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly |
IsolatedStorageScope.Roaming, null, null);

Um escopo de domínio pode ser adicionado para criar um armazenamento móvel


isolado por usuário, domínio e aplicativo. O código de exemplo a seguir demonstra isso.

C#

IsolatedStorageFile isoFile =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain |
IsolatedStorageScope.Roaming, null, null);

Confira também
IsolatedStorageScope
Armazenamentos isolado
Como: Obter repositórios para o
armazenamento isolado
Artigo • 10/05/2023

Um repositório isolado expõe um sistema de arquivos virtual dentro de um


compartimento de dados. A classe IsolatedStorageFile fornece vários métodos para
interagir com um repositório isolado. Para criar e recuperar repositórios, o
IsolatedStorageFile fornece três métodos estáticos:

GetUserStoreForAssembly retorna o armazenamento que é isolado pelo usuário e


pelo assembly.

GetUserStoreForDomain retorna o armazenamento que é isolado pelo domínio e


pelo assembly.

Os dois métodos recuperam um repositório que pertence ao código do qual eles


são chamados.

O método estático GetStore retorna um repositório isolado que é especificado


passando uma combinação de parâmetros de escopo.

O exemplo de código a seguir retorna um repositório que é isolado pelo usuário, pelo
assembly e pelo domínio.

C#

IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly | IsolatedStorageScope.Domain, null,
null);

Você pode usar o método GetStore para especificar se um repositório deve ser usado
com um perfil de usuário móvel. Para obter detalhes sobre como configurar isso, confira
Tipos de isolamento.

Os repositórios isolados obtidos em diferentes assemblies são, por padrão, os


repositórios diferentes. Você pode acessar o repositório de um assembly ou de um
domínio diferente passando a evidência de assembly ou domínio nos parâmetros do
método GetStore. Isso exige uma permissão para acessar o armazenamento isolado pela
identidade do domínio do aplicativo. Para obter mais informações, confira as
sobrecargas do método GetStore.
Os métodos GetUserStoreForAssembly, GetUserStoreForDomain e GetStore retornam
um objeto IsolatedStorageFile. Para ajudá-lo a decidir qual tipo de isolamento é mais
apropriado para sua situação, confira Tipos de isolamento. Quando você tem um objeto
de arquivo de armazenamento isolado, você pode usar os métodos de armazenamento
isolado para ler, gravar, criar e excluir arquivos e diretórios.

Não há mecanismos que impeçam o código de passar um objeto IsolatedStorageFile


para o código que não tem acesso suficiente para obter o próprio repositório. As
identidades de domínio e de assembly e as permissões de armazenamento isolado são
verificadas apenas quando uma referência a um objeto IsolatedStorage é obtida,
normalmente no método GetUserStoreForAssembly, GetUserStoreForDomain ou
GetStore. Proteger referências a objetos IsolatedStorageFile é, portanto, a
responsabilidade do código que usa essas referências.

Exemplo
O código a seguir fornece um exemplo simples de uma classe obtendo um repositório
que é isolado pelo usuário e pelo assembly. O código pode ser alterado para recuperar
um repositório que é isolado pelo usuário, domínio e assembly adicionando
IsolatedStorageScope.Domain aos argumentos que o método GetStore passa.

Depois de executar o código, você pode confirmar se um repositório foi criado


digitando StoreAdm /LIST na linha de comando. Essa ação executa a Ferramenta de
armazenamento isolado (Storeadm.exe) e lista todos os atuais repositórios isolados para
o usuário.

C#

using System;
using System.IO.IsolatedStorage;

public class ObtainingAStore


{
public static void Main()
{
// Get a new isolated store for this assembly and put it into an
// isolated store object.

IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);
}
}
Confira também
IsolatedStorageFile
IsolatedStorageScope
Armazenamentos isolado
Tipos de isolamento
Assemblies no .NET
Como: Enumerar repositórios para o
armazenamento isolado
Artigo • 07/04/2023

Você pode enumerar todos os repositórios isolados para o usuário atual usando o
método estático IsolatedStorageFile.GetEnumerator. Esse método usa um valor
IsolatedStorageScope e retorna um enumerador IsolatedStorageFile. Para enumerar os
repositórios, você deve ter a permissão IsolatedStorageFilePermission que especifica o
valor AdministerIsolatedStorageByUser. Se você chamar o método GetEnumerator com
o valor User, ele retornará uma matriz de objetos IsolatedStorageFile que são definidos
para o usuário atual.

Exemplo
O exemplo de código a seguir obtém um repositório que é isolado pelo usuário e pelo
assembly, cria alguns arquivos e recupera esses arquivos usando o método
GetEnumerator.

C#

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;

public class EnumeratingStores


{
public static void Main()
{
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null))
{
isoStore.CreateFile("TestFileA.Txt");
isoStore.CreateFile("TestFileB.Txt");
isoStore.CreateFile("TestFileC.Txt");
isoStore.CreateFile("TestFileD.Txt");
}

IEnumerator allFiles =
IsolatedStorageFile.GetEnumerator(IsolatedStorageScope.User);
long totalsize = 0;

while (allFiles.MoveNext())
{
IsolatedStorageFile storeFile =
(IsolatedStorageFile)allFiles.Current;
totalsize += (long)storeFile.UsedSize;
}

Console.WriteLine("The total size = " + totalsize);


}
}

Confira também
IsolatedStorageFile
Armazenamentos isolado
Como: Excluir repositórios no
armazenamento isolado
Artigo • 07/04/2023

A classe IsolatedStorageFile fornece dois métodos para excluir arquivos de


armazenamento isolado:

O método de instância Remove() não recebe qualquer argumento e exclui o


armazenamento que o chama. Nenhuma permissão é necessária para essa
operação. Qualquer código que possa acessar o armazenamento pode excluir
qualquer ou todos os dados dentro dele.

O método estático Remove(IsolatedStorageScope) usa o valor de enumeração User


e exclui todos os armazenamentos para o usuário que está executando o código.
Esta operação exige a permissão IsolatedStorageFilePermission para o valor
AdministerIsolatedStorageByUser.

Exemplo
O exemplo de código a seguir demonstra o uso dos métodos Remove estático e de
instância . A classe obtém dois armazenamentos; uma é isolado para o usuário e o
assembly, e o outro é isolado para o usuário, domínio e assembly. O armazenamento de
usuário, domínio e assembly é excluído chamando o método Remove() do arquivo de
armazenamento isolado isoStore1 . Em seguida, todos os armazenamentos restantes
para o usuário são excluídos chamando o método estático
Remove(IsolatedStorageScope).

C#

using System;
using System.IO.IsolatedStorage;

public class DeletingStores


{
public static void Main()
{
// Get a new isolated store for this user, domain, and assembly.
// Put the store into an IsolatedStorageFile object.

IsolatedStorageFile isoStore1 =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly,
null, null);
Console.WriteLine("A store isolated by user, assembly, and domain
has been obtained.");

// Get a new isolated store for user and assembly.


// Put that store into a different IsolatedStorageFile object.

IsolatedStorageFile isoStore2 =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);
Console.WriteLine("A store isolated by user and assembly has been
obtained.");

// The Remove method deletes a specific store, in this case the


// isoStore1 file.

isoStore1.Remove();
Console.WriteLine("The user, domain, and assembly isolated store has
been deleted.");

// This static method deletes all the isolated stores for this user.

IsolatedStorageFile.Remove(IsolatedStorageScope.User);
Console.WriteLine("All isolated stores for this user have been
deleted.");
} // End of Main.
}

Confira também
IsolatedStorageFile
Armazenamentos isolado
Como: Prever condições de espaço
insuficiente com o armazenamento
isolado
Artigo • 07/04/2023

O código que usa armazenamento isolado é restrito por uma cota que especifica o
tamanho máximo do compartimento de dados no qual arquivos de armazenamento e
diretórios isolados existem. A cota é definida pela política de segurança e é configurável
por administradores. Se o tamanho máximo permitido for ultrapassado ao tentar gravar
dados, uma exceção IsolatedStorageException será lançada e a operação falhará. Isso
ajuda a evitar ataques de negação de serviço que podem fazer com que o aplicativo
recuse solicitações porque o armazenamento de dados está cheio.

Para ajudar você a determinar se uma determinada tentativa de gravação


provavelmente falhará por esse motivo, a classe IsolatedStorage fornece três
propriedades somente leitura: AvailableFreeSpace, UsedSize e Quota. Você pode usar
essas propriedades para determinar se a gravação no armazenamento fará com que o
tamanho máximo permitido de armazenamento seja ultrapassado. Lembre-se de que o
armazenamento isolado pode ser acessado simultaneamente; portanto, ao calcular a
quantidade de armazenamento restante, o espaço de armazenamento poderá ser
consumido até você tentar gravar no armazenamento. No entanto, você pode usar o
tamanho máximo do armazenamento para ajudar a determinar se o limite superior no
armazenamento disponível está prestes a ser alcançado.

A propriedade Quota depende da evidência do funcionamento correto do assembly. Por


esse motivo, você deve recuperar essa propriedade somente em objetos
IsolatedStorageFile que foram criados usando o método GetUserStoreForAssembly,
GetUserStoreForDomain ou GetStore. Os objetos IsolatedStorageFile que foram criados
de qualquer outra forma (por exemplo, objetos retornados do método GetEnumerator)
não retornarão um tamanho máximo preciso.

Exemplo
O exemplo de código a seguir obtém um armazenamento isolado, cria alguns arquivos
e recupera a propriedade AvailableFreeSpace. O espaço restante é relatado em bytes.

C#
using System;
using System.IO;
using System.IO.IsolatedStorage;

public class CheckingSpace


{
public static void Main()
{
// Get an isolated store for this assembly and put it into an
// IsolatedStoreFile object.
IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);

// Create a few placeholder files in the isolated store.


new IsolatedStorageFileStream("InTheRoot.txt", FileMode.Create,
isoStore);
new IsolatedStorageFileStream("Another.txt", FileMode.Create,
isoStore);
new IsolatedStorageFileStream("AThird.txt", FileMode.Create,
isoStore);
new IsolatedStorageFileStream("AFourth.txt", FileMode.Create,
isoStore);
new IsolatedStorageFileStream("AFifth.txt", FileMode.Create,
isoStore);

Console.WriteLine(isoStore.AvailableFreeSpace + " bytes of space


remain in this isolated store.");
} // End of Main.
}

Confira também
IsolatedStorageFile
Armazenamentos isolado
Como: Obter repositórios para o armazenamento isolado
Como: Criar arquivos e diretórios no
armazenamento isolado
Artigo • 07/04/2023

Depois de obter um repositório isolado, você pode criar diretórios e arquivos para
armazenar dados. Em um repositório, nomes de arquivo e diretório são especificados
com relação à raiz do sistema de arquivos virtual.

Para criar um diretório, use o método de instância IsolatedStorageFile.CreateDirectory.


Se você especificar um subdiretório de um diretório que não existe, os dois diretórios
serão criados. Se você especificar um diretório que já existe, o método retornará sem
criar um diretório, e nenhuma exceção será lançada. No entanto, se você especificar um
nome de diretório que contém caracteres inválidos, uma exceção
IsolatedStorageException será lançada.

Para criar um arquivo, use o método IsolatedStorageFile.CreateFile.

No sistema operacional Windows, nomes de arquivo de armazenamento isolado e de


diretório diferenciam maiúsculas de minúsculas. Ou seja, se você criar um arquivo
chamado ThisFile.txt , depois criar outro arquivo chamado THISFILE.TXT , somente um
arquivo será criado. O nome do arquivo mantém sua capitalização original para fins de
exibição.

A criação de arquivo de armazenamento isolado gerará um IsolatedStorageException se


o caminho contiver um diretório que não existe.

Exemplo
O exemplo de código a seguir ilustra como criar arquivos e diretórios em um repositório
isolado.

C#

using System;
using System.IO;
using System.IO.IsolatedStorage;

public class CreatingFilesDirectories


{
public static void Main()
{
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly, null, null))
{
isoStore.CreateDirectory("TopLevelDirectory");
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel");

isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
Console.WriteLine("Created directories.");

isoStore.CreateFile("InTheRoot.txt");
Console.WriteLine("Created a new file in the root.");

isoStore.CreateFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine("Created a new file in the InsideDirectory.");
}
}
}

Confira também
IsolatedStorageFile
IsolatedStorageFileStream
Armazenamentos isolado
Como: Localizar arquivos e diretórios
existentes no armazenamento isolado
Artigo • 07/04/2023

Para procurar um diretório no armazenamento isolado, use o método


IsolatedStorageFile.GetDirectoryNames. Esse método usa uma cadeia de caracteres que
representa um padrão de pesquisa. Você pode usar o caractere único (?) e vários
caracteres (*) caracteres curinga no padrão de pesquisa, mas os caracteres curinga
devem aparecer na parte final do nome. Por exemplo, directory1/*ect* é uma cadeia
de caracteres de pesquisa válida, mas *ect*/directory2 não é.

Para procurar um arquivo, use o método IsolatedStorageFile.GetFileNames. A restrição


para caracteres curinga em cadeias de caracteres de pesquisa que se aplica a
GetDirectoryNames também se aplica a GetFileNames.

Nenhum desses métodos é recursivo; a classe IsolatedStorageFile fornece métodos para


listar todos os diretórios ou arquivos no repositório. No entanto, os métodos recursivos
são mostrados no exemplo de código a seguir.

Exemplo
O exemplo de código a seguir ilustra como criar arquivos e diretórios em um repositório
isolado. Primeiro, um repositório isolado para usuário, domínio e assembly é recuperado
e colocado na variável isoStore . O método CreateDirectory é usado para configurar
algumas pastas diferentes, e o construtor IsolatedStorageFileStream(String, FileMode,
IsolatedStorageFile) cria alguns arquivos nesses diretórios. O código executa um loop
através dos resultados do método GetAllDirectories . Esse método usa
GetDirectoryNames para localizar todos os nomes de diretório no diretório atual. Esses
nomes são armazenados em uma matriz, e GetAllDirectories chama a si mesmo,
passando cada diretório encontrado. Como resultado, todos os nomes de diretório são
retornados em uma matriz. Em seguida, o código chama o método GetAllFiles . Este
método chama GetAllDirectories para descobrir os nomes de todos os diretórios e
verifica cada diretório de arquivos usando o método GetFileNames. O resultado é
retornado em uma matriz para exibição.

C#

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Collections;
using System.Collections.Generic;

public class FindingExistingFilesAndDirectories


{
// Retrieves an array of all directories in the store, and
// displays the results.
public static void Main()
{
// This part of the code sets up a few directories and files in the
// store.
IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);
isoStore.CreateDirectory("TopLevelDirectory");
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel");

isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
isoStore.CreateFile("InTheRoot.txt");

isoStore.CreateFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
// End of setup.

Console.WriteLine('\r');
Console.WriteLine("Here is a list of all directories in this
isolated store:");

foreach (string directory in GetAllDirectories("*", isoStore))


{
Console.WriteLine(directory);
}
Console.WriteLine('\r');

// Retrieve all the files in the directory by calling the GetFiles


// method.

Console.WriteLine("Here is a list of all the files in this isolated


store:");
foreach (string file in GetAllFiles("*", isoStore)){
Console.WriteLine(file);
}
} // End of Main.

// Method to retrieve all directories, recursively, within a store.


public static List<String> GetAllDirectories(string pattern,
IsolatedStorageFile storeFile)
{
// Get the root of the search string.
string root = Path.GetDirectoryName(pattern);

if (root != "")
{
root += "/";
}
// Retrieve directories.
List<String> directoryList = new List<String>
(storeFile.GetDirectoryNames(pattern));

// Retrieve subdirectories of matches.


for (int i = 0, max = directoryList.Count; i < max; i++)
{
string directory = directoryList[i] + "/";
List<String> more = GetAllDirectories(root + directory + "*",
storeFile);

// For each subdirectory found, add in the base path.


for (int j = 0; j < more.Count; j++)
{
more[j] = directory + more[j];
}

// Insert the subdirectories into the list and


// update the counter and upper bound.
directoryList.InsertRange(i + 1, more);
i += more.Count;
max += more.Count;
}

return directoryList;
}

public static List<String> GetAllFiles(string pattern,


IsolatedStorageFile storeFile)
{
// Get the root and file portions of the search string.
string fileString = Path.GetFileName(pattern);

List<String> fileList = new List<String>


(storeFile.GetFileNames(pattern));

// Loop through the subdirectories, collect matches,


// and make separators consistent.
foreach (string directory in GetAllDirectories("*", storeFile))
{
foreach (string file in storeFile.GetFileNames(directory + "/" +
fileString))
{
fileList.Add((directory + "/" + file));
}
}

return fileList;
} // End of GetFiles.
}

Confira também
IsolatedStorageFile
Armazenamentos isolado
Como: Ler e gravar em arquivos no
armazenamento isolado
Artigo • 07/04/2023

Para ler ou gravar de um arquivo em um armazenamento isolado, use um objeto


IsolatedStorageFileStream com um leitor de fluxo (objeto StreamReader) ou o gravador
do fluxo (objeto StreamWriter).

Exemplo
O exemplo de código a seguir obtém um repositório isolado e verifica se existe um
arquivo chamado TestStore.txt no repositório. Se não existir, ele cria o arquivo e grava
"Armazenamento Isolado do Hello" no arquivo. Se TestStore.txt já existir, o código de
exemplo será lido a partir do arquivo.

C#

using System;
using System.IO;
using System.IO.IsolatedStorage;

namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Assembly, null, null);

if (isoStore.FileExists("TestStore.txt"))
{
Console.WriteLine("The file already exists!");
using (IsolatedStorageFileStream isoStream = new
IsolatedStorageFileStream("TestStore.txt", FileMode.Open, isoStore))
{
using (StreamReader reader = new
StreamReader(isoStream))
{
Console.WriteLine("Reading contents:");
Console.WriteLine(reader.ReadToEnd());
}
}
}
else
{
using (IsolatedStorageFileStream isoStream = new
IsolatedStorageFileStream("TestStore.txt", FileMode.CreateNew, isoStore))
{
using (StreamWriter writer = new
StreamWriter(isoStream))
{
writer.WriteLine("Hello Isolated Storage");
Console.WriteLine("You have written to the file.");
}
}
}
}
}
}

Confira também
IsolatedStorageFile
IsolatedStorageFileStream
System.IO.FileMode
System.IO.FileAccess
System.IO.StreamReader
System.IO.StreamWriter
E/S de arquivo e de fluxo
Armazenamentos isolado
Como: Excluir arquivos e diretórios no
armazenamento isolado
Artigo • 07/04/2023

Você pode excluir pastas e arquivos em um arquivo de armazenamento isolado. Em um


repositório, nomes de arquivo e diretório são dependentes do sistema operacional e
são especificados como relativos à raiz do sistema de arquivos virtual. Eles não
diferenciam maiúsculas de minúsculas em sistemas operacionais Windows.

A classe System.IO.IsolatedStorage.IsolatedStorageFile fornece dois métodos para


excluir arquivos e diretórios: DeleteDirectory e DeleteFile. Uma exceção
IsolatedStorageException será gerada se você tentar excluir um arquivo ou um diretório
que não exista. Se você incluir um caractere curinga no nome, DeleteDirectory gerará
uma exceção IsolatedStorageException e DeleteFile gerará uma exceção
ArgumentException.

O método DeleteDirectory falhará se o diretório contiver arquivos ou subpastas. Você


pode usar os métodos GetFileNames e GetDirectoryNames para recuperar os arquivos e
diretórios existentes. Para saber mais sobre o sistema de arquivos virtual de um
repositório de pesquisa, confira Como localizar arquivos e diretórios existentes no
armazenamento isolado.

Exemplo
O exemplo de código a seguir cria e exclui vários diretórios e arquivos.

C#

using System;
using System.IO.IsolatedStorage;
using System.IO;

public class DeletingFilesDirectories


{
public static void Main()
{
// Get a new isolated store for this user domain and assembly.
// Put the store into an isolatedStorageFile object.

IsolatedStorageFile isoStore =
IsolatedStorageFile.GetStore(IsolatedStorageScope.User |
IsolatedStorageScope.Domain | IsolatedStorageScope.Assembly,
null, null);
Console.WriteLine("Creating Directories:");

// This code creates several different directories.

isoStore.CreateDirectory("TopLevelDirectory");
Console.WriteLine("TopLevelDirectory");
isoStore.CreateDirectory("TopLevelDirectory/SecondLevel");
Console.WriteLine("TopLevelDirectory/SecondLevel");

// This code creates two new directories, one inside the other.

isoStore.CreateDirectory("AnotherTopLevelDirectory/InsideDirectory");
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory");
Console.WriteLine();

// This code creates a few files and places them in the directories.

Console.WriteLine("Creating Files:");

// This file is placed in the root.

IsolatedStorageFileStream isoStream1 = new


IsolatedStorageFileStream("InTheRoot.txt",
FileMode.Create, isoStore);
Console.WriteLine("InTheRoot.txt");

isoStream1.Close();

// This file is placed in the InsideDirectory.

IsolatedStorageFileStream isoStream2 = new


IsolatedStorageFileStream(
"AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt",
FileMode.Create, isoStore);

Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine();

isoStream2.Close();

Console.WriteLine("Deleting File:");

// This code deletes the HereIAm.txt file.

isoStore.DeleteFile("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");

Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/HereIAm.txt");
Console.WriteLine();

Console.WriteLine("Deleting Directory:");

// This code deletes the InsideDirectory.


isoStore.DeleteDirectory("AnotherTopLevelDirectory/InsideDirectory/");
Console.WriteLine("AnotherTopLevelDirectory/InsideDirectory/");
Console.WriteLine();
} // End of main.
}

Confira também
System.IO.IsolatedStorage.IsolatedStorageFile
Armazenamentos isolado
Operações básicas de pipe no .NET
Artigo • 10/05/2023

Os pipes fornecem um meio de comunicação entre processos. Existem dois tipos de


pipes:

Pipes anônimos.

Os pipes anônimos fornecem comunicação entre processos em um computador


local. Os pipes anônimos exigem menos sobrecarga do que os pipes nomeados
mas oferecem serviços limitados. Os pipes anônimos são unidirecionais e não
podem ser usados em uma rede. Eles oferecem suporte a apenas uma única
instância de servidor. Os pipes anônimos são úteis para a comunicação entre
threads ou entre processos pai e filho em que os identificadores de pipe podem
ser facilmente passados para o processo filho quando ele é criado.

No .NET, você implementa pipes anônimos usando as classes


AnonymousPipeServerStream e AnonymousPipeClientStream.

Veja Como usar pipes anônimos para a comunicação entre processos locais.

Pipes nomeados.

Os pipes nomeados fornecem a comunicação entre processos entre um servidor


de pipe e um ou mais clientes pipe. Os pipes nomeados podem ser unidirecionais
ou bidirecionais. Eles oferecem suporte à comunicação baseada em mensagens e
permitem que vários clientes se conectem simultaneamente ao processo do
servidor usando o mesmo nome de pipe. Os pipes nomeados também oferecem
suporte à representação, que permite aos processos de conexão usar suas próprias
permissões em servidores remotos.

No .NET, você implementa pipes nomeados usando as classes


NamedPipeServerStream e NamedPipeClientStream.

Veja Como usar pipes nomeados para comunicação entre processos na rede.

Confira também
E/S de arquivo e de fluxo
Como: Usar pipes anônimos para comunicação entre processos locais
Como: Usar pipes nomeados para comunicação entre processos na rede
Como: Usar pipes anônimos para
comunicação entre processos locais
Artigo • 10/05/2023

Os pipes anônimos fornecem comunicação entre processos em um computador local.


Eles oferecem menos funcionalidades que pipes nomeados, mas também exigem menos
sobrecarga. Você pode usar pipes anônimos para facilitar a comunicação entre
processos em um computador local. Você não pode usar pipes anônimos para
comunicações através de uma rede.

Para implementar pipes anônimos, use as classes AnonymousPipeServerStream e


AnonymousPipeClientStream.

Exemplo 1
O exemplo a seguir demonstra uma maneira de enviar uma cadeia de caracteres de um
processo pai para um processo filho usando pipes anônimos. Este exemplo cria um
objeto AnonymousPipeServerStream em um processo pai com um valor PipeDirection
de Out. Em seguida, o processo pai cria um processo filho usando um identificador de
cliente para criar um objeto AnonymousPipeClientStream. O processo filho tem um
valor PipeDirection de In.

Em seguida, o processo pai envia uma cadeia de caracteres fornecida pelo usuário ao
processo filho. A cadeia de caracteres é exibida no console do processo filho.

O exemplo a seguir mostra o processo do servidor.

C#

using System;
using System.IO;
using System.IO.Pipes;
using System.Diagnostics;

class PipeServer
{
static void Main()
{
Process pipeClient = new Process();

pipeClient.StartInfo.FileName = "pipeClient.exe";

using (AnonymousPipeServerStream pipeServer =


new AnonymousPipeServerStream(PipeDirection.Out,
HandleInheritability.Inheritable))
{
Console.WriteLine("[SERVER] Current TransmissionMode: {0}.",
pipeServer.TransmissionMode);

// Pass the client process a handle to the server.


pipeClient.StartInfo.Arguments =
pipeServer.GetClientHandleAsString();
pipeClient.StartInfo.UseShellExecute = false;
pipeClient.Start();

pipeServer.DisposeLocalCopyOfClientHandle();

try
{
// Read user input and send that to the client process.
using (StreamWriter sw = new StreamWriter(pipeServer))
{
sw.AutoFlush = true;
// Send a 'sync message' and wait for client to receive
it.
sw.WriteLine("SYNC");
pipeServer.WaitForPipeDrain();
// Send the console input to the client process.
Console.Write("[SERVER] Enter text: ");
sw.WriteLine(Console.ReadLine());
}
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException e)
{
Console.WriteLine("[SERVER] Error: {0}", e.Message);
}
}

pipeClient.WaitForExit();
pipeClient.Close();
Console.WriteLine("[SERVER] Client quit. Server terminating.");
}
}

Exemplo 2
O exemplo a seguir mostra o processo do cliente. O processo do servidor inicia o
processo do cliente e oferece a ele um identificador de cliente. O executável que resulta
do código do cliente deve ser chamado de pipeClient.exe e copiado para o mesmo
diretório que o executável do servidor antes que o processo do servidor seja executado.

C#
using System;
using System.IO;
using System.IO.Pipes;

class PipeClient
{
static void Main(string[] args)
{
if (args.Length > 0)
{
using (PipeStream pipeClient =
new AnonymousPipeClientStream(PipeDirection.In, args[0]))
{
Console.WriteLine("[CLIENT] Current TransmissionMode: {0}.",
pipeClient.TransmissionMode);

using (StreamReader sr = new StreamReader(pipeClient))


{
// Display the read text to the console
string temp;

// Wait for 'sync message' from the server.


do
{
Console.WriteLine("[CLIENT] Wait for sync...");
temp = sr.ReadLine();
}
while (!temp.StartsWith("SYNC"));

// Read the server data and echo to the console.


while ((temp = sr.ReadLine()) != null)
{
Console.WriteLine("[CLIENT] Echo: " + temp);
}
}
}
}
Console.Write("[CLIENT] Press Enter to continue...");
Console.ReadLine();
}
}

Confira também
Pipes
Como: Usar pipes nomeados para comunicação entre processos na rede
Como: Usar pipes nomeados para
comunicação entre processos na rede
Artigo • 09/01/2024

Os pipes nomeados fornecem a comunicação entre processos entre um servidor de pipe


e um ou mais clientes pipe. Eles oferecem mais funcionalidades do que pipes anônimos,
que fornecem comunicação entre processos em um computador local. Pipes nomeados
oferecem suporte à comunicação full-duplex em uma rede e em várias instâncias do
servidor, comunicação por mensagens e representação do cliente, o que permite que
processos de conexão usem seus próprios conjuntos de permissões em servidores
remotos.

) Importante

O .NET no Linux usa soquetes de domínio Unix (UDS) para a implementação dessas
APIs.

Para implementar os pipes nomeados, use as classes NamedPipeServerStream e


NamedPipeClientStream.

Exemplo 1
O exemplo a seguir demonstra como criar um pipe nomeado usando a classe
NamedPipeServerStream. Neste exemplo, o processo do servidor cria quatro threads.
Cada thread pode aceitar uma conexão de cliente. O processo do cliente conectado, em
seguida, fornece um nome de arquivo ao servidor. Se o cliente tiver permissões
suficientes, o processo do servidor abrirá o arquivo e enviará seu conteúdo de volta ao
cliente.

C#

using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;

public class PipeServer


{
private static int numThreads = 4;

public static void Main()


{
int i;
Thread?[] servers = new Thread[numThreads];

Console.WriteLine("\n*** Named pipe server stream with impersonation


example ***\n");
Console.WriteLine("Waiting for client connect...\n");
for (i = 0; i < numThreads; i++)
{
servers[i] = new Thread(ServerThread);
servers[i]?.Start();
}
Thread.Sleep(250);
while (i > 0)
{
for (int j = 0; j < numThreads; j++)
{
if (servers[j] != null)
{
if (servers[j]!.Join(250))
{
Console.WriteLine("Server thread[{0}] finished.",
servers[j]!.ManagedThreadId);
servers[j] = null;
i--; // decrement the thread watch count
}
}
}
}
Console.WriteLine("\nServer threads exhausted, exiting.");
}

private static void ServerThread(object? data)


{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut,
numThreads);

int threadId = Thread.CurrentThread.ManagedThreadId;

// Wait for a client to connect


pipeServer.WaitForConnection();

Console.WriteLine("Client connected on thread[{0}].", threadId);


try
{
// Read the request from the client. Once the client has
// written to the pipe its security token will be available.

StreamString ss = new StreamString(pipeServer);

// Verify our identity to the connected client using a


// string that the client anticipates.

ss.WriteString("I am the one true server!");


string filename = ss.ReadString();

// Read in the contents of the file while impersonating the


client.
ReadFileToStream fileReader = new ReadFileToStream(ss,
filename);

// Display the name of the user we are impersonating.


Console.WriteLine("Reading file: {0} on thread[{1}] as user:
{2}.",
filename, threadId, pipeServer.GetImpersonationUserName());
pipeServer.RunAsClient(fileReader.Start);
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
pipeServer.Close();
}
}

// Defines the data protocol for reading and writing strings on our stream
public class StreamString
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;

public StreamString(Stream ioStream)


{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}

public string ReadString()


{
int len = 0;

len = ioStream.ReadByte() * 256;


len += ioStream.ReadByte();
byte[] inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);

return streamEncoding.GetString(inBuffer);
}

public int WriteString(string outString)


{
byte[] outBuffer = streamEncoding.GetBytes(outString);
int len = outBuffer.Length;
if (len > UInt16.MaxValue)
{
len = (int)UInt16.MaxValue;
}
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();

return outBuffer.Length + 2;
}
}

// Contains the method executed in the context of the impersonated user


public class ReadFileToStream
{
private string fn;
private StreamString ss;

public ReadFileToStream(StreamString str, string filename)


{
fn = filename;
ss = str;
}

public void Start()


{
string contents = File.ReadAllText(fn);
ss.WriteString(contents);
}
}

Exemplo 2
O exemplo a seguir mostra o processo do cliente, que usa a classe
NamedPipeClientStream. O cliente se conecta ao processo do servidor e envia um nome
de arquivo para o servidor. O exemplo usa representação, de forma que a identidade
que está executando o aplicativo do cliente deve ter permissão para acessar o arquivo.
O servidor, então, envia o conteúdo do arquivo de volta ao cliente. O conteúdo do
arquivo é exibido no console.

C#

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Security.Principal;
using System.Text;
using System.Threading;

public class PipeClient


{
private static int numClients = 4;
public static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[0] == "spawnclient")
{
var pipeClient =
new NamedPipeClientStream(".", "testpipe",
PipeDirection.InOut, PipeOptions.None,
TokenImpersonationLevel.Impersonation);

Console.WriteLine("Connecting to server...\n");
pipeClient.Connect();

var ss = new StreamString(pipeClient);


// Validate the server's signature string.
if (ss.ReadString() == "I am the one true server!")
{
// The client security token is sent with the first
write.
// Send the name of the file whose contents are returned
// by the server.
ss.WriteString("c:\\textfile.txt");

// Print the file to the screen.


Console.Write(ss.ReadString());
}
else
{
Console.WriteLine("Server could not be verified.");
}
pipeClient.Close();
// Give the client process some time to display results
before exiting.
Thread.Sleep(4000);
}
}
else
{
Console.WriteLine("\n*** Named pipe client stream with
impersonation example ***\n");
StartClients();
}
}

// Helper function to create pipe client processes


private static void StartClients()
{
string currentProcessName = Environment.CommandLine;

// Remove extra characters when launched from Visual Studio


currentProcessName = currentProcessName.Trim('"', ' ');

currentProcessName = Path.ChangeExtension(currentProcessName,
".exe");
Process?[] plist = new Process?[numClients];

Console.WriteLine("Spawning client processes...\n");

if (currentProcessName.Contains(Environment.CurrentDirectory))
{
currentProcessName =
currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
}

// Remove extra characters when launched from Visual Studio


currentProcessName = currentProcessName.Replace("\\", String.Empty);
currentProcessName = currentProcessName.Replace("\"", String.Empty);

int i;
for (i = 0; i < numClients; i++)
{
// Start 'this' program but spawn a named pipe client.
plist[i] = Process.Start(currentProcessName, "spawnclient");
}
while (i > 0)
{
for (int j = 0; j < numClients; j++)
{
if (plist[j] != null)
{
if (plist[j]!.HasExited)
{
Console.WriteLine($"Client process[{plist[j]?.Id}]
has exited.");
plist[j] = null;
i--; // decrement the process watch count
}
else
{
Thread.Sleep(250);
}
}
}
}
Console.WriteLine("\nClient processes finished, exiting.");
}
}

// Defines the data protocol for reading and writing strings on our stream.
public class StreamString
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;

public StreamString(Stream ioStream)


{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}

public string ReadString()


{
int len;
len = ioStream.ReadByte() * 256;
len += ioStream.ReadByte();
var inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);

return streamEncoding.GetString(inBuffer);
}

public int WriteString(string outString)


{
byte[] outBuffer = streamEncoding.GetBytes(outString);
int len = outBuffer.Length;
if (len > UInt16.MaxValue)
{
len = (int)UInt16.MaxValue;
}
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();

return outBuffer.Length + 2;
}
}

Programação robusta
Os processos de cliente e servidor neste exemplo devem ser executados no mesmo
computador, para que o nome do servidor fornecido para o objeto
NamedPipeClientStream seja "." . Se os processos do cliente e do servidor fossem
realizados em computadores separados, "." seria substituído pelo nome de rede do
computador que executa o processo do servidor.

Confira também
TokenImpersonationLevel
GetImpersonationUserName
Pipes
Como: Usar pipes anônimos para comunicação entre processos locais
6 Colaborar conosco no Comentários do .NET
GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.IO.Pipelines no .NET
Artigo • 09/05/2023

System.IO.Pipelines é uma biblioteca projetada para facilitar a execução de E/S de alto


desempenho no .NET. É uma biblioteca direcionada ao .NET Standard que funciona em
todas as implementações do .NET.

A biblioteca está disponível no pacote Nuget System.IO.Pipelines .

Qual problema resolvido pelo


System.IO.Pipelines
Os aplicativos que analisam dados de streaming são compostos de código clichê com
muitos fluxos de código especializados e incomuns. A clichê e o código de maiúsculas e
minúsculas especiais são complexos e difíceis de manter.

System.IO.Pipelines foi arquitetado para:

Ter dados de streaming de análise de alto desempenho.


Reduzir a complexidade do código.

O código a seguir é típico para um servidor TCP que recebe mensagens delimitadas por
linha (delimitadas por '\n' ) de um cliente:

C#

async Task ProcessLinesAsync(NetworkStream stream)


{
var buffer = new byte[1024];
await stream.ReadAsync(buffer, 0, buffer.Length);

// Process a single line from the buffer


ProcessLine(buffer);
}

O código anterior tem vários problemas:

A mensagem inteira (fim de linha) pode não ser recebida em uma única chamada
para ReadAsync .
Está ignorando o resultado de stream.ReadAsync . stream.ReadAsync retorna
quantos dados foram lidos.
Ele não lida com o caso em que várias linhas são lidas em uma única chamada
ReadAsync .
Ele aloca uma matriz byte com cada leitura.

Para corrigir os problemas anteriores, as seguintes alterações são necessárias:

Buffer dos dados de entrada até que uma nova linha seja encontrada.

Analise todas as linhas retornadas no buffer.

É possível que a linha seja maior que 1 KB (1024 bytes). O código precisa
redimensionar o buffer de entrada até que o delimitador seja encontrado para
ajustar a linha completa dentro do buffer.
Se o buffer for redimensionado, mais cópias de buffer serão feitas à medida que
linhas mais longas aparecerem na entrada.
Para reduzir o espaço desperdiçado, compacte o buffer usado para ler linhas.

Considere usar o pool de buffers para evitar alocar memória repetidamente.

O código a seguir resolve alguns desses problemas:

C#

async Task ProcessLinesAsync(NetworkStream stream)


{
byte[] buffer = ArrayPool<byte>.Shared.Rent(1024);
var bytesBuffered = 0;
var bytesConsumed = 0;

while (true)
{
// Calculate the amount of bytes remaining in the buffer.
var bytesRemaining = buffer.Length - bytesBuffered;

if (bytesRemaining == 0)
{
// Double the buffer size and copy the previously buffered data
into the new buffer.
var newBuffer = ArrayPool<byte>.Shared.Rent(buffer.Length * 2);
Buffer.BlockCopy(buffer, 0, newBuffer, 0, buffer.Length);
// Return the old buffer to the pool.
ArrayPool<byte>.Shared.Return(buffer);
buffer = newBuffer;
bytesRemaining = buffer.Length - bytesBuffered;
}

var bytesRead = await stream.ReadAsync(buffer, bytesBuffered,


bytesRemaining);
if (bytesRead == 0)
{
// EOF
break;
}

// Keep track of the amount of buffered bytes.


bytesBuffered += bytesRead;
var linePosition = -1;

do
{
// Look for a EOL in the buffered data.
linePosition = Array.IndexOf(buffer, (byte)'\n', bytesConsumed,
bytesBuffered - bytesConsumed);

if (linePosition >= 0)
{
// Calculate the length of the line based on the offset.
var lineLength = linePosition - bytesConsumed;

// Process the line.


ProcessLine(buffer, bytesConsumed, lineLength);

// Move the bytesConsumed to skip past the line consumed


(including \n).
bytesConsumed += lineLength + 1;
}
}
while (linePosition >= 0);
}
}

O código anterior é complexo e não resolve todos os problemas identificados. A rede


de alto desempenho geralmente significa escrever código complexo para maximizar o
desempenho. System.IO.Pipelines foi projetado para facilitar a gravação desse tipo de
código.

Pipe
A classe Pipe pode ser usada para criar um par PipeWriter/PipeReader . Todos os dados
gravados no PipeWriter estão disponíveis no PipeReader :

C#

var pipe = new Pipe();


PipeReader reader = pipe.Reader;
PipeWriter writer = pipe.Writer;
Uso básico do pipe
C#

async Task ProcessLinesAsync(Socket socket)


{
var pipe = new Pipe();
Task writing = FillPipeAsync(socket, pipe.Writer);
Task reading = ReadPipeAsync(pipe.Reader);

await Task.WhenAll(reading, writing);


}

async Task FillPipeAsync(Socket socket, PipeWriter writer)


{
const int minimumBufferSize = 512;

while (true)
{
// Allocate at least 512 bytes from the PipeWriter.
Memory<byte> memory = writer.GetMemory(minimumBufferSize);
try
{
int bytesRead = await socket.ReceiveAsync(memory,
SocketFlags.None);
if (bytesRead == 0)
{
break;
}
// Tell the PipeWriter how much was read from the Socket.
writer.Advance(bytesRead);
}
catch (Exception ex)
{
LogError(ex);
break;
}

// Make the data available to the PipeReader.


FlushResult result = await writer.FlushAsync();

if (result.IsCompleted)
{
break;
}
}

// By completing PipeWriter, tell the PipeReader that there's no more


data coming.
await writer.CompleteAsync();
}

async Task ReadPipeAsync(PipeReader reader)


{
while (true)
{
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;

while (TryReadLine(ref buffer, out ReadOnlySequence<byte> line))


{
// Process the line.
ProcessLine(line);
}

// Tell the PipeReader how much of the buffer has been consumed.
reader.AdvanceTo(buffer.Start, buffer.End);

// Stop reading if there's no more data coming.


if (result.IsCompleted)
{
break;
}
}

// Mark the PipeReader as complete.


await reader.CompleteAsync();
}

bool TryReadLine(ref ReadOnlySequence<byte> buffer, out


ReadOnlySequence<byte> line)
{
// Look for a EOL in the buffer.
SequencePosition? position = buffer.PositionOf((byte)'\n');

if (position == null)
{
line = default;
return false;
}

// Skip the line + the \n.


line = buffer.Slice(0, position.Value);
buffer = buffer.Slice(buffer.GetPosition(1, position.Value));
return true;
}

Há dois loops:

FillPipeAsync lê de Socket e grava para PipeWriter .

ReadPipeAsync lê de PipeReader e analisa as linhas de chegada.

Não há buffers explícitos alocados. Todo o gerenciamento de buffers é delegado às


implementações PipeReader e PipeWriter . Delegar o gerenciamento de buffer facilita o
consumo de código para se concentrar apenas na lógica de negócios.
No primeiro loop:

PipeWriter.GetMemory(Int32) é chamado para obter memória do gravador


subjacente.
PipeWriter.Advance(Int32) é chamado para informar quantos dados PipeWriter
foram gravados no buffer.
PipeWriter.FlushAsync é chamado para disponibilizar os dados para o PipeReader .

No segundo loop, PipeReader consome os buffers gravados por PipeWriter . Os buffers


vêm do soquete. A chamada para PipeReader.ReadAsync :

Retorna ReadResult que contém duas informações importantes:


Os dados que foram lidos na forma de ReadOnlySequence<byte> .
Um booleano IsCompleted que indica se o fim dos dados (EOF) foi atingido.

Depois de encontrar o delimitador de fim de linha (EOL) e analisar a linha:

A lógica processa o buffer para ignorar o que já está processado.


PipeReader.AdvanceTo é chamado para informar a PipeReader quantos dados
foram consumidos e examinados.

Os loops de leitor e gravador terminam chamando Complete . Complete permite que o


Pipe subjacente libere a memória alocada.

Backpressure e controle de fluxo


O ideal é que a leitura e a análise trabalhem juntas:

O thread de leitura consome dados da rede e os coloca em buffers.


O thread de análise é responsável por construir as estruturas de dados
apropriadas.

Normalmente, a análise leva mais tempo do que apenas copiar blocos de dados da
rede:

O thread de leitura fica à frente do thread de análise.


O thread de leitura precisa diminuir ou alocar mais memória para armazenar os
dados para o thread de análise.

Para um desempenho ideal, há um equilíbrio entre pausas frequentes e alocação de


mais memória.

Para resolver o problema anterior, Pipe tem duas configurações para controlar o fluxo
de dados:
PauseWriterThreshold: determina quantos dados devem ser armazenados em
buffer antes das chamadas para FlushAsync pausar.
ResumeWriterThreshold: determina a quantidade de dados que o leitor deve
observar antes que as chamadas para PipeWriter.FlushAsync sejam retomadas.

PipeWriter.FlushAsync:

Retorna um ValueTask<FlushResult> incompleto quando a quantidade de dados


em Pipe cruza PauseWriterThreshold .
ValueTask<FlushResult> é concluído quando ele se torna menor que
ResumeWriterThreshold .

Dois valores são usados para evitar o ciclismo rápido, que pode ocorrer se um valor for
usado.

Exemplos
C#

// The Pipe will start returning incomplete tasks from FlushAsync until
// the reader examines at least 5 bytes.
var options = new PipeOptions(pauseWriterThreshold: 10,
resumeWriterThreshold: 5);
var pipe = new Pipe(options);

PipeScheduler
Normalmente, ao usar async e await , o código assíncrono é retomado em
TaskScheduler ou no código atual SynchronizationContext.

Ao fazer E/S, é importante ter controle refinado sobre onde ela é executada. Esse
controle permite aproveitar os caches de CPU com eficiência. O cache eficiente é
essencial para aplicativos de alto desempenho, como servidores Web. PipeScheduler
fornece controle sobre onde os retornos de chamada assíncronos são executados. Por
padrão:
A corrente SynchronizationContext é usada.
Se não houver SynchronizationContext , ele usará o pool de encadeamentos para
executar retornos de chamada.

C#

public static void Main(string[] args)


{
var writeScheduler = new SingleThreadPipeScheduler();
var readScheduler = new SingleThreadPipeScheduler();

// Tell the Pipe what schedulers to use and disable the


SynchronizationContext.
var options = new PipeOptions(readerScheduler: readScheduler,
writerScheduler: writeScheduler,
useSynchronizationContext: false);
var pipe = new Pipe(options);
}

// This is a sample scheduler that async callbacks on a single dedicated


thread.
public class SingleThreadPipeScheduler : PipeScheduler
{
private readonly BlockingCollection<(Action<object> Action, object
State)> _queue =
new BlockingCollection<(Action<object> Action, object State)>();
private readonly Thread _thread;

public SingleThreadPipeScheduler()
{
_thread = new Thread(DoWork);
_thread.Start();
}

private void DoWork()


{
foreach (var item in _queue.GetConsumingEnumerable())
{
item.Action(item.State);
}
}

public override void Schedule(Action<object?> action, object? state)


{
if (state is not null)
{
_queue.Add((action, state));
}
// else log the fact that _queue.Add was not called.
}
}
PipeScheduler.ThreadPool é a implementação PipeScheduler que enfileira retornos de
chamada para o pool de encadeamentos. PipeScheduler.ThreadPool é o padrão e
geralmente a melhor opção. PipeScheduler.Inline pode causar consequências não
intencionais, como deadlocks.

Redefinição de pipe
É frequentemente eficiente reutilizar o objeto Pipe . Para redefinir o pipe, chame
PipeReaderReset quando PipeReader e PipeWriter estiverem concluídos.

PipeReader
PipeReader gerencia a memória em nome do chamador. Sempre chame
PipeReader.AdvanceTo após chamar PipeReader.ReadAsync. Isso informa PipeReader
quando o chamador é feito com a memória para que ele possa ser rastreado. O
ReadOnlySequence<byte> retornado de PipeReader.ReadAsync só é válido até a chamada
de PipeReader.AdvanceTo . É ilegal usar ReadOnlySequence<byte> após chamar
PipeReader.AdvanceTo .

PipeReader.AdvanceTo usa dois argumentos SequencePosition:

O primeiro argumento determina a quantidade de memória consumida.


O segundo argumento determina quanto do buffer foi observado.

Marcar dados como consumidos significa que o pipe pode retornar a memória para o
pool de buffers subjacente. Marcar dados como observado controla o que fará a
próxima chamada para PipeReader.ReadAsync . Marcar tudo como observado significa
que a próxima chamada para PipeReader.ReadAsync não retornará até que haja mais
dados gravados no pipe. Qualquer outro valor fará a próxima chamada para
PipeReader.ReadAsync retornar imediatamente com os dados observados e não

observados, mas não os dados que já foram consumidos.

Ler cenários de dados de streaming


Há alguns padrões típicos que surgem ao tentar ler dados de streaming:

Dado um fluxo de dados, analise uma única mensagem.


Dado um fluxo de dados, analise uma única mensagem.

Os exemplos a seguir usam o método TryParseLines para analisar mensagens de um


ReadOnlySequence<byte> . TryParseLines analisa uma única mensagem e atualiza o buffer
de entrada para cortar a mensagem analisada do buffer. TryParseLines não faz parte do
.NET, é um método escrito pelo usuário usado nas seções a seguir.

C#

bool TryParseLines(ref ReadOnlySequence<byte> buffer, out Message message);

Lê uma única mensagem


O código a seguir lê uma única mensagem de PipeReader e a retorna ao chamador.

C#

async ValueTask<Message?> ReadSingleMessageAsync(PipeReader reader,


CancellationToken cancellationToken = default)
{
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> buffer = result.Buffer;

// In the event that no message is parsed successfully, mark


consumed
// as nothing and examined as the entire buffer.
SequencePosition consumed = buffer.Start;
SequencePosition examined = buffer.End;

try
{
if (TryParseLines(ref buffer, out Message message))
{
// A single message was successfully parsed so mark the
start of the
// parsed buffer as consumed. TryParseLines trims the buffer
to
// point to the data after the message was parsed.
consumed = buffer.Start;

// Examined is marked the same as consumed here, so the next


call
// to ReadSingleMessageAsync will process the next message
if there's
// one.
examined = consumed;

return message;
}

// There's no more data to be processed.


if (result.IsCompleted)
{
if (buffer.Length > 0)
{
// The message is incomplete and there's no more data to
process.
throw new InvalidDataException("Incomplete message.");
}

break;
}
}
finally
{
reader.AdvanceTo(consumed, examined);
}
}

return null;
}

O código anterior:

Analisa uma única mensagem.


Atualizações o SequencePosition consumido e SequencePosition examinado para
apontar para o início do buffer de entrada cortado.

Os dois argumentos SequencePosition são atualizados porque TryParseLines remove a


mensagem analisada do buffer de entrada. Em geral, ao analisar uma única mensagem
do buffer, a posição examinada deve ser uma das seguintes:

O final da mensagem.
O final do buffer recebido se nenhuma mensagem foi encontrada.

O caso de mensagem única tem o maior potencial de erros. Passar os valores errados
para examinados pode resultar em uma exceção de memória insuficiente ou um loop
infinito. Para obter mais informações, consulte a seção Problemas comuns do
PipeReader neste artigo.

Lendo várias mensagens


O código a seguir lê todas as mensagens de um PipeReader e chama
ProcessMessageAsync em cada um.

C#

async Task ProcessMessagesAsync(PipeReader reader, CancellationToken


cancellationToken = default)
{
try
{
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> buffer = result.Buffer;

try
{
// Process all messages from the buffer, modifying the input
buffer on each
// iteration.
while (TryParseLines(ref buffer, out Message message))
{
await ProcessMessageAsync(message);
}

// There's no more data to be processed.


if (result.IsCompleted)
{
if (buffer.Length > 0)
{
// The message is incomplete and there's no more
data to process.
throw new InvalidDataException("Incomplete
message.");
}
break;
}
}
finally
{
// Since all messages in the buffer are being processed, you
can use the
// remaining buffer's Start and End position to determine
consumed and examined.
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
finally
{
await reader.CompleteAsync();
}
}

Cancelamento
PipeReader.ReadAsync :

Dá suporte à passagem de CancellationToken.


Gerará OperationCanceledException se CancellationToken estiver cancelado
enquanto houver uma leitura pendente.
Dá suporte a uma maneira de cancelar a operação de leitura atual por meio de
PipeReader.CancelPendingRead, o que evita a criação de uma exceção. A chamada
PipeReader.CancelPendingRead faz com que a chamada atual ou próxima para

PipeReader.ReadAsync retorne ReadResult com IsCanceled definido como true .

Isso pode ser útil para interromper o loop de leitura existente de forma não
destrutiva e não excepcional.

C#

private PipeReader reader;

public MyConnection(PipeReader reader)


{
this.reader = reader;
}

public void Abort()


{
// Cancel the pending read so the process loop ends without an
exception.
reader.CancelPendingRead();
}

public async Task ProcessMessagesAsync()


{
try
{
while (true)
{
ReadResult result = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = result.Buffer;

try
{
if (result.IsCanceled)
{
// The read was canceled. You can quit without reading
the existing data.
break;
}

// Process all messages from the buffer, modifying the input


buffer on each
// iteration.
while (TryParseLines(ref buffer, out Message message))
{
await ProcessMessageAsync(message);
}
// There's no more data to be processed.
if (result.IsCompleted)
{
break;
}
}
finally
{
// Since all messages in the buffer are being processed, you
can use the
// remaining buffer's Start and End position to determine
consumed and examined.
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
finally
{
await reader.CompleteAsync();
}
}

Problemas comuns do PipeReader


Passar os valores errados para consumed ou examined pode resultar na leitura de
dados já lidos.

Passar buffer.End conforme examinado pode resultar em:


Dados paralisados
Possivelmente uma eventual exceção de OOM (Memória Insuficiente) se os
dados não forem consumidos. Por exemplo, PipeReader.AdvanceTo(position,
buffer.End) ao processar uma única mensagem por vez do buffer.

Passar os valores errados para consumed ou examined pode resultar em um loop


infinito. Por exemplo, PipeReader.AdvanceTo(buffer.Start) se buffer.Start não
tiver sido alterado fará com que a próxima chamada PipeReader.ReadAsync retorne
imediatamente antes da chegada de novos dados.

Passar os valores errados para consumed ou examined pode resultar em um


buffering infinito (eventual OOM).

Usar o ReadOnlySequence<byte> após chamar PipeReader.AdvanceTo pode resultar


em memória corrompida (use depois de liberar).

A falha na chamada PipeReader.Complete/CompleteAsync pode resultar em um


vazamento de memória.
Verificar ReadResult.IsCompleted e sair da lógica de leitura antes de processar o
buffer resulta em perda de dados. A condição de saída do loop deve ser baseada
em ReadResult.Buffer.IsEmpty e ReadResult.IsCompleted . Fazer isso
incorretamente pode resultar em um loop infinito.

Código problemático

❌Perda de dados
O ReadResult pode retornar o segmento final de dados quando IsCompleted é definido
como true . Não ler esses dados antes de sair do loop de leitura resultará em perda de
dados.

2 Aviso

NÃO use o código a seguir. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo a
seguir é fornecido para explicar Problemas comuns do PipeReader.

C#

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> dataLossBuffer = result.Buffer;

if (result.IsCompleted)
break;

Process(ref dataLossBuffer, out Message message);

reader.AdvanceTo(dataLossBuffer.Start, dataLossBuffer.End);
}

2 Aviso

NÃO use o código anterior. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo
anterior é fornecido para explicar Problemas comuns do PipeReader.

❌Loop infinito
A lógica a seguir pode resultar em um loop infinito se o Result.IsCompleted é true ,
mas nunca há uma mensagem completa no buffer.

2 Aviso

NÃO use o código a seguir. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo a
seguir é fornecido para explicar Problemas comuns do PipeReader.

C#

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> infiniteLoopBuffer = result.Buffer;
if (result.IsCompleted && infiniteLoopBuffer.IsEmpty)
break;

Process(ref infiniteLoopBuffer, out Message message);

reader.AdvanceTo(infiniteLoopBuffer.Start, infiniteLoopBuffer.End);
}

2 Aviso

NÃO use o código anterior. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo
anterior é fornecido para explicar Problemas comuns do PipeReader.

Aqui está outro código com o mesmo problema. Ele está verificando se há um buffer
não vazio antes de verificar ReadResult.IsCompleted . Como ele está em um else if , ele
ficará em loop para sempre se nunca houver uma mensagem completa no buffer.

2 Aviso

NÃO use o código a seguir. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo a
seguir é fornecido para explicar Problemas comuns do PipeReader.

C#
Environment.FailFast("This code is terrible, don't use it!");
while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> infiniteLoopBuffer = result.Buffer;

if (!infiniteLoopBuffer.IsEmpty)
Process(ref infiniteLoopBuffer, out Message message);

else if (result.IsCompleted)
break;

reader.AdvanceTo(infiniteLoopBuffer.Start, infiniteLoopBuffer.End);
}

2 Aviso

NÃO use o código anterior. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo
anterior é fornecido para explicar Problemas comuns do PipeReader.

❌Aplicativo sem resposta

Chamar incondicionalmente PipeReader.AdvanceTo com buffer.End na posição


examined pode fazer com que o aplicativo não responda ao analisar uma única

mensagem. A próxima chamada para PipeReader.AdvanceTo não retornará até:

Há mais dados gravados no pipe.


E os novos dados não foram examinados anteriormente.

2 Aviso

NÃO use o código a seguir. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo a
seguir é fornecido para explicar Problemas comuns do PipeReader.

C#

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> hangBuffer = result.Buffer;

Process(ref hangBuffer, out Message message);


if (result.IsCompleted)
break;

reader.AdvanceTo(hangBuffer.Start, hangBuffer.End);

if (message != null)
return message;
}

2 Aviso

NÃO use o código anterior. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo
anterior é fornecido para explicar Problemas comuns do PipeReader.

❌Memória insuficiente (OOM)

Com as seguintes condições, o código a seguir mantém o buffer até que um


OutOfMemoryException ocorra:

Não há tamanho máximo de mensagem.


Os dados retornados de PipeReader não fazem uma mensagem completa. Por
exemplo, ele não faz uma mensagem completa porque o outro lado está
escrevendo uma mensagem grande (por exemplo, uma mensagem de 4 GB).

2 Aviso

NÃO use o código a seguir. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo a
seguir é fornecido para explicar Problemas comuns do PipeReader.

C#

Environment.FailFast("This code is terrible, don't use it!");


while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> thisCouldOutOfMemory = result.Buffer;

Process(ref thisCouldOutOfMemory, out Message message);

if (result.IsCompleted)
break;

reader.AdvanceTo(thisCouldOutOfMemory.Start, thisCouldOutOfMemory.End);
if (message != null)
return message;
}

2 Aviso

NÃO use o código anterior. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo
anterior é fornecido para explicar Problemas comuns do PipeReader.

❌Memória corrompida

Ao escrever auxiliares que lêem o buffer, qualquer carga retornada deve ser copiada
antes de chamar Advance . O exemplo a seguir retornará a memória que Pipe descartou
e poderá reutilizá-la para a próxima operação (leitura/gravação).

2 Aviso

NÃO use o código a seguir. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo a
seguir é fornecido para explicar Problemas comuns do PipeReader.

C#

public class Message


{
public ReadOnlySequence<byte> CorruptedPayload { get; set; }
}

C#

Environment.FailFast("This code is terrible, don't use it!");


Message message = null;

while (true)
{
ReadResult result = await reader.ReadAsync(cancellationToken);
ReadOnlySequence<byte> buffer = result.Buffer;

ReadHeader(ref buffer, out int length);

if (length <= buffer.Length)


{
message = new Message
{
// Slice the payload from the existing buffer
CorruptedPayload = buffer.Slice(0, length)
};

buffer = buffer.Slice(length);
}

if (result.IsCompleted)
break;

reader.AdvanceTo(buffer.Start, buffer.End);

if (message != null)
{
// This code is broken since reader.AdvanceTo() was called with
a position *after* the buffer
// was captured.
break;
}
}

return message;
}

2 Aviso

NÃO use o código anterior. O uso desse exemplo resultará em perda de dados,
travamentos, problemas de segurança e NÃO deve ser copiado. O exemplo
anterior é fornecido para explicar Problemas comuns do PipeReader.

PipeWriter
O PipeWriter gerencia buffers para gravação em nome do chamador. PipeWriter
implementa IBufferWriter<byte>. IBufferWriter<byte> possibilita obter acesso a buffers
para executar gravações sem cópias de buffer extras.

C#

async Task WriteHelloAsync(PipeWriter writer, CancellationToken


cancellationToken = default)
{
// Request at least 5 bytes from the PipeWriter.
Memory<byte> memory = writer.GetMemory(5);

// Write directly into the buffer.


int written = Encoding.ASCII.GetBytes("Hello".AsSpan(), memory.Span);
// Tell the writer how many bytes were written.
writer.Advance(written);

await writer.FlushAsync(cancellationToken);
}

O código anterior:

Solicita um buffer de pelo menos 5 bytes do PipeWriter usando GetMemory.


Grava bytes para a cadeia de caracteres ASCII "Hello" no Memory<byte> retornado.
Chamadas Advance para indicar quantos bytes foram gravados no buffer.
Libera o PipeWriter , que envia os bytes para o dispositivo subjacente.

O método anterior de gravação usa os buffers fornecidos pelo PipeWriter . Ele também
poderia ter usado PipeWriter.WriteAsync, o que:

Copia o buffer existente para PipeWriter .


Chamadas GetSpan , Advance conforme apropriado e chamadas FlushAsync.

C#

async Task WriteHelloAsync(PipeWriter writer, CancellationToken


cancellationToken = default)
{
byte[] helloBytes = Encoding.ASCII.GetBytes("Hello");

// Write helloBytes to the writer, there's no need to call Advance here


// (Write does that).
await writer.WriteAsync(helloBytes, cancellationToken);
}

Cancelamento
FlushAsync dá suporte à passagem de CancellationToken. Passar CancellationToken
resulta em OperationCanceledException se o token for cancelado enquanto houver um
flush pendente. PipeWriter.FlushAsync oferece suporte a uma maneira de cancelar a
operação de liberação atual por meio de PipeWriter.CancelPendingFlush sem gerar uma
exceção. A chamada PipeWriter.CancelPendingFlush faz com que a chamada atual ou
próxima para PipeWriter.FlushAsync ou PipeWriter.WriteAsync retorne FlushResult
com IsCanceled definido como true . Isso pode ser útil para interromper a liberação de
rendimento de maneira não destrutiva e não excepcional.

Problemas comuns do PipeWriter


GetSpan e GetMemory retornam um buffer com pelo menos a quantidade de
memória solicitada. Não suponha tamanhos exatos do buffer.
Não há garantia de que chamadas sucessivas retornarão o mesmo buffer ou o
mesmo tamanho de buffer.
Um novo buffer deve ser solicitado após a chamada Advance para continuar
gravando mais dados. O buffer adquirido anteriormente não pode ser gravado.
Chamar GetMemory ou GetSpan enquanto houver uma chamada para FlushAsync
incompleta não é seguro.
Chamar Complete ou CompleteAsync , embora haja dados não corrompidos, pode
resultar em memória corrompida.

Dicas para usar PipeReader e PipeWriter


As dicas a seguir ajudarão você a usar as classes System.IO.Pipelines com êxito:

Preencha sempre o PipeReader e o PipeWriter, incluindo uma exceção, quando


aplicável.
Sempre chame PipeReader.AdvanceTo após chamar PipeReader.ReadAsync.
Periodicamente await PipeWriter.FlushAsync enquanto grava e sempre verifique
FlushResult.IsCompleted. Abortar a gravação se IsCompleted for true , pois isso
indica que o leitor está preenchido e não se importa mais com o que está gravado.
Chame PipeWriter.FlushAsync depois de escrever algo que você deseja que o
PipeReader tenha acesso.

Não chame FlushAsync se o leitor não puder iniciar até que FlushAsync termine,
pois isso pode causar um impasse.
Verifique se apenas um contexto "possui" PipeReader ou PipeWriter ou acessa-os.
Esses tipos não são thread-safe.
Nunca acesse um ReadResult.Buffer após a chamada AdvanceTo ou a conclusão de
PipeReader .

IDuplexPipe
IDuplexPipe é um contrato para tipos que dão suporte à leitura e à gravação. Por
exemplo, uma conexão de rede seria representada por IDuplexPipe .

Ao contrário de Pipe , que contém PipeReader e PipeWriter , IDuplexPipe representa


um único lado de uma conexão full duplex. Isso significa que o que está escrito em
PipeWriter não será lido no PipeReader .
Fluxos
Ao ler ou gravar dados de fluxo, você normalmente lê dados usando um desserializador
e grava dados usando um serializador. A maioria dessas APIs de fluxo de leitura e
gravação tem um parâmetro Stream . Para facilitar a integração com essas APIs
existentes, PipeReader e PipeWriter expõem um método AsStream. AsStream retorna
uma implementação Stream em torno de PipeReader ou PipeWriter .

Exemplo de fluxo
As instâncias PipeReader e PipeWriter podem ser criadas usando os métodos estáticos
Create com um objeto Stream e opções de criação correspondentes opcionais.

StreamPipeReaderOptions permite o controle sobre a criação da instância PipeReader


com os seguintes parâmetros:

StreamPipeReaderOptions.BufferSize é o tamanho mínimo do buffer em bytes


usado ao alugar memória do pool e o padrão é 4096 .
O sinalizador StreamPipeReaderOptions.LeaveOpen determina se o fluxo
subjacente é deixado aberto após a conclusão de PipeReader e o padrão é false .
StreamPipeReaderOptions.MinimumReadSize representa o limite de bytes
restantes no buffer antes que um novo buffer seja alocado e o padrão é 1024 .
StreamPipeReaderOptions.Pool é o MemoryPool<byte> usado ao alocar memória e o
padrão é null .

StreamPipeWriterOptions permite o controle sobre a criação da instância PipeWriter


com os seguintes parâmetros:

O sinalizador StreamPipeWriterOptions.LeaveOpen determina se o fluxo


subjacente é deixado aberto após a conclusão de PipeWriter e o padrão é false .
StreamPipeWriterOptions.MinimumBufferSize representa o tamanho mínimo do
buffer a ser usado ao alugar memória do Pool e o padrão é 4096 .
StreamPipeWriterOptions.Pool é o MemoryPool<byte> usado ao alocar memória e o
padrão é null .

) Importante

Ao criar instâncias PipeReader e PipeWriter usando os métodos Create , você


precisa considerar a vida útil do objeto Stream . Se você precisar de acesso ao fluxo
depois que o leitor ou gravador terminar, você precisará definir o sinalizador
LeaveOpen como true nas opções de criação. Caso contrário, o fluxo será fechado.

O código a seguir demonstra a criação de instâncias PipeReader e PipeWriter usando


os métodos Create de um fluxo.

C#

using System.Buffers;
using System.IO.Pipelines;
using System.Text;

class Program
{
static async Task Main()
{
using var stream = File.OpenRead("lorem-ipsum.txt");

var reader = PipeReader.Create(stream);


var writer = PipeWriter.Create(
Console.OpenStandardOutput(),
new StreamPipeWriterOptions(leaveOpen: true));

WriteUserCancellationPrompt();

var processMessagesTask = ProcessMessagesAsync(reader, writer);


var userCanceled = false;
var cancelProcessingTask = Task.Run(() =>
{
while (char.ToUpperInvariant(Console.ReadKey().KeyChar) != 'C')
{
WriteUserCancellationPrompt();
}

userCanceled = true;

// No exceptions thrown
reader.CancelPendingRead();
writer.CancelPendingFlush();
});

await Task.WhenAny(cancelProcessingTask, processMessagesTask);

Console.WriteLine(
$"\n\nProcessing {(userCanceled ? "cancelled" :
"completed")}.\n");
}

static void WriteUserCancellationPrompt() =>


Console.WriteLine("Press 'C' to cancel processing...\n");

static async Task ProcessMessagesAsync(


PipeReader reader,
PipeWriter writer)
{
try
{
while (true)
{
ReadResult readResult = await reader.ReadAsync();
ReadOnlySequence<byte> buffer = readResult.Buffer;

try
{
if (readResult.IsCanceled)
{
break;
}

if (TryParseLines(ref buffer, out string message))


{
FlushResult flushResult =
await WriteMessagesAsync(writer, message);

if (flushResult.IsCanceled ||
flushResult.IsCompleted)
{
break;
}
}

if (readResult.IsCompleted)
{
if (!buffer.IsEmpty)
{
throw new InvalidDataException("Incomplete
message.");
}
break;
}
}
finally
{
reader.AdvanceTo(buffer.Start, buffer.End);
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex);
}
finally
{
await reader.CompleteAsync();
await writer.CompleteAsync();
}
}
static bool TryParseLines(
ref ReadOnlySequence<byte> buffer,
out string message)
{
SequencePosition? position;
StringBuilder outputMessage = new();

while(true)
{
position = buffer.PositionOf((byte)'\n');

if (!position.HasValue)
break;

outputMessage.Append(Encoding.ASCII.GetString(buffer.Slice(buffer.Start,
position.Value)))
.AppendLine();

buffer = buffer.Slice(buffer.GetPosition(1, position.Value));


};

message = outputMessage.ToString();
return message.Length != 0;
}

static ValueTask<FlushResult> WriteMessagesAsync(


PipeWriter writer,
string message) =>
writer.WriteAsync(Encoding.ASCII.GetBytes(message));
}

O aplicativo usa StreamReader para ler o arquivo lorem-ipsum.txt como um fluxo e deve
terminar com uma linha em branco. O FileStream é passado para PipeReader.Create, o
que instancia um objeto PipeReader . Em seguida, o aplicativo de console passa seu
fluxo de saída padrão para PipeWriter.Create usando Console.OpenStandardOutput(). O
exemplo dá suporte ao cancelamento.
Trabalhar com Buffers no .NET
Artigo • 09/05/2023

Este artigo fornece uma visão geral dos tipos que ajudam a ler dados executados em
vários buffers. Eles são usados principalmente para dar suporte a objetos PipeReader.

IBufferWriter<T>
System.Buffers.IBufferWriter<T> é um contrato para gravação em buffer síncrono. No
nível mais baixo, a interface:

É básico e não é difícil de usar.


Permite o acesso a um Memory<T> ou Span<T>. O Memory<T> ou Span<T> pode
ser gravado e você pode determinar quantos itens T foram gravados.

C#

void WriteHello(IBufferWriter<byte> writer)


{
// Request at least 5 bytes.
Span<byte> span = writer.GetSpan(5);
ReadOnlySpan<char> helloSpan = "Hello".AsSpan();
int written = Encoding.ASCII.GetBytes(helloSpan, span);

// Tell the writer how many bytes were written.


writer.Advance(written);
}

O método anterior:

Solicita um buffer de pelo menos 5 bytes do IBufferWriter<byte> usando


GetSpan(5) .

Grava bytes para a cadeia de caracteres ASCII "Hello" no Span<byte> retornado.


Chama IBufferWriter<T> para indicar quantos bytes foram gravados no buffer.

Este método de gravação usa o buffer Memory<T> / Span<T> fornecido pelo


IBufferWriter<T> . Como alternativa, o método de extensão Write pode ser usado para
copiar um buffer existente para o IBufferWriter<T> . Write faz o trabalho de chamar
GetSpan / Advance conforme apropriado, portanto, não é necessário chamar Advance

após a gravação:

C#
void WriteHello(IBufferWriter<byte> writer)
{
byte[] helloBytes = Encoding.ASCII.GetBytes("Hello");

// Write helloBytes to the writer. There's no need to call Advance here


// since Write calls Advance.
writer.Write(helloBytes);
}

ArrayBufferWriter<T> é uma implementação de IBufferWriter<T> cujo repositório de


backup é uma única matriz contígua.

Problemas comuns do IBufferWriter


GetSpan e GetMemory retornam um buffer com pelo menos a quantidade de

memória solicitada. Não suponha tamanhos de buffer exatos.


Não há garantia de que chamadas sucessivas retornarão o mesmo buffer ou o
mesmo tamanho de buffer.
Um novo buffer deve ser solicitado após chamar Advance para continuar gravando
mais dados. Um buffer adquirido anteriormente não pode ser gravado após
Advance ter sido chamado.

ReadOnlySequence<T>

ReadOnlySequence<T> é um struct que pode representar uma sequência contígua ou


não contígua de T . Ele pode ser construído a partir de:

1. Um T[]
2. Um ReadOnlyMemory<T>
3. Um par de nó de lista vinculada ReadOnlySequenceSegment<T> e índice para
representar a posição inicial e final da sequência.
A terceira representação é a mais interessante, pois tem implicações de desempenho
em várias operações no ReadOnlySequence<T> :

Representação Operação Complexidade

T[] / ReadOnlyMemory<T> Length O(1)

T[] / ReadOnlyMemory<T> GetPosition(long) O(1)

T[] / ReadOnlyMemory<T> Slice(int, int) O(1)

T[] / ReadOnlyMemory<T> Slice(SequencePosition, O(1)


SequencePosition)

ReadOnlySequenceSegment<T> Length O(1)

ReadOnlySequenceSegment<T> GetPosition(long) O(number of


segments)

ReadOnlySequenceSegment<T> Slice(int, int) O(number of


segments)

ReadOnlySequenceSegment<T> Slice(SequencePosition, O(1)


SequencePosition)

Devido a essa representação mista, o ReadOnlySequence<T> expõe os índices como


SequencePosition em vez de um número inteiro. Um SequencePosition :

É um valor opaco que representa um índice em ReadOnlySequence<T> onde se


originou.
Consiste em duas partes, um inteiro e um objeto. O que esses dois valores
representam estão vinculados à implementação de ReadOnlySequence<T> .

Acessar dados
O ReadOnlySequence<T> expõe dados como um enumerável de ReadOnlyMemory<T> . A
enumeração de cada um dos segmentos pode ser feita usando um foreach básico:

C#

long FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)


{
long position = 0;

foreach (ReadOnlyMemory<byte> segment in buffer)


{
ReadOnlySpan<byte> span = segment.Span;
var index = span.IndexOf(data);
if (index != -1)
{
return position + index;
}

position += span.Length;
}

return -1;
}

O método anterior pesquisa cada segmento por um byte específico. Se você precisar
acompanhar o SequencePosition de cada segmento, ReadOnlySequence<T>.TryGet é
mais apropriado. O próximo exemplo altera o código anterior para retornar um
SequencePosition em vez de um número inteiro. Retornar um SequencePosition tem o
benefício de permitir que o chamador evite uma segunda verificação para obter os
dados em um índice específico.

C#

SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)


{
SequencePosition position = buffer.Start;
SequencePosition result = position;

while (buffer.TryGet(ref position, out ReadOnlyMemory<byte> segment))


{
ReadOnlySpan<byte> span = segment.Span;
var index = span.IndexOf(data);
if (index != -1)
{
return buffer.GetPosition(index, result);
}

result = position;
}
return null;
}

A combinação de SequencePosition e TryGet atua como um enumerador. O campo de


posição é modificado no início de cada iteração para ser o início de cada segmento
dentro do ReadOnlySequence<T> .

O método anterior existe como um método de extensão em ReadOnlySequence<T> .


PositionOf pode ser usado para simplificar o código anterior:

C#
SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)
=> buffer.PositionOf(data);

Processar um ReadOnlySequence<T>

Processar um ReadOnlySequence<T> pode ser desafiador, pois os dados podem ser


divididos em vários segmentos dentro da sequência. Para obter o melhor desempenho,
divida o código em dois caminhos:

Um caminho rápido que lida com o caso de segmento único.


Um caminho lento que lida com os dados divididos entre segmentos.

Há algumas abordagens que podem ser usadas para processar dados em sequências
multissegmentadas:

Use a SequenceReader<T>.
Analisar segmento de dados por segmento, acompanhando o SequencePosition e
o índice dentro do segmento analisado. Isso evita alocações desnecessárias, mas
pode ser ineficiente, especialmente para buffers pequenos.
Copie o ReadOnlySequence<T> para uma matriz contígua e trate-o como um único
buffer:
Se o tamanho do ReadOnlySequence<T> for pequeno, pode ser razoável copiar os
dados em um buffer alocado em pilha usando o operador stackalloc.
Copie o ReadOnlySequence<T> em uma matriz em pool usando
ArrayPool<T>.Shared.
Use ReadOnlySequence<T>.ToArray(). Isso não é recomendado em caminhos
críticos, pois aloca um novo T[] no heap.

Os exemplos a seguir demonstram alguns casos comuns para processar


ReadOnlySequence<byte> :

Processar dados binários

O exemplo a seguir analisa o comprimento de um inteiro big-endian de 4 bytes desde o


início de ReadOnlySequence<byte> .

C#

bool TryParseHeaderLength(ref ReadOnlySequence<byte> buffer, out int length)


{
// If there's not enough space, the length can't be obtained.
if (buffer.Length < 4)
{
length = 0;
return false;
}

// Grab the first 4 bytes of the buffer.


var lengthSlice = buffer.Slice(buffer.Start, 4);
if (lengthSlice.IsSingleSegment)
{
// Fast path since it's a single segment.
length =
BinaryPrimitives.ReadInt32BigEndian(lengthSlice.First.Span);
}
else
{
// There are 4 bytes split across multiple segments. Since it's so
small, it
// can be copied to a stack allocated buffer. This avoids a heap
allocation.
Span<byte> stackBuffer = stackalloc byte[4];
lengthSlice.CopyTo(stackBuffer);
length = BinaryPrimitives.ReadInt32BigEndian(stackBuffer);
}

// Move the buffer 4 bytes ahead.


buffer = buffer.Slice(lengthSlice.End);

return true;
}

Processar dados de texto

O exemplo a seguir:

Localiza a primeira nova linha ( \r\n ) em ReadOnlySequence<byte> e a retorna por


meio do parâmetro out 'line'.
Corta essa linha, excluindo o \r\n do buffer de entrada.

C#

static bool TryParseLine(ref ReadOnlySequence<byte> buffer, out


ReadOnlySequence<byte> line)
{
SequencePosition position = buffer.Start;
SequencePosition previous = position;
var index = -1;
line = default;

while (buffer.TryGet(ref position, out ReadOnlyMemory<byte> segment))


{
ReadOnlySpan<byte> span = segment.Span;
// Look for \r in the current segment.
index = span.IndexOf((byte)'\r');

if (index != -1)
{
// Check next segment for \n.
if (index + 1 >= span.Length)
{
var next = position;
if (!buffer.TryGet(ref next, out ReadOnlyMemory<byte>
nextSegment))
{
// You're at the end of the sequence.
return false;
}
else if (nextSegment.Span[0] == (byte)'\n')
{
// A match was found.
break;
}
}
// Check the current segment of \n.
else if (span[index + 1] == (byte)'\n')
{
// It was found.
break;
}
}

previous = position;
}

if (index != -1)
{
// Get the position just before the \r\n.
var delimeter = buffer.GetPosition(index, previous);

// Slice the line (excluding \r\n).


line = buffer.Slice(buffer.Start, delimeter);

// Slice the buffer to get the remaining data after the line.
buffer = buffer.Slice(buffer.GetPosition(2, delimeter));
return true;
}

return false;
}

Segmentos vazios

É válido armazenar segmentos vazios dentro de um ReadOnlySequence<T> . Segmentos


vazios podem ocorrer ao enumerar segmentos explicitamente:
C#

static void EmptySegments()


{
// This logic creates a ReadOnlySequence<byte> with 4 segments,
// two of which are empty.
var first = new BufferSegment(new byte[0]);
var last = first.Append(new byte[] { 97 })
.Append(new byte[0]).Append(new byte[] { 98 });

// Construct the ReadOnlySequence<byte> from the linked list segments.


var data = new ReadOnlySequence<byte>(first, 0, last, 1);

// Slice using numbers.


var sequence1 = data.Slice(0, 2);

// Slice using SequencePosition pointing at the empty segment.


var sequence2 = data.Slice(data.Start, 2);

Console.WriteLine($"sequence1.Length={sequence1.Length}"); //
sequence1.Length=2
Console.WriteLine($"sequence2.Length={sequence2.Length}"); //
sequence2.Length=2

// sequence1.FirstSpan.Length=1
Console.WriteLine($"sequence1.FirstSpan.Length=
{sequence1.FirstSpan.Length}");

// Slicing using SequencePosition will Slice the ReadOnlySequence<byte>


directly
// on the empty segment!
// sequence2.FirstSpan.Length=0
Console.WriteLine($"sequence2.FirstSpan.Length=
{sequence2.FirstSpan.Length}");

// The following code prints 0, 1, 0, 1.


SequencePosition position = data.Start;
while (data.TryGet(ref position, out ReadOnlyMemory<byte> memory))
{
Console.WriteLine(memory.Length);
}
}

class BufferSegment : ReadOnlySequenceSegment<byte>


{
public BufferSegment(Memory<byte> memory)
{
Memory = memory;
}

public BufferSegment Append(Memory<byte> memory)


{
var segment = new BufferSegment(memory)
{
RunningIndex = RunningIndex + Memory.Length
};
Next = segment;
return segment;
}
}

O código anterior cria um ReadOnlySequence<byte> com segmentos vazios e mostra


como esses segmentos vazios afetam as várias APIs:

ReadOnlySequence<T>.Slice com um SequencePosition apontando para um

segmento vazio preserva esse segmento.


ReadOnlySequence<T>.Slice com um int ignora os segmentos vazios.

Enumerar o ReadOnlySequence<T> enumera os segmentos vazios.

Potenciais problemas com ReadOnlySequence<T> e


SequencePosition
Há vários resultados incomuns ao lidar com um ReadOnlySequence<T> / SequencePosition
versus um ReadOnlySpan<T> / ReadOnlyMemory<T> / T[] / int normal:

SequencePosition é um marcador de posição para um ReadOnlySequence<T>

específico, não uma posição absoluta. Por ser relativo a um ReadOnlySequence<T>


específico, não tem significado se usado fora do ReadOnlySequence<T> de onde se
originou.
A aritmética não pode ser realizada em SequencePosition sem o
ReadOnlySequence<T> . Isso significa fazer coisas básicas como position++ é escrito

position = ReadOnlySequence<T>.GetPosition(1, position) .

GetPosition(long) não oferece suporte a índices negativos. Isso significa que é


impossível obter o penúltimo caractere sem percorrer todos os segmentos.
Dois SequencePosition não podem ser comparados, o que dificulta:
Saber se uma posição é maior ou menor que outra.
Escrever alguns algoritmos de análise.
ReadOnlySequence<T> é maior que uma referência de objeto e deve ser passado por
in ou ref sempre que possível. Passar ReadOnlySequence<T> por in ou ref reduz as
cópias do struct.
Segmentos vazios:
São válidos dentro de um ReadOnlySequence<T> .
Pode aparecer ao iterar usando o método ReadOnlySequence<T>.TryGet .
Pode aparecer fatiando a sequência usando o método
ReadOnlySequence<T>.Slice() com objetos SequencePosition .
SequenceReader<T>
SequenceReader<T>:

É um novo tipo que foi introduzido no .NET Core 3.0 para simplificar o
processamento de um ReadOnlySequence<T> .
Unifica as diferenças entre um único segmento ReadOnlySequence<T> e vários
segmentos ReadOnlySequence<T> .
Fornece auxiliares para a leitura de dados binários e de texto ( byte e char ) que
podem ou não ser divididos entre segmentos.

Há métodos internos para lidar com o processamento de dados binários e delimitados.


A seção a seguir demonstra como são esses mesmos métodos com SequenceReader<T> :

Acessar dados
SequenceReader<T> possui métodos para enumerar dados diretamente dentro do
ReadOnlySequence<T> . O código a seguir é um exemplo de processamento de um

ReadOnlySequence<byte> e um byte por vez:

C#

while (reader.TryRead(out byte b))


{
Process(b);
}

O CurrentSpan expõe o Span do segmento atual, que é semelhante ao que foi feito no
método manualmente.

Usar posição
O código a seguir é um exemplo de implementação de FindIndexOf usando o
SequenceReader<T> :

C#

SequencePosition? FindIndexOf(in ReadOnlySequence<byte> buffer, byte data)


{
var reader = new SequenceReader<byte>(buffer);

while (!reader.End)
{
// Search for the byte in the current span.
var index = reader.CurrentSpan.IndexOf(data);
if (index != -1)
{
// It was found, so advance to the position.
reader.Advance(index);

return reader.Position;
}
// Skip the current segment since there's nothing in it.
reader.Advance(reader.CurrentSpan.Length);
}

return null;
}

Processar dados binários


O exemplo a seguir analisa o comprimento de um inteiro big-endian de 4 bytes desde o
início de ReadOnlySequence<byte> .

C#

bool TryParseHeaderLength(ref ReadOnlySequence<byte> buffer, out int length)


{
var reader = new SequenceReader<byte>(buffer);
return reader.TryReadBigEndian(out length);
}

Processar dados de texto


C#

static ReadOnlySpan<byte> NewLine => new byte[] { (byte)'\r', (byte)'\n' };

static bool TryParseLine(ref ReadOnlySequence<byte> buffer,


out ReadOnlySequence<byte> line)
{
var reader = new SequenceReader<byte>(buffer);

if (reader.TryReadTo(out line, NewLine))


{
buffer = buffer.Slice(reader.Position);

return true;
}

line = default;
return false;
}
Problemas comuns de SequenceReader<T>
Como SequenceReader<T> é uma estrutura mutável, ela sempre deve ser passada
por referência.
SequenceReader<T> é uma estrutura de referência, portanto, só pode ser usada em

métodos síncronos e não pode ser armazenada em campos. Para obter mais
informações, confira Evitar alocações.
SequenceReader<T> é otimizado para uso como leitor somente de

encaminhamento. Rewind destina-se a backups pequenos que não podem ser


resolvidos utilizando outras APIs Read , Peek e IsNext .
Arquivos mapeados na memória
Artigo • 10/05/2023

Um arquivo mapeado pela memória tem o conteúdo de um arquivo em memória


virtual. Esse mapeamento entre um espaço de arquivo e a memória permite que um
aplicativo, inclusive vários processos, modifique o arquivo ao ler e gravar diretamente na
memória. Você pode usar o código gerenciado para acessar arquivos mapeados na
memória tal como fazem funções nativas do Windows, conforme descrito em
Gerenciamento de Arquivos Mapeados na Memória.

Há dois tipos de arquivos mapeados na memória:

Arquivos persistentes mapeados na memória

Arquivos persistentes são arquivos mapeados na memória associados a um


arquivo de origem em um disco. Quando o último processo termina de trabalhar
com o arquivo, os dados são salvos no arquivo de origem no disco. Esses arquivos
mapeados na memória são adequados para trabalhar com arquivos de origem
muito grandes.

Arquivos não persistentes mapeados na memória

Arquivos não persistentes são arquivos mapeados na memória não associados a


um arquivo em disco. Quando o último processo termina de trabalhar com o
arquivo, os dados são perdidos e o arquivo é solicitado pela coleta de lixo. Esses
arquivos são adequados para a criação de memória compartilhada para
comunicações entre processos (IPC).

Processos, exibição e gerenciamento de


memória
Os arquivos mapeados na memória podem ser compartilhados entre vários processos.
Os processos podem mapear para o mesmo arquivo mapeado na memória usando um
nome comum, que é atribuído pelo processo que criou o arquivo.

Para trabalhar com um arquivo mapeado na memória, você deve criar uma exibição de
todo o arquivo mapeado na memória ou de parte dele. Também é possível criar vários
modos de exibição para a mesma parte do arquivo mapeado na memória, criando assim
memórias simultâneas. Para que dois modos de exibição permaneçam simultâneos,
precisam ser criados usando o mesmo arquivo de memória mapeada.
Vários modos de exibição também poderão ser necessários se o arquivo ultrapassar o
tamanho do espaço de memória lógica do aplicativo disponível para o mapeamento de
memória (2GB em um computador de 32 bits).

Há dois tipos de modos de exibição: modo de exibição de acesso por fluxo e modo de
exibição de acesso aleatório. Use os modos de exibição de acesso por fluxo para
acessos sequenciais a um arquivo. Isso é recomendado para arquivos não persistentes e
IPCs. A preferência é para os modos de exibição de acessos aleatórios para trabalhos
com arquivos persistentes.

Os arquivos mapeados na memória são acessados pelo gerenciador de memória do


sistema operacional e o arquivo é automaticamente particionado em um número de
páginas e acessado conforme a necessidade. Não é preciso lidar com o gerenciamento
de memória por conta própria.

A ilustração a seguir mostra como vários processos podem ter modos de exibição
variados e sobrepostos para o mesmo arquivo mapeado na memória ao mesmo tempo.

A seguinte imagem mostra várias exibições sobrepostas em um arquivo mapeado em


memória:

Programar com arquivos mapeados na


memória
A tabela a seguir orienta como usar objetos de arquivos mapeados na memória e os
membros deles.
Tarefa Métodos ou propriedades a serem usados

Para obter um objeto Método MemoryMappedFile.CreateFromFile.


MemoryMappedFile de um
arquivo no disco que representa
um arquivo persistente mapeado
na memória.

Para obter um objeto Método MemoryMappedFile.CreateNew.


MemoryMappedFile que
representa um arquivo não - ou -
persistente mapeado na
memória (não associado a um Método MemoryMappedFile.CreateOrOpen.
arquivo no disco).

Para obter um objeto Método MemoryMappedFile.OpenExisting.


MemoryMappedFile de um
arquivo mapeado na memória
existente (persistente ou não
persistente).

Para obter um objeto Método MemoryMappedFile.CreateViewStream.


UnmanagedMemoryStream para
uma exibição de acesso em
sequência para o arquivo
mapeado na memória.

Para obter um objeto Método MemoryMappedFile.CreateViewAccessor.


UnmanagedMemoryAccessor
para uma exibição de acesso
aleatório para um arquivo
mapeado na memória.

Para obter um objeto Propriedade


SafeMemoryMappedViewHandle MemoryMappedFile.SafeMemoryMappedFileHandle.
a ser usado com um código não
gerenciado. - ou -

Propriedade
MemoryMappedViewAccessor.SafeMemoryMappedViewHandle.

- ou -

Propriedade
MemoryMappedViewStream.SafeMemoryMappedViewHandle.
Tarefa Métodos ou propriedades a serem usados

Para atrasar a alocação de Método CreateNew com o valor


memória até que uma exibição MemoryMappedFileOptions.DelayAllocatePages.
seja criada (somente arquivos
não persistentes). - ou -

Para determinar o tamanho de Métodos CreateOrOpen que possuem uma enumeração


página atual do sistema, use a MemoryMappedFileOptions como parâmetro.
propriedade
Environment.SystemPageSize.

Segurança
É possível aplicar os direitos de acesso ao criar um arquivo mapeado na memória
usando os seguintes métodos que usam uma enumeração MemoryMappedFileAccess
como parâmetro:

MemoryMappedFile.CreateFromFile

MemoryMappedFile.CreateNew

MemoryMappedFile.CreateOrOpen

É possível especificar os direitos de acesso para abrir um arquivo mapeado na memória


existente usando os métodos OpenExisting que usam MemoryMappedFileRights como
parâmetro.

Além disso, é possível incluir um objeto MemoryMappedFileSecurity que contém regras


de acesso predefinidas.

Para aplicar as regras de acesso novas ou modificadas a um arquivo mapeado na


memória, use o método SetAccessControl. Para recuperar as regras de acesso ou de
auditoria de um arquivo existente, use o método GetAccessControl.

Exemplos

Arquivos persistentes mapeados na memória


Os métodos CreateFromFile criam um arquivo mapeado na memória de um arquivo
existente no disco.

O exemplo a seguir cria uma exibição de mapeamento na memória de parte de um


arquivo muito grande e manipula uma porção dele.
C#

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
static void Main(string[] args)
{
long offset = 0x10000000; // 256 megabytes
long length = 0x20000000; // 512 megabytes

// Create the memory-mapped file.


using (var mmf =
MemoryMappedFile.CreateFromFile(@"c:\ExtremelyLargeImage.data",
FileMode.Open,"ImgA"))
{
// Create a random access view, from the 256th megabyte (the
offset)
// to the 768th megabyte (the offset plus length).
using (var accessor = mmf.CreateViewAccessor(offset, length))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;

// Make changes to the view.


for (long i = 0; i < length; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(10);
accessor.Write(i, ref color);
}
}
}
}
}

public struct MyColor


{
public short Red;
public short Green;
public short Blue;
public short Alpha;

// Make the view brighter.


public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}

O exemplo a seguir abre o mesmo arquivo mapeado na memória para outro processo.

C#

using System;
using System.IO.MemoryMappedFiles;
using System.Runtime.InteropServices;

class Program
{
static void Main(string[] args)
{
// Assumes another process has created the memory-mapped file.
using (var mmf = MemoryMappedFile.OpenExisting("ImgA"))
{
using (var accessor = mmf.CreateViewAccessor(4000000, 2000000))
{
int colorSize = Marshal.SizeOf(typeof(MyColor));
MyColor color;

// Make changes to the view.


for (long i = 0; i < 1500000; i += colorSize)
{
accessor.Read(i, out color);
color.Brighten(20);
accessor.Write(i, ref color);
}
}
}
}
}

public struct MyColor


{
public short Red;
public short Green;
public short Blue;
public short Alpha;

// Make the view brigher.


public void Brighten(short value)
{
Red = (short)Math.Min(short.MaxValue, (int)Red + value);
Green = (short)Math.Min(short.MaxValue, (int)Green + value);
Blue = (short)Math.Min(short.MaxValue, (int)Blue + value);
Alpha = (short)Math.Min(short.MaxValue, (int)Alpha + value);
}
}
Arquivos não persistentes mapeados na memória
Os métodos CreateNew e CreateOrOpen criam um arquivo mapeado na memória que
não está mapeado para um arquivo existente no disco.

O exemplo a seguir consiste de três processos separados (aplicativos de console) que


gravam valores booleanos em um arquivo mapeado na memória. Ocorre a seguinte
sequência de ações:

1. O Process A cria o arquivo mapeado na memória e grava um valor para ele.

2. O Process B abre o arquivo mapeado na memória e grava um valor para ele.

3. O Process C abre o arquivo mapeado na memória e grava um valor para ele.

4. O Process A lê e exibe os valores do arquivo mapeado na memória.

5. Após o Process A concluir com o arquivo mapeado na memória, o arquivo é


imediatamente recuperado pela coleta de lixo.

Para executar este exemplo, faça o seguinte:

1. Compile os aplicativos e abra três janelas de Prompt de comando.

2. Na primeira janela do prompt, execute o Process A .

3. Na segunda janela, execute o Process B .

4. Retorne ao Process A e pressione ENTER.

5. Na terceira janela do prompt, execute o Process C .

6. Retorne ao Process A e pressione ENTER.

A saída do Process A é a seguinte:

Console

Start Process B and press ENTER to continue.


Start Process C and press ENTER to continue.
Process A says: True
Process B says: False
Process C says: True

Processo A

C#
using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
// Process A:
static void Main(string[] args)
{
using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap",
10000))
{
bool mutexCreated;
Mutex mutex = new Mutex(true, "testmapmutex", out mutexCreated);
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();

Console.WriteLine("Start Process B and press ENTER to


continue.");
Console.ReadLine();

Console.WriteLine("Start Process C and press ENTER to


continue.");
Console.ReadLine();

mutex.WaitOne();
using (MemoryMappedViewStream stream = mmf.CreateViewStream())
{
BinaryReader reader = new BinaryReader(stream);
Console.WriteLine("Process A says: {0}",
reader.ReadBoolean());
Console.WriteLine("Process B says: {0}",
reader.ReadBoolean());
Console.WriteLine("Process C says: {0}",
reader.ReadBoolean());
}
mutex.ReleaseMutex();
}
}
}

Processo B

C#

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
// Process B:
static void Main(string[] args)
{
try
{
using (MemoryMappedFile mmf =
MemoryMappedFile.OpenExisting("testmap"))
{

Mutex mutex = Mutex.OpenExisting("testmapmutex");


mutex.WaitOne();

using (MemoryMappedViewStream stream =


mmf.CreateViewStream(1, 0))
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(0);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("Memory-mapped file does not exist. Run
Process A first.");
}
}
}

Processo C

C#

using System;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Threading;

class Program
{
// Process C:
static void Main(string[] args)
{
try
{
using (MemoryMappedFile mmf =
MemoryMappedFile.OpenExisting("testmap"))
{
Mutex mutex = Mutex.OpenExisting("testmapmutex");
mutex.WaitOne();

using (MemoryMappedViewStream stream =


mmf.CreateViewStream(2, 0))
{
BinaryWriter writer = new BinaryWriter(stream);
writer.Write(1);
}
mutex.ReleaseMutex();
}
}
catch (FileNotFoundException)
{
Console.WriteLine("Memory-mapped file does not exist. Run
Process A first, then B.");
}
}
}

Confira também
E/S de arquivo e de fluxo
System.IO.FileStream classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Use a classe para ler, gravar, abrir e fechar arquivos em um sistema de arquivos e para
manipular outros identificadores do sistema operacional relacionados a FileStream
arquivos, incluindo pipes, entrada padrão e saída padrão. Você pode usar os
Readmétodos , , e para executar operações síncronas ou os métodos ,
WriteAsyncCopyToWrite, CopyToAsynce FlushAsyncFlush para executar operações
assíncronas.ReadAsync Use os métodos assíncronos para executar operações de arquivo
com uso intensivo de recursos sem bloquear o thread principal. Essa consideração sobre
o desempenho é particularmente importante em um aplicativo da Microsoft Store 8.x ou
aplicativo de desktop em que uma operação demorada de fluxo pode bloquear o
thread de interface do usuário e fazer seu aplicativo parecer como se não estivesse
funcionando. FileStream buffers de entrada e saída para melhor desempenho.

) Importante

Esse tipo implementa a interface IDisposable. Quando você terminar de usar o tipo,
deverá descartá-lo direta ou indiretamente. Para descartar o tipo diretamente,
chame o método Dispose dele em um bloco try / catch . Para descartá-lo
indiretamente, use um constructo de linguagem como using ( em C#) ou Using
(em Visual Basic). Saiba mais na seção "Como usar um objeto que implementa
IDisposable" no tópico da interface IDisposable.

A IsAsync propriedade detecta se o identificador de arquivo foi aberto de forma


assíncrona. Você especifica esse valor ao criar uma instância da FileStream classe usando
um construtor que tenha um isAsync parâmetro , useAsync ou options . Quando a
propriedade é true , o fluxo utiliza E/S sobreposta para executar operações de arquivo
de forma assíncrona. No entanto, a IsAsync propriedade não precisa ser true para
chamar o ReadAsync, WriteAsyncou CopyToAsync método. Quando a propriedade é e
você chama as operações de leitura e gravação assíncronas, o thread da interface do
usuário ainda não é bloqueado, mas a IsAsync operação de E/S real é false executada
de forma síncrona.

O Seek método oferece suporte a acesso aleatório a arquivos. Seek Permite que a
posição de leitura/gravação seja movida para qualquer posição dentro do arquivo. Isso
é feito com parâmetros de ponto de referência de deslocamento de bytes. O
deslocamento de byte é relativo ao ponto de referência de busca, que pode ser o início,
a posição atual ou o fim do arquivo subjacente, conforme representado pelos três
membros da SeekOrigin enumeração.

7 Observação

Os arquivos de disco sempre oferecem suporte a acesso aleatório. No momento da


construção, o CanSeek valor da propriedade é definido como true ou false
dependendo do tipo de arquivo subjacente. Se o tipo de arquivo subjacente for
FILE_TYPE_DISK, conforme definido em winbase.h, o valor da CanSeek propriedade
será true . Caso contrário, o valor da CanSeek propriedade será false .

Se um processo termina com parte de um arquivo bloqueado ou fecha um arquivo que


tem bloqueios pendentes, o comportamento é indefinido.

Para operações de diretório e outras operações de arquivo, consulte as Fileclasses ,


Directorye Path . A File classe é uma classe de utilitário que tem métodos estáticos
principalmente para a criação de objetos com base em caminhos de FileStream arquivo.
A MemoryStream classe cria um fluxo de uma matriz de bytes e é semelhante à
FileStream classe.

Para obter uma lista de operações comuns de arquivo e diretório, consulte Tarefas
comuns de E/S.

Detecção de mudanças de posição do fluxo


Quando um FileStream objeto não tem uma retenção exclusiva em seu identificador,
outro thread pode acessar o identificador de arquivo simultaneamente e alterar a
posição do ponteiro de arquivo do sistema operacional que está associado ao
identificador de arquivo. Nesse caso, a posição armazenada em cache no objeto e os
dados armazenados em cache no FileStream buffer podem ser comprometidos. O
FileStream objeto executa rotineiramente verificações em métodos que acessam o
buffer armazenado em cache para garantir que a posição do identificador do sistema
operacional seja a mesma usada pelo FileStream objeto.

Se uma alteração inesperada na posição do identificador for detectada em uma


chamada para o método, o .NET descarta o conteúdo do buffer e lê o Read fluxo do
arquivo novamente. Isso pode afetar o desempenho, dependendo do tamanho do
arquivo e de quaisquer outros processos que possam afetar a posição do fluxo de
arquivos.
Se uma alteração inesperada na posição do identificador for detectada em uma
chamada para o método, o Write conteúdo do buffer será descartado e uma
IOException exceção será lançada.

Um FileStream objeto não terá uma retenção exclusiva em seu identificador quando a
propriedade for acessada para expor o identificador ou o FileStream objeto receber a
SafeFileHandleSafeFileHandle propriedade em seu construtor.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.IO.FileSystemWatcher classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

Use FileSystemWatcher para observar alterações em um diretório especificado. Você


pode observar alterações em arquivos e subdiretórios do diretório especificado. Você
pode criar um componente para observar arquivos em um computador local, uma
unidade de rede ou um computador remoto.

Para observar as alterações em todos os arquivos, defina a Filter propriedade como uma
cadeia de caracteres vazia ("") ou use curingas ("*.*"). Para observar um arquivo
específico, defina a propriedade como o nome do Filter arquivo. Por exemplo, para
observar alterações no arquivo MyDoc.txt, defina a Filter propriedade como
"MyDoc.txt". Você também pode observar as alterações em um determinado tipo de
arquivo. Por exemplo, para observar alterações em arquivos de texto, defina a Filter
propriedade como "*.txt".

Há vários tipos de alterações que você pode observar em um diretório ou arquivo. Por
exemplo, você pode observar as alterações no Attributes , a data e a LastWrite hora,
ou o Size de arquivos ou diretórios. Isso é feito definindo a NotifyFilter propriedade
para um dos NotifyFilters valores. Para obter mais informações sobre o tipo de
alterações que você pode assistir, consulte NotifyFilters.

Você pode observar a renomeação, exclusão ou criação de arquivos ou diretórios. Por


exemplo, para observar a renomeação de arquivos de texto, defina a Filter propriedade
como "*.txt" e chame o WaitForChanged método com um Renamed especificado para
seu parâmetro.

O sistema operacional Windows notifica o componente sobre alterações de arquivo em


um buffer criado pelo FileSystemWatcher. Se houver muitas alterações em um curto
espaço de tempo, o buffer pode transbordar. Isso faz com que o componente perca o
controle das alterações no diretório e fornecerá apenas uma notificação geral. Aumentar
o tamanho do buffer com a InternalBufferSize propriedade é caro, pois ele vem de
memória não paginada que não pode ser trocada para o disco, portanto, mantenha o
buffer tão pequeno quanto grande o suficiente para não perder nenhum evento de
alteração de arquivo. Para evitar um estouro de buffer, use as NotifyFilter propriedades
e IncludeSubdirectories para que você possa filtrar notificações de alteração
indesejadas.
Para obter uma lista de valores de propriedade inicial para uma instância de
FileSystemWatcher, consulte o FileSystemWatcher construtor.

Considerações ao usar a FileSystemWatcher classe:

Os arquivos ocultos não são ignorados.


Em alguns sistemas, FileSystemWatcher relata alterações em arquivos usando o
formato de nome de arquivo 8.3 curto. Por exemplo, uma alteração para
"LongFileName.LongExtension" pode ser relatada como "LongFil~. Lon".
Esta classe contém uma demanda de link e uma demanda de herança no nível de
classe que se aplica a todos os membros. Um SecurityException é lançado quando
o chamador imediato ou a classe derivada não tem permissão de confiança total.
Para obter detalhes sobre demandas de segurança, consulte Demandas de link.
O tamanho máximo que você pode definir para a InternalBufferSize propriedade
para monitorar um diretório pela rede é 64 KB.

Copiar e mover pastas


O sistema operacional e o objeto interpretam uma ação de cortar e colar ou uma ação
de mover como uma ação de renomeação para uma pasta e FileSystemWatcher seu
conteúdo. Se você recortar e colar uma pasta com arquivos em uma pasta que está
sendo observada, o objeto relatará FileSystemWatcher apenas a pasta como nova, mas
não seu conteúdo, porque eles são essencialmente apenas renomeados.

Para ser notificado de que o conteúdo das pastas foi movido ou copiado para uma
pasta monitorada, forneça OnChanged e OnRenamed manipule métodos de
manipulador de eventos, conforme sugerido na tabela a seguir.

ノ Expandir a tabela

Manipulador de Eventos É executado


Eventos manipulados

OnChanged Changed, Created, Relatar alterações em atributos de arquivo, arquivos


Deleted criados e arquivos excluídos.

OnRenamed Renamed Liste os caminhos antigos e novos de arquivos e


pastas renomeados, expandindo recursivamente se
necessário.

Eventos e tamanhos de buffer


Observe que vários fatores podem afetar quais eventos de alteração do sistema de
arquivos são gerados, conforme descrito a seguir:

Operações comuns do sistema de arquivos podem gerar mais de um evento. Por


exemplo, quando um arquivo é movido de um diretório para outro, vários
OnChanged e alguns OnCreated e OnDeleted eventos podem ser gerados. Mover
um arquivo é uma operação complexa que consiste em várias operações simples,
portanto, gerando vários eventos. Da mesma forma, alguns aplicativos (por
exemplo, software antivírus) podem causar eventos adicionais do sistema de
arquivos detectados pelo FileSystemWatcher.
O FileSystemWatcher pode assistir discos, desde que eles não são trocados ou
removidos. O FileSystemWatcher não gera eventos para CDs e DVDs, porque os
carimbos de data/hora e as propriedades não podem ser alterados. Os
computadores remotos devem ter uma das plataformas necessárias instaladas
para que o componente funcione corretamente.

Observe que um pode perder um FileSystemWatcher evento quando o tamanho do


buffer é excedido. Para evitar a perda de eventos, siga estas diretrizes:

Aumente o tamanho do buffer definindo a InternalBufferSize propriedade.


Evite observar arquivos com nomes de arquivo longos, porque um nome de
arquivo longo contribui para preencher o buffer. Considere renomear esses
arquivos usando nomes mais curtos.
Mantenha seu código de manipulação de eventos o mais curto possível.

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.AppContext classe
Artigo • 30/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A AppContext classe permite que os escritores de biblioteca forneçam um mecanismo


de exclusão uniforme para novas funcionalidades para seus usuários. Ela estabelece um
contrato flexível entre componentes a fim de comunicar uma solicitação de recusa.
Normalmente, essa funcionalidade é importante quando uma alteração é feita na
funcionalidade existente. Por outro lado, já existe uma aceitação implícita da nova
funcionalidade.

AppContext para desenvolvedores de


bibliotecas
As bibliotecas usam a AppContext classe para definir e expor opções de
compatibilidade, enquanto os usuários da biblioteca podem definir essas opções para
afetar o comportamento da biblioteca. Por padrão, as bibliotecas fornecem a nova
funcionalidade, e apenas a alteram (ou seja, eles fornecem a funcionalidade anterior) se
a opção for definida. Isso permite que as bibliotecas forneçam um novo
comportamento para uma API existente enquanto continuam a oferecer suporte a
chamadores que dependem do comportamento anterior.

Definir o nome do switch


A maneira mais comum de permitir que os consumidores de sua biblioteca optem por
não participar de uma mudança de comportamento é definir um switch nomeado. Seu
value elemento é um par nome/valor que consiste no nome de um switch e seu

Boolean valor. Por padrão, a opção é sempre implicitamente false , o que fornece o
novo comportamento (e torna o novo comportamento opt-in por padrão). Definir o
switch para true habilitá-lo, o que fornece o comportamento herdado. Definir
explicitamente a opção para false também fornece o novo comportamento.

É benéfico usar um formato consistente para alternar nomes, já que eles são um
contrato formal exposto por uma biblioteca. A seguir estão dois formatos óbvios:

Opção.namespace.nomedaopção
Opção.biblioteca.nomedaopção
Depois de definir e documentar o switch, os chamadores podem usá-lo chamando o
AppContext.SetSwitch(String, Boolean) método programaticamente. Os aplicativos do
.NET Framework também podem usar a opção adicionando um <elemento
AppContextSwitchOverrides ao arquivo de configuração do> aplicativo ou usando o
Registro. Para obter mais informações sobre como os chamadores usam e definem o
valor das opções de configuração, consulte a seção AppContext para consumidores de
AppContext biblioteca.

No .NET Framework, quando o Common Language Runtime executa um aplicativo, ele


lê automaticamente as configurações de compatibilidade do Registro e carrega o
arquivo de configuração do aplicativo para preencher a instância do AppContext
aplicativo. Como a instância é preenchida programaticamente pelo chamador ou pelo
tempo de execução, os aplicativos do .NET Framework não precisam executar nenhuma
ação, como chamar o SetSwitch método, para configurar a AppContextAppContext
instância.

Verifique a configuração
Você pode verificar se um consumidor declarou o valor do switch e agir adequadamente
chamando o AppContext.TryGetSwitch método. O método retorna true se o argumento
for encontrado e seu isEnabled argumento indica o switchName valor da opção. Do
contrário, o método retorna false .

Exemplo
O exemplo a seguir ilustra o uso da AppContext classe para permitir que o cliente
escolha o comportamento original de um método de biblioteca. A seguir está a versão
1.0 de uma biblioteca chamada StringLibrary . Ele define um SubstringStartsAt
método que executa uma comparação ordinal para determinar o índice inicial de uma
subcadeia de caracteres dentro de uma cadeia de caracteres maior.

C#

using System;
using System.Reflection;

[assembly: AssemblyVersion("1.0.0.0")]

public static class StringLibrary1


{
public static int SubstringStartsAt(string fullString, string substr)
{
return fullString.IndexOf(substr, StringComparison.Ordinal);
}
}

O exemplo a seguir usa a biblioteca para localizar o índice inicial da subcadeia de


caracteres "archæ" em "O arqueólogo". Como o método executa uma comparação
ordinal, a substring não pode ser encontrada.

C#

using System;

public class Example1


{
public static void Main()
{
string value = "The archaeologist";
string substring = "archæ";
int position = StringLibrary1.SubstringStartsAt(value, substring);
if (position >= 0)
Console.WriteLine("'{0}' found in '{1}' starting at position
{2}",
substring, value, position);
else
Console.WriteLine("'{0}' not found in '{1}'", substring, value);
}
}
// The example displays the following output:
// 'archæ' not found in 'The archaeologist'

A versão 2.0 da biblioteca, no entanto, altera o método para usar a comparação sensível
à SubstringStartsAt cultura.

C#

using System;
using System.Reflection;

[assembly: AssemblyVersion("2.0.0.0")]

public static class StringLibrary2


{
public static int SubstringStartsAt(string fullString, string substr)
{
return fullString.IndexOf(substr, StringComparison.CurrentCulture);
}
}

Quando o aplicativo é recompilado para ser executado contra a nova versão da


biblioteca, ele agora relata que a substring "archæ" é encontrada no índice 4 em "O
arqueólogo".

C#

using System;

public class Example2


{
public static void Main()
{
string value = "The archaeologist";
string substring = "archæ";
int position = StringLibrary2.SubstringStartsAt(value, substring);
if (position >= 0)
Console.WriteLine("'{0}' found in '{1}' starting at position
{2}",
substring, value, position);
else
Console.WriteLine("'{0}' not found in '{1}'", substring, value);
}
}
// The example displays the following output:
// 'archæ' found in 'The archaeologist' starting at position 4

Essa alteração pode ser impedida de quebrar os aplicativos que dependem do


comportamento original definindo uma opção. Nesse caso, a opção é chamada
StringLibrary.DoNotUseCultureSensitiveComparison . Seu valor padrão, , false indica que

a biblioteca deve executar sua comparação sensível à cultura da versão 2.0. true indica
que a biblioteca deve executar sua comparação ordinal da versão 1.0. Uma pequena
modificação do código anterior permite que o consumidor da biblioteca defina a opção
para determinar o tipo de comparação que o método executa.

C#

using System;
using System.Reflection;

[assembly: AssemblyVersion("2.0.0.0")]

public static class StringLibrary


{
public static int SubstringStartsAt(string fullString, string substr)
{
bool flag;
if
(AppContext.TryGetSwitch("StringLibrary.DoNotUseCultureSensitiveComparison",
out flag) && flag == true)
return fullString.IndexOf(substr, StringComparison.Ordinal);
else
return fullString.IndexOf(substr, StringComparison.CurrentCulture);
}
}

Um aplicativo .NET Framework pode usar o seguinte arquivo de configuração para


restaurar o comportamento da versão 1.0.

XML

<configuration>
<runtime>
<AppContextSwitchOverrides
value="StringLibrary.DoNotUseCultureSensitiveComparison=true" />
</runtime>
</configuration>

Quando o aplicativo é executado com o arquivo de configuração presente, ele produz a


seguinte saída:

Saída

'archæ' not found in 'The archaeologist'

AppContext para consumidores de bibliotecas


Se você for o consumidor de uma biblioteca, a AppContext classe permitirá que você
aproveite o mecanismo de desativação de uma biblioteca ou método de biblioteca para
novas funcionalidades. Os métodos individuais da biblioteca de classes que você está
chamando definem opções específicas que habilitam ou desabilitam um novo
comportamento. O valor do switch é booleano. Se for , que normalmente é o valor
padrão, o novo comportamento será habilitado, se for false true , o novo
comportamento será desabilitado e o membro se comportará como antes.

Você pode definir o valor de uma opção chamando o AppContext.SetSwitch(String,


Boolean) método em seu código. O switchName argumento define o nome da opção e a
isEnabled propriedade define o valor da opção. Como AppContext é uma classe

estática, ela está disponível por domínio de aplicativo. Chamar o tem escopo de
aplicativo, ou seja, afeta apenas o AppContext.SetSwitch(String, Boolean) aplicativo.

Os aplicativos do .NET Framework têm maneiras adicionais de definir o valor de uma


opção:

Adicionando um <AppContextSwitchOverrides> elemento à <seção de tempo de


execução> do arquivo app.config. O switch tem um único atributo, value , cujo
valor é uma cadeia de caracteres que representa um par chave/valor que contém o
nome do switch e seu valor.

Para definir várias opções, separe o <par chave/valor de cada switch no atributo
do elemento AppContextSwitchOverrides> value com ponto-e-vírgula. Nesse caso,
o elemento tem o <AppContextSwitchOverrides> seguinte formato:

XML

<AppContextSwitchOverrides
value="switchName1=value1;switchName2=value2" />

Usar o elemento para definir uma definição de configuração tem escopo de


aplicativo, ou seja, afeta apenas o <AppContextSwitchOverrides> aplicativo.

7 Observação

Para obter informações sobre as opções definidas pelo .NET Framework,


consulte <AppContextSwitchOverrides> elemento.

Adicionando uma entrada ao registro. Adicione um novo valor de cadeia de


caracteres ao HKLM\SOFTWARE\Microsoft\. NETFramework\AppContext
subchave. Defina o nome da entrada como o nome do switch. Defina seu valor
como uma das seguintes opções: True , , true False ou false . Se o tempo de
execução encontrar qualquer outro valor, ele ignorará a opção.

Em um sistema operacional de 64 bits, você também deve adicionar a mesma


entrada ao HKLM\SOFTWARE\Wow6432Node\Microsoft\.
NETFramework\AppContext subchave.

Usar o registro para definir um AppContext switch tem escopo de máquina, ou


seja, afeta todos os aplicativos em execução na máquina.

Para aplicativos ASP.NET e ASP.NET Core, defina uma opção adicionando um <elemento
Add> à <seção appSettings> do arquivo web.config. Por exemplo:

XML

<appSettings>
<add key="AppContext.SetSwitch:switchName1" value="switchValue1" />
<add key="AppContext.SetSwitch:switchName2" value="switchValue2" />
</appSettings>
Se você definir a mesma opção de mais de uma maneira, a ordem de precedência para
determinar qual configuração substitui as outras será:

1. A configuração programática.
2. A configuração no arquivo app.config (para aplicativos .NET Framework) ou no
arquivo web.config (para aplicativos ASP.NET Core).
3. A configuração do Registro (somente para aplicativos do .NET Framework).

Confira também
Opção AppContext

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Aplicativos de console no .NET
Artigo • 10/05/2023

Os aplicativos do .NET podem usar a classe System.Console para ler e gravar caracteres
no console. Os dados do console são lidos a partir do fluxo de entrada padrão, os dados
para o console são gravados no fluxo de saída padrão e os dados de erro do console
são gravados no fluxo de saída de erro padrão. Esses fluxos são automaticamente
associados ao console quando o aplicativo é iniciado e são apresentados como as
propriedades In, Out e Error, respectivamente.

O valor da propriedade Console.In é um objeto System.IO.TextReader, ao passo que os


valores das propriedades Console.Out e Console.Error são objetos System.IO.TextWriter.
Você pode associar essas propriedades com fluxos que não representam o console,
tornando possível apontar para o fluxo em um local diferente de entrada ou saída. Por
exemplo, você pode redirecionar a saída para um arquivo ao definir a propriedade
Console.Out para um System.IO.StreamWriter que encapsula um System.IO.FileStream
pelo método Console.SetOut. As propriedades Console.In e Console.Out não precisam
fazer referência ao mesmo fluxo.

7 Observação

Para mais informações sobre a compilação de aplicativos de console, incluindo


exemplos em C #, Visual Basic e C++, consulte a documentação da classe Console.

Se o console não existir, como em um aplicativo baseado no Windows Forms, a


gravação de saída no fluxo de saída padrão não será visível, uma vez que não haverá
nenhum console para gravar as informações. A gravação de informações em um console
inacessível não gera uma exceção. (Você sempre pode alterar o tipo de aplicativo para
Aplicativo de console, por exemplo, nas páginas de propriedades do projeto no Visual
Studio).

A classe System.Console tem métodos que podem ler caracteres individuais ou linhas
inteiras do console. Outros métodos convertem dados e cadeias de formato e gravam
as cadeias formatadas no console. Para mais informações sobre cadeias de caracteres de
formatação, confira Tipos de formatação.

 Dica

Nos aplicativos de console falta uma bomba de mensagem iniciada por padrão.
Portanto, as chamadas do console para os temporizadores do Microsoft Win32
podem falhar.

Confira também
System.Console
Formatar tipos
System.Console classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

O console é uma janela do sistema operacional onde os usuários interagem com o


sistema operacional ou com um aplicativo de console baseado em texto inserindo
entrada de texto através do teclado do computador e lendo a saída de texto do terminal
do computador. Por exemplo, no sistema operacional Windows, o console é chamado
de janela de prompt de comando e aceita comandos do MS-DOS. A Console classe
fornece suporte básico para aplicativos que leem caracteres e gravam caracteres no
console.

Fluxos de E/S do console


Quando um aplicativo de console é iniciado, o sistema operacional associa
automaticamente três fluxos de E/S ao console: fluxo de entrada padrão, fluxo de saída
padrão e fluxo de saída de erro padrão. Seu aplicativo pode ler a entrada do usuário a
partir do fluxo de entrada padrão; gravar dados normais no fluxo de saída padrão; e
gravar dados de erro no fluxo de saída de erro padrão. Esses fluxos são apresentados ao
seu aplicativo como os valores das propriedades , Console.InConsole.Oute Console.Error
.

Por padrão, o In valor da propriedade é um System.IO.TextReader objeto que representa


o teclado, e os valores das propriedades e Error são System.IO.TextWriter objetos que
representam uma janela do Out console. No entanto, você pode definir essas
propriedades como fluxos que não representam a janela do console ou o teclado; Por
exemplo, você pode definir essas propriedades como fluxos que representam arquivos.
Para redirecionar a entrada padrão, a saída padrão ou o fluxo de erro padrão, chame o
Console.SetInmétodo , Console.SetOutou Console.SetError , respectivamente. As
operações de E/S que usam esses fluxos são sincronizadas, o que significa que vários
threads podem ler ou gravar nos fluxos. Isso significa que os métodos que normalmente
são assíncronos, como TextReader.ReadLineAsync, são executados de forma síncrona se
o objeto representar um fluxo de console.

7 Observação

Não use a classe para exibir a Console saída em aplicativos autônomos, como
aplicativos de servidor. Chamadas para métodos como Console.Write e
Console.WriteLine não têm efeito em aplicativos GUI.

Console Os membros da classe que funcionam normalmente quando o fluxo subjacente


é direcionado para um console podem lançar uma exceção se o fluxo for redirecionado,
por exemplo, para um arquivo. Programe seu aplicativo para capturar
System.IO.IOException exceções se você redirecionar um fluxo padrão. Você também
pode usar as IsOutputRedirectedpropriedades , IsInputRedirectede IsErrorRedirected
para determinar se um fluxo padrão é redirecionado antes de executar uma operação
que gera uma System.IO.IOException exceção.

Às vezes, é útil chamar explicitamente os membros dos objetos de fluxo representados


pelas Inpropriedades , Oute Error . Por exemplo, por padrão, o Console.ReadLine
método lê a entrada do fluxo de entrada padrão. Da mesma forma, o método grava
Console.WriteLine dados no fluxo de saída padrão e os dados são seguidos pela cadeia
de caracteres de terminação de linha padrão, que pode ser encontrada em
Environment.NewLine. No entanto, a classe não fornece um método correspondente
para gravar dados no fluxo de saída de erro padrão ou uma propriedade para alterar a
Console cadeia de caracteres de terminação de linha para dados gravados nesse fluxo.

Você pode resolver esse problema definindo a TextWriter.NewLineOut propriedade da


propriedade or Error para outra cadeia de caracteres de terminação de linha. Por
exemplo, a instrução C# a seguir define a cadeia de caracteres de terminação de linha
para o fluxo de saída de erro padrão como duas sequências de retorno de carro e
alimentação de linha:

Console.Error.NewLine = "\r\n\r\n";

Em seguida, você pode chamar explicitamente o WriteLine método do objeto de fluxo


de saída de erro, como na instrução C# a seguir:

Console.Error.WriteLine();

Buffer de tela e janela do console


Dois recursos intimamente relacionados do console são o buffer de tela e a janela do
console. Na verdade, o texto é lido ou gravado em fluxos de propriedade do console,
mas parece ser lido ou gravado em uma área de propriedade do console chamada
buffer de tela. O buffer de tela é um atributo do console e é organizado como uma
grade retangular de linhas e colunas onde cada interseção de grade, ou célula de
caractere, pode conter um caractere. Cada caractere tem sua própria cor de primeiro
plano e cada célula de caractere tem sua própria cor de plano de fundo.
O buffer de tela é visualizado através de uma região retangular chamada janela do
console. A janela do console é outro atributo do console; não é o console em si, que é
uma janela do sistema operacional. A janela do console é organizada em linhas e
colunas, é menor ou igual ao tamanho do buffer de tela e pode ser movida para exibir
diferentes áreas do buffer de tela subjacente. Se o buffer de tela for maior que a janela
do console, o console exibirá automaticamente as barras de rolagem para que a janela
do console possa ser reposicionada sobre a área do buffer da tela.

Um cursor indica a posição do buffer de tela onde o texto é lido ou gravado no


momento. O cursor pode ser oculto ou tornado visível, e sua altura pode ser alterada. Se
o cursor estiver visível, a posição da janela do console será movida automaticamente
para que o cursor esteja sempre em exibição.

A origem das coordenadas da célula de caractere no buffer de tela é o canto superior


esquerdo, e as posições do cursor e da janela do console são medidas em relação a essa
origem. Use índices baseados em zero para especificar posições; ou seja, especifique a
linha superior como linha 0 e a coluna mais à esquerda como coluna 0. O valor máximo
para os índices de linha e coluna é Int16.MaxValue.

Suporte a Unicode para o console


Em geral, o console lê a entrada e grava a saída usando a página de código do console
atual, que a localidade do sistema define por padrão. Uma página de código pode
manipular apenas um subconjunto de caracteres Unicode disponíveis, portanto, se você
tentar exibir caracteres que não são mapeados por uma página de código específica, o
console não poderá exibir todos os caracteres ou representá-los com precisão. O
exemplo a seguir ilustra esse problema. Ele tenta exibir os caracteres do alfabeto cirílico
de U+0410 a U+044F para o console. Se você executar o exemplo em um sistema que
usa a página de código de console 437, cada caractere será substituído por um ponto
de interrogação (?), porque os caracteres cirílicos não são mapeados para os caracteres
na página de código 437.

C#

using System;

public class Example3


{
public static void Main()
{
// Create a Char array for the modern Cyrillic alphabet,
// from U+0410 to U+044F.
int nChars = 0x044F - 0x0410 + 1;
char[] chars = new char[nChars];
ushort codePoint = 0x0410;
for (int ctr = 0; ctr < chars.Length; ctr++)
{
chars[ctr] = (char)codePoint;
codePoint++;
}

Console.WriteLine("Current code page: {0}\n",


Console.OutputEncoding.CodePage);
// Display the characters.
foreach (var ch in chars)
{
Console.Write("{0} ", ch);
if (Console.CursorLeft >= 70)
Console.WriteLine();
}
}
}
// The example displays the following output:
// Current code page: 437
//
// ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
// ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?
// ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?

Além de oferecer suporte a páginas de código, a classe oferece suporte à codificação


UTF-8 com a ConsoleUTF8Encoding classe. A partir do .NET Framework 4.5, a classe
também oferece suporte à codificação UTF-16 com a ConsoleUnicodeEncoding classe.
Para exibir caracteres Unicode no console. Você define a OutputEncoding propriedade
como ou UTF8EncodingUnicodeEncoding.

O suporte para caracteres Unicode requer que o codificador reconheça um caractere


Unicode específico e também requer uma fonte que tenha os glifos necessários para
renderizar esse caractere. Para exibir caracteres Unicode com êxito no console, a fonte
do console deve ser definida como uma fonte não raster ou TrueType, como Consolas
ou Lucida Console. O exemplo a seguir mostra como você pode alterar
programaticamente a fonte de uma fonte raster para Lucida Console.

C#

using System;
using System.Runtime.InteropServices;

public class Example2


{
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError =


true)]
static extern bool GetCurrentConsoleFontEx(
IntPtr consoleOutput,
bool maximumWindow,
ref CONSOLE_FONT_INFO_EX lpConsoleCurrentFontEx);

[DllImport("kernel32.dll", SetLastError = true)]


static extern bool SetCurrentConsoleFontEx(
IntPtr consoleOutput,
bool maximumWindow,
CONSOLE_FONT_INFO_EX consoleCurrentFontEx);

private const int STD_OUTPUT_HANDLE = -11;


private const int TMPF_TRUETYPE = 4;
private const int LF_FACESIZE = 32;
private static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);

public static unsafe void Main()


{
string fontName = "Lucida Console";
IntPtr hnd = GetStdHandle(STD_OUTPUT_HANDLE);
if (hnd != INVALID_HANDLE_VALUE)
{
CONSOLE_FONT_INFO_EX info = new CONSOLE_FONT_INFO_EX();
info.cbSize = (uint)Marshal.SizeOf(info);
bool tt = false;
// First determine whether there's already a TrueType font.
if (GetCurrentConsoleFontEx(hnd, false, ref info))
{
tt = (info.FontFamily & TMPF_TRUETYPE) == TMPF_TRUETYPE;
if (tt)
{
Console.WriteLine("The console already is using a
TrueType font.");
return;
}
// Set console font to Lucida Console.
CONSOLE_FONT_INFO_EX newInfo = new CONSOLE_FONT_INFO_EX();
newInfo.cbSize = (uint)Marshal.SizeOf(newInfo);
newInfo.FontFamily = TMPF_TRUETYPE;
IntPtr ptr = new IntPtr(newInfo.FaceName);
Marshal.Copy(fontName.ToCharArray(), 0, ptr,
fontName.Length);
// Get some settings from current font.
newInfo.dwFontSize = new COORD(info.dwFontSize.X,
info.dwFontSize.Y);
newInfo.FontWeight = info.FontWeight;
SetCurrentConsoleFontEx(hnd, false, newInfo);
}
}
}

[StructLayout(LayoutKind.Sequential)]
internal struct COORD
{
internal short X;
internal short Y;

internal COORD(short x, short y)


{
X = x;
Y = y;
}
}

[StructLayout(LayoutKind.Sequential)]
internal unsafe struct CONSOLE_FONT_INFO_EX
{
internal uint cbSize;
internal uint nFont;
internal COORD dwFontSize;
internal int FontFamily;
internal int FontWeight;
internal fixed char FaceName[LF_FACESIZE];
}
}

No entanto, as fontes TrueType podem exibir apenas um subconjunto de glifos. Por


exemplo, a fonte Lucida Console exibe apenas 643 dos cerca de 64.000 caracteres
disponíveis de U+0021 a U+FB02. Para ver quais caracteres uma fonte específica
suporta, abra o miniaplicativo Fontes no Painel de Controle, escolha a opção Localizar
um caractere e escolha a fonte cujo conjunto de caracteres você deseja examinar na
lista Fonte da janela Mapa de Caracteres.

O Windows usa a vinculação de fonte para exibir glifos que não estão disponíveis em
uma fonte específica. Para obter informações sobre a vinculação de fontes para exibir
conjuntos de caracteres adicionais, consulte Passo a passo da globalização: fontes . As
fontes vinculadas são definidas na subchave
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows
NT\CurrentVersion\FontLink\SystemLink do Registro. Cada entrada associada a essa
subchave corresponde ao nome de uma fonte base, e seu valor é uma matriz de cadeia
de caracteres que define os arquivos de fonte e as fontes vinculadas à fonte base. Cada
membro da matriz define uma fonte vinculada e assume a forma font-file-name,font-
name. O exemplo a seguir ilustra como você pode definir programaticamente uma fonte
vinculada chamada SimSun encontrada em um arquivo de fonte chamado simsun.ttc
que exibe caracteres Han simplificados.

C#

using Microsoft.Win32;
using System;

public class Example


{
public static void Main()
{
string valueName = "Lucida Console";
string newFont = "simsun.ttc,SimSun";
string[] fonts = null;
RegistryValueKind kind = 0;
bool toAdd;

RegistryKey key = Registry.LocalMachine.OpenSubKey(


@"Software\Microsoft\Windows
NT\CurrentVersion\FontLink\SystemLink",
true);
if (key == null) {
Console.WriteLine("Font linking is not enabled.");
}
else {
// Determine if the font is a base font.
string[] names = key.GetValueNames();
if (Array.Exists(names, s => s.Equals(valueName,
StringComparison.OrdinalIgnoreCase)))
{
// Get the value's type.
kind = key.GetValueKind(valueName);

// Type should be RegistryValueKind.MultiString, but we can't be


sure.
switch (kind) {
case RegistryValueKind.String:
fonts = new string[] { (string) key.GetValue(valueName) };
break;
case RegistryValueKind.MultiString:
fonts = (string[]) key.GetValue(valueName);
break;
case RegistryValueKind.None:
// Do nothing.
fonts = new string[] { };
break;
}
// Determine whether SimSun is a linked font.
if (Array.FindIndex(fonts, s =>s.IndexOf("SimSun",
StringComparison.OrdinalIgnoreCase)
>=0) >= 0) {
Console.WriteLine("Font is already linked.");
toAdd = false;
}
else {
// Font is not a linked font.
toAdd = true;
}
}
else {
// Font is not a base font.
toAdd = true;
fonts = new string[] { };
}

if (toAdd) {
Array.Resize(ref fonts, fonts.Length + 1);
fonts[fonts.GetUpperBound(0)] = newFont;
// Change REG_SZ to REG_MULTI_SZ.
if (kind == RegistryValueKind.String)
key.DeleteValue(valueName, false);

key.SetValue(valueName, fonts, RegistryValueKind.MultiString);


Console.WriteLine("SimSun added to the list of linked fonts.");
}
}

if (key != null) key.Close();


}
}

O suporte a Unicode para o console tem as seguintes limitações:

A codificação UTF-32 não é suportada. As únicas codificações Unicode suportadas


são UTF-8 e UTF-16, que são representadas pelas UTF8Encoding classes e
UnicodeEncoding , respectivamente.

Não há suporte para saída bidirecional.

Não há suporte para a exibição de caracteres fora do Plano Multilíngue Básico (ou
seja, de pares substitutos), mesmo que eles estejam definidos em um arquivo de
fonte vinculado.

Não há suporte para a exibição de caracteres em scripts complexos.

A combinação de sequências de caracteres (ou seja, caracteres que consistem em


um caractere base e um ou mais caracteres combinados) são exibidas como
caracteres separados. Para contornar essa limitação, você pode normalizar a
sequência de caracteres a ser exibida chamando o método antes de enviar a saída
para o String.Normalize console. No exemplo a seguir, uma cadeia de caracteres
que contém a sequência de caracteres de combinação U+0061 U+0308 é exibida
no console como dois caracteres antes da cadeia de caracteres de saída ser
normalizada e como um único caractere depois que o String.Normalize método é
chamado.

C#

using System;
using System.IO;

public class Example1


{
public static void Main()
{
char[] chars = { '\u0061', '\u0308' };

string combining = new String(chars);


Console.WriteLine(combining);

combining = combining.Normalize();
Console.WriteLine(combining);
}
}
// The example displays the following output:
// a"
// ä

A normalização é uma solução viável somente se o padrão Unicode para o


caractere incluir uma forma pré-composta que corresponda a uma sequência de
caracteres de combinação específica.

Se uma fonte fornecer um glifo para um ponto de código na área de uso privado,
esse glifo será exibido. No entanto, como os caracteres na área de uso privado são
específicos do aplicativo, esse pode não ser o glifo esperado.

O exemplo a seguir exibe um intervalo de caracteres Unicode para o console. O


exemplo aceita três parâmetros de linha de comando: o início do intervalo a ser exibido,
o fim do intervalo a ser exibido e se a codificação de console atual () ou UTF-16 true
( false ). Ele pressupõe que o console está usando uma fonte TrueType.

C#

using System;
using System.IO;
using System.Globalization;
using System.Text;

public static class DisplayChars


{
private static void Main(string[] args)
{
uint rangeStart = 0;
uint rangeEnd = 0;
bool setOutputEncodingToUnicode = true;
// Get the current encoding so we can restore it.
Encoding originalOutputEncoding = Console.OutputEncoding;

try
{
switch(args.Length)
{
case 2:
rangeStart = uint.Parse(args[0], NumberStyles.HexNumber);
rangeEnd = uint.Parse(args[1], NumberStyles.HexNumber);
setOutputEncodingToUnicode = true;
break;
case 3:
if (! uint.TryParse(args[0], NumberStyles.HexNumber, null,
out rangeStart))
throw new ArgumentException(String.Format("{0} is not a
valid hexadecimal number.", args[0]));

if (!uint.TryParse(args[1], NumberStyles.HexNumber, null, out


rangeEnd))
throw new ArgumentException(String.Format("{0} is not a
valid hexadecimal number.", args[1]));

bool.TryParse(args[2], out setOutputEncodingToUnicode);


break;
default:
Console.WriteLine("Usage: {0} <{1}> <{2}> [{3}]",
Environment.GetCommandLineArgs()[0],
"startingCodePointInHex",
"endingCodePointInHex",
"<setOutputEncodingToUnicode?{true|false,
default:false}>");
return;
}

if (setOutputEncodingToUnicode) {
// This won't work before .NET Framework 4.5.
try {
// Set encoding using endianness of this system.
// We're interested in displaying individual Char objects, so
// we don't want a Unicode BOM or exceptions to be thrown on
// invalid Char values.
Console.OutputEncoding = new UnicodeEncoding(!
BitConverter.IsLittleEndian, false);
Console.WriteLine("\nOutput encoding set to UTF-16");
}
catch (IOException) {
Console.OutputEncoding = new UTF8Encoding();
Console.WriteLine("Output encoding set to UTF-8");
}
}
else {
Console.WriteLine("The console encoding is {0} (code page {1})",
Console.OutputEncoding.EncodingName,
Console.OutputEncoding.CodePage);
}
DisplayRange(rangeStart, rangeEnd);
}
catch (ArgumentException ex) {
Console.WriteLine(ex.Message);
}
finally {
// Restore console environment.
Console.OutputEncoding = originalOutputEncoding;
}
}

public static void DisplayRange(uint start, uint end)


{
const uint upperRange = 0x10FFFF;
const uint surrogateStart = 0xD800;
const uint surrogateEnd = 0xDFFF;

if (end <= start) {


uint t = start;
start = end;
end = t;
}

// Check whether the start or end range is outside of last plane.


if (start > upperRange)
throw new ArgumentException(String.Format("0x{0:X5} is outside the
upper range of Unicode code points (0x{1:X5})",
start, upperRange));
if (end > upperRange)
throw new ArgumentException(String.Format("0x{0:X5} is outside the
upper range of Unicode code points (0x{0:X5})",
end, upperRange));

// Since we're using 21-bit code points, we can't use U+D800 to


U+DFFF.
if ((start < surrogateStart & end > surrogateStart) || (start >=
surrogateStart & start <= surrogateEnd ))
throw new ArgumentException(String.Format("0x{0:X5}-0x{1:X5}
includes the surrogate pair range 0x{2:X5}-0x{3:X5}",
start, end,
surrogateStart, surrogateEnd));
uint last = RoundUpToMultipleOf(0x10, end);
uint first = RoundDownToMultipleOf(0x10, start);

uint rows = (last - first) / 0x10;

for (uint r = 0; r < rows; ++r) {


// Display the row header.
Console.Write("{0:x5} ", first + 0x10 * r);

for (uint c = 0; c < 0x10; ++c) {


uint cur = (first + 0x10 * r + c);
if (cur < start) {
Console.Write($" {(char)(0x20)} ");
}
else if (end < cur) {
Console.Write($" {(char)(0x20)} ");
}
else {
// the cast to int is safe, since we know that val <=
upperRange.
String chars = Char.ConvertFromUtf32( (int) cur);
// Display a space for code points that are not valid
characters.
if (CharUnicodeInfo.GetUnicodeCategory(chars[0]) ==

UnicodeCategory.OtherNotAssigned)
Console.Write($" {(char)(0x20)} ");
// Display a space for code points in the private use area.
else if (CharUnicodeInfo.GetUnicodeCategory(chars[0]) ==
UnicodeCategory.PrivateUse)
Console.Write($" {(char)(0x20)} ");
// Is surrogate pair a valid character?
// Note that the console will interpret the high and low
surrogate
// as separate (and unrecognizable) characters.
else if (chars.Length > 1 &&
CharUnicodeInfo.GetUnicodeCategory(chars, 0) ==

UnicodeCategory.OtherNotAssigned)
Console.Write($" {(char)(0x20)} ");
else
Console.Write($" {chars} ");
}

switch (c) {
case 3: case 11:
Console.Write("-");
break;
case 7:
Console.Write("--");
break;
}
}

Console.WriteLine();
if (0 < r && r % 0x10 == 0)
Console.WriteLine();
}
}

private static uint RoundUpToMultipleOf(uint b, uint u)


{
return RoundDownToMultipleOf(b, u) + b;
}

private static uint RoundDownToMultipleOf(uint b, uint u)


{
return u - (u % b);
}
}
// If the example is run with the command line
// DisplayChars 0400 04FF true
// the example displays the Cyrillic character set as follows:
// Output encoding set to UTF-16
// 00400 Ѐ Ё Ђ Ѓ - Є Ѕ І Ї -- Ј Љ Њ Ћ - Ќ Ѝ Ў Џ
// 00410 А Б В Г - Д Е Ж З -- И Й К Л - М Н О П
// 00420 Р С Т У - Ф Х Ц Ч -- Ш Щ Ъ Ы - Ь Э Ю Я
// 00430 а б в г - д е ж з -- и й к л - м н о п
// 00440 р с т у - ф х ц ч -- ш щ ъ ы - ь э ю я
// 00450 ѐ ё ђ ѓ - є ѕ і ї -- ј љ њ ћ - ќ ѝ ў џ
// 00460 Ѡ ѡ Ѣ ѣ - Ѥ ѥ Ѧ ѧ -- Ѩ ѩ Ѫ ѫ - Ѭ ѭ Ѯ ѯ
// 00470 Ѱ ѱ Ѳ ѳ - Ѵ ѵ Ѷ ѷ -- Ѹ ѹ Ѻ ѻ - Ѽ ѽ Ѿ ѿ
// 00480 Ҁ ҁ ҂ ҃- ҄ ҅ ҆ ҇-- ҈ ҉ Ҋ ҋ - Ҍ ҍ Ҏ ҏ
// 00490 Ґ ґ Ғ ғ - Ҕ ҕ Җ җ -- Ҙ ҙ Қ қ - Ҝ ҝ Ҟ ҟ
// 004a0 Ҡ ҡ Ң ң - Ҥ ҥ Ҧ ҧ -- Ҩ ҩ Ҫ ҫ - Ҭ ҭ Ү ү
// 004b0 Ұ ұ Ҳ ҳ - Ҵ ҵ Ҷ ҷ -- Ҹ ҹ Һ һ - Ҽ ҽ Ҿ ҿ
// 004c0 Ӏ Ӂ ӂ Ӄ - ӄ Ӆ ӆ Ӈ -- ӈ Ӊ ӊ Ӌ - ӌ Ӎ ӎ ӏ
// 004d0 Ӑ ӑ Ӓ ӓ - Ӕ ӕ Ӗ ӗ -- Ә ә Ӛ ӛ - Ӝ ӝ Ӟ ӟ
// 004e0 Ӡ ӡ Ӣ ӣ - Ӥ ӥ Ӧ ӧ -- Ө ө Ӫ ӫ - Ӭ ӭ Ӯ ӯ
// 004f0 Ӱ ӱ Ӳ ӳ - Ӵ ӵ Ӷ ӷ -- Ӹ ӹ Ӻ ӻ - Ӽ ӽ Ӿ ӿ

Operações comuns
A Console classe contém os seguintes métodos para ler a entrada do console e gravar a
saída do console:

As sobrecargas do ReadKey método leem um caractere individual.

O ReadLine método lê uma linha inteira de entrada.

A Write sobrecarga do método converte uma instância de um tipo de valor, uma


matriz de caracteres ou um conjunto de objetos em uma cadeia de caracteres
formatada ou não formatada e, em seguida, grava essa cadeia de caracteres no
console.

Um conjunto paralelo de sobrecargas de método produz a mesma cadeia de


caracteres que as sobrecargas, Write mas também adiciona uma cadeia de
caracteres de terminação de WriteLine linha.

A Console classe também contém métodos e propriedades para executar as seguintes


operações:

Obter ou definir o tamanho do buffer de tela. As BufferHeight propriedades e


permitem que você obtenha ou defina a altura e a largura do buffer,
respectivamente, e BufferWidth o método permite definir o SetBufferSize tamanho
do buffer em uma única chamada de método.

Obter ou definir o tamanho da janela do console. As WindowHeight propriedades


e permitem que você obtenha ou defina a altura e a largura da janela,
respectivamente, e WindowWidth o método permite definir o SetWindowSize
tamanho da janela em uma única chamada de método.
Obter ou definir o tamanho do cursor. A CursorSize propriedade especifica a altura
do cursor em uma célula de caractere.

Obtenha ou defina a posição da janela do console em relação ao buffer de tela. As


WindowTop propriedades e permitem que você obtenha ou defina a linha superior
e a coluna mais à esquerda do buffer de tela que aparece na janela do console, e
WindowLeft o SetWindowPosition método permite definir esses valores em uma
única chamada de método.

Obtenha ou defina a posição do cursor obtendo ou definindo as CursorTop


propriedades e CursorLeft ou defina a posição do cursor chamando o
SetCursorPosition método.

Mova ou limpe dados no buffer de tela chamando o MoveBufferArea método ou


Clear .

Obtenha ou defina as cores de primeiro plano e plano de fundo usando as


ForegroundColor propriedades e ou redefina o plano de fundo e BackgroundColor
o primeiro plano para suas cores padrão chamando o ResetColor método.

Reproduza o som de um bipe através do alto-falante do console chamando o Beep


método.

Notas do .NET Core


No .NET Framework na área de trabalho, a classe usa a Console codificação retornada
por GetConsoleCP e GetConsoleOutputCP , que normalmente é uma codificação de página
de código. Por exemplo, em sistemas cuja cultura é inglês (Estados Unidos), a página de
código 437 é a codificação usada por padrão. No entanto, o .NET Core pode
disponibilizar apenas um subconjunto limitado dessas codificações. Quando esse for o
caso, Encoding.UTF8 é usado como a codificação padrão para o console.

Se seu aplicativo depender de codificações de página de código específicas, você ainda


poderá disponibilizá-las fazendo o seguinte antes de chamar qualquer Console método:

1. Recupere o EncodingProvider objeto da CodePagesEncodingProvider.Instance


propriedade.

2. Passe o objeto para o EncodingProviderEncoding.RegisterProvider método para


disponibilizar as codificações adicionais suportadas pelo provedor de codificação.

A Console classe usará automaticamente a codificação padrão do sistema em vez de


UTF8, desde que você tenha registrado o provedor de codificação antes de chamar
qualquer Console método de saída.

Exemplos
O exemplo a seguir demonstra como ler e gravar dados nos fluxos de entrada e saída
padrão. Observe que esses fluxos podem ser redirecionados usando os SetIn métodos e
SetOut .

C#

using System;

public class Example4


{
public static void Main()
{
Console.Write("Hello ");
Console.WriteLine("World!");
Console.Write("Enter your name: ");
string name = Console.ReadLine();
Console.Write("Good day, ");
Console.Write(name);
Console.WriteLine("!");
}
}
// The example displays output similar to the following:
// Hello World!
// Enter your name: James
// Good day, James!

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
System.Random classe
Artigo • 11/01/2024

Este artigo fornece observações complementares à documentação de referência para


essa API.

A Random classe representa um gerador de números pseudoaleatórios, que é um


algoritmo que produz uma sequência de números que atendem a certos requisitos
estatísticos para aleatoriedade.

Números pseudoaleatórios são escolhidos com igual probabilidade a partir de um


conjunto finito de números. Os números escolhidos não são completamente aleatórios
porque um algoritmo matemático é usado para selecioná-los, mas eles são
suficientemente aleatórios para fins práticos. A implementação da Random classe é
baseada em uma versão modificada do algoritmo gerador de números aleatórios
subtrativos de Donald E. Knuth. Para obter mais informações, consulte D. E. Knuth. A
Arte da Programação de Computadores, Volume 2: Algoritmos Seminuméricos. Addison-
Wesley, Reading, MA, terceira edição, 1997.

Para gerar um número aleatório criptograficamente seguro, como um que seja


adequado para criar uma senha aleatória, use a RNGCryptoServiceProvider classe ou
derive uma classe de System.Security.Cryptography.RandomNumberGenerator.

Instanciar o gerador de números aleatórios


Você instancia o gerador de números aleatórios fornecendo um valor de semente (um
valor inicial para o algoritmo de geração de números pseudoaleatórios) para um
Random construtor de classe. Você pode fornecer o valor de semente explícita ou
implicitamente:

O Random(Int32) construtor usa um valor de semente explícito que você fornece.


O Random() construtor usa o valor de semente padrão. Esta é a maneira mais
comum de instanciar o gerador de números aleatórios.

No .NET Framework, o valor de propagação padrão é dependente do tempo. No .NET


Core, o valor de propagação padrão é produzido pelo gerador de números
pseudoaleatórios estáticos de thread.

Se a mesma semente for usada para objetos separados Random , eles gerarão a mesma
série de números aleatórios. Isso pode ser útil para criar um conjunto de testes que
processa valores aleatórios ou para repetir jogos que derivam seus dados de números
aleatórios. No entanto, observe que Random objetos em processos em execução em
versões diferentes do .NET Framework podem retornar séries diferentes de números
aleatórios, mesmo se eles são instanciados com valores de semente idênticos.

Para produzir diferentes sequências de números aleatórios, você pode tornar o valor de
semente dependente do tempo, produzindo assim uma série diferente a cada nova
instância do Random. O construtor parametrizado Random(Int32) pode assumir um
Int32 valor com base no número de ticks no tempo atual, enquanto o construtor sem
Random() parâmetros usa o relógio do sistema para gerar seu valor de semente. No
entanto, somente no .NET Framework, como o relógio tem resolução finita, usar o
construtor sem parâmetros para criar objetos diferentes Random em sucessão próxima
cria geradores de números aleatórios que produzem sequências idênticas de números
aleatórios. O exemplo a seguir ilustra como dois Random objetos que são instanciados
em sucessão próxima em um aplicativo .NET Framework geram uma série idêntica de
números aleatórios. Na maioria dos sistemas Windows, Random os objetos criados
dentro de 15 milissegundos um do outro provavelmente têm valores de semente
idênticos.

C#

byte[] bytes1 = new byte[100];


byte[] bytes2 = new byte[100];
Random rnd1 = new Random();
Random rnd2 = new Random();

rnd1.NextBytes(bytes1);
rnd2.NextBytes(bytes2);

Console.WriteLine("First Series:");
for (int ctr = bytes1.GetLowerBound(0);
ctr <= bytes1.GetUpperBound(0);
ctr++) {
Console.Write("{0, 5}", bytes1[ctr]);
if ((ctr + 1) % 10 == 0) Console.WriteLine();
}

Console.WriteLine();

Console.WriteLine("Second Series:");
for (int ctr = bytes2.GetLowerBound(0);
ctr <= bytes2.GetUpperBound(0);
ctr++) {
Console.Write("{0, 5}", bytes2[ctr]);
if ((ctr + 1) % 10 == 0) Console.WriteLine();
}

// The example displays output like the following:


// First Series:
// 97 129 149 54 22 208 120 105 68 177
// 113 214 30 172 74 218 116 230 89 18
// 12 112 130 105 116 180 190 200 187 120
// 7 198 233 158 58 51 50 170 98 23
// 21 1 113 74 146 245 34 255 96 24
// 232 255 23 9 167 240 255 44 194 98
// 18 175 173 204 169 171 236 127 114 23
// 167 202 132 65 253 11 254 56 214 127
// 145 191 104 163 143 7 174 224 247 73
// 52 6 231 255 5 101 83 165 160 231
//
// Second Series:
// 97 129 149 54 22 208 120 105 68 177
// 113 214 30 172 74 218 116 230 89 18
// 12 112 130 105 116 180 190 200 187 120
// 7 198 233 158 58 51 50 170 98 23
// 21 1 113 74 146 245 34 255 96 24
// 232 255 23 9 167 240 255 44 194 98
// 18 175 173 204 169 171 236 127 114 23
// 167 202 132 65 253 11 254 56 214 127
// 145 191 104 163 143 7 174 224 247 73
// 52 6 231 255 5 101 83 165 160 231

Para evitar esse problema, crie um único Random objeto em vez de vários objetos.
Observe que a Random classe no .NET Core não tem essa limitação.

Evite várias instanciações


No .NET Framework, inicializar dois geradores de números aleatórios em um loop
apertado ou em sucessão rápida cria dois geradores de números aleatórios que podem
produzir sequências idênticas de números aleatórios. Na maioria dos casos, essa não é a
intenção do desenvolvedor e pode levar a problemas de desempenho, porque
instanciar e inicializar um gerador de números aleatórios é um processo relativamente
caro.

Tanto para melhorar o desempenho quanto para evitar a criação inadvertida de


geradores de números aleatórios separados que geram sequências numéricas idênticas,
recomendamos que você crie um objeto para gerar muitos números aleatórios ao longo
do tempo, em vez de criar novos Random objetos para gerar um Random número
aleatório.

No entanto, a Random classe não é thread safe. Se você chamar Random métodos de
vários threads, siga as diretrizes discutidas na próxima seção.

Acesso thread-safe
Em vez de instanciar objetos individuais Random , recomendamos que você crie uma
única Random instância para gerar todos os números aleatórios necessários para seu
aplicativo. No entanto, Random os objetos não são thread safe. Se seu aplicativo
chamar Random métodos de vários threads, você deverá usar um objeto de
sincronização para garantir que apenas um thread possa acessar o gerador de números
aleatórios por vez. Se você não garantir que o objeto seja acessado Random de forma
segura para threads, as chamadas para métodos que retornam números aleatórios
retornarão 0.

O exemplo a seguir usa a instrução de bloqueio C#, a função de bloqueio F# ea


instrução SyncLock do Visual Basic para garantir que um único gerador de números
aleatórios seja acessado por 11 threads de maneira segura para threads. Cada thread
gera 2 milhões de números aleatórios, conta o número de números aleatórios gerados e
calcula sua soma e, em seguida, atualiza os totais de todos os threads quando termina
de executar.

C#

using System;
using System.Threading;

public class Example13


{
[ThreadStatic] static double previous = 0.0;
[ThreadStatic] static int perThreadCtr = 0;
[ThreadStatic] static double perThreadTotal = 0.0;
static CancellationTokenSource source;
static CountdownEvent countdown;
static Object randLock, numericLock;
static Random rand;
double totalValue = 0.0;
int totalCount = 0;

public Example13()
{
rand = new Random();
randLock = new Object();
numericLock = new Object();
countdown = new CountdownEvent(1);
source = new CancellationTokenSource();
}

public static void Main()


{
Example13 ex = new Example13();
Thread.CurrentThread.Name = "Main";
ex.Execute();
}

private void Execute()


{
CancellationToken token = source.Token;

for (int threads = 1; threads <= 10; threads++)


{
Thread newThread = new Thread(this.GetRandomNumbers);
newThread.Name = threads.ToString();
newThread.Start(token);
}
this.GetRandomNumbers(token);

countdown.Signal();
// Make sure all threads have finished.
countdown.Wait();
source.Dispose();

Console.WriteLine("\nTotal random numbers generated: {0:N0}",


totalCount);
Console.WriteLine("Total sum of all random numbers: {0:N2}",
totalValue);
Console.WriteLine("Random number mean: {0:N4}", totalValue /
totalCount);
}

private void GetRandomNumbers(Object o)


{
CancellationToken token = (CancellationToken)o;
double result = 0.0;
countdown.AddCount(1);

try
{
for (int ctr = 0; ctr < 2000000; ctr++)
{
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested();

lock (randLock)
{
result = rand.NextDouble();
}
// Check for corruption of Random instance.
if ((result == previous) && result == 0)
{
source.Cancel();
}
else
{
previous = result;
}
perThreadCtr++;
perThreadTotal += result;
}

Console.WriteLine("Thread {0} finished execution.",


Thread.CurrentThread.Name);
Console.WriteLine("Random numbers generated: {0:N0}",
perThreadCtr);
Console.WriteLine("Sum of random numbers: {0:N2}",
perThreadTotal);
Console.WriteLine("Random number mean: {0:N4}\n", perThreadTotal
/ perThreadCtr);

// Update overall totals.


lock (numericLock)
{
totalCount += perThreadCtr;
totalValue += perThreadTotal;
}
}
catch (OperationCanceledException e)
{
Console.WriteLine("Corruption in Thread {1}", e.GetType().Name,
Thread.CurrentThread.Name);
}
finally
{
countdown.Signal();
}
}
}
// The example displays output like the following:
// Thread 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,491.05
// Random number mean: 0.5002
//
// Thread 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,329.64
// Random number mean: 0.4997
//
// Thread 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,166.89
// Random number mean: 0.5001
//
// Thread 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,628.37
// Random number mean: 0.4998
//
// Thread Main finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,920.89
// Random number mean: 0.5000
//
// Thread 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,370.45
// Random number mean: 0.4997
//
// Thread 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,330.92
// Random number mean: 0.4997
//
// Thread 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,172.79
// Random number mean: 0.5001
//
// Thread 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,079.43
// Random number mean: 0.5000
//
// Thread 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,817.91
// Random number mean: 0.4999
//
// Thread 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,930.63
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 10,998,238.98
// Random number mean: 0.4999

O exemplo garante a segurança dos threads das seguintes maneiras:

O ThreadStaticAttribute atributo é usado para definir variáveis de thread-local que


rastreiam o número total de números aleatórios gerados e sua soma para cada
thread.
Um bloqueio (a instrução em C#, a função em F# e a instrução no Visual Basic)
protege o acesso às variáveis para a contagem total e a lock lock SyncLock soma
de todos os números aleatórios gerados em todos os threads.
Um semáforo (o objeto) é usado para garantir que o CountdownEvent thread
principal seja bloqueado até que todos os outros threads concluam a execução.
O exemplo verifica se o gerador de números aleatórios foi corrompido,
determinando se duas chamadas consecutivas para métodos de geração de
números aleatórios retornam 0. Se a corrupção for detectada, o exemplo usará o
CancellationTokenSource objeto para sinalizar que todos os threads devem ser
cancelados.
Antes de gerar cada número aleatório, cada thread verifica o CancellationToken
estado do objeto. Se o cancelamento for solicitado, o exemplo chamará o método
para cancelar o CancellationToken.ThrowIfCancellationRequested thread.

O exemplo a seguir é idêntico ao primeiro, exceto que ele usa um Task objeto e uma
expressão lambda em vez de Thread objetos.

C#

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

public class Example15


{
static Object randLock, numericLock;
static Random rand;
static CancellationTokenSource source;
double totalValue = 0.0;
int totalCount = 0;

public Example15()
{
rand = new Random();
randLock = new Object();
numericLock = new Object();
source = new CancellationTokenSource();
}

public static async Task Main()


{
Example15 ex = new Example15();
Thread.CurrentThread.Name = "Main";
await ex.Execute();
}

private async Task Execute()


{
List<Task> tasks = new List<Task>();

for (int ctr = 0; ctr <= 10; ctr++)


{
CancellationToken token = source.Token;
int taskNo = ctr;
tasks.Add(Task.Run(() =>
{
double previous = 0.0;
int taskCtr = 0;
double taskTotal = 0.0;
double result = 0.0;

for (int n = 0; n < 2000000; n++)


{
// Make sure there's no corruption of Random.
token.ThrowIfCancellationRequested();

lock (randLock)
{
result = rand.NextDouble();
}
// Check for corruption of Random instance.
if ((result == previous) && result == 0)
{
source.Cancel();
}
else
{
previous = result;
}
taskCtr++;
taskTotal += result;
}

// Show result.
Console.WriteLine("Task {0} finished execution.",
taskNo);
Console.WriteLine("Random numbers generated: {0:N0}",
taskCtr);
Console.WriteLine("Sum of random numbers: {0:N2}",
taskTotal);
Console.WriteLine("Random number mean: {0:N4}\n",
taskTotal / taskCtr);

// Update overall totals.


lock (numericLock)
{
totalCount += taskCtr;
totalValue += taskTotal;
}
},
token));
}
try
{
await Task.WhenAll(tasks.ToArray());
Console.WriteLine("\nTotal random numbers generated: {0:N0}",
totalCount);
Console.WriteLine("Total sum of all random numbers: {0:N2}",
totalValue);
Console.WriteLine("Random number mean: {0:N4}", totalValue /
totalCount);
}
catch (AggregateException e)
{
foreach (Exception inner in e.InnerExceptions)
{
TaskCanceledException canc = inner as TaskCanceledException;
if (canc != null)
Console.WriteLine("Task #{0} cancelled.", canc.Task.Id);
else
Console.WriteLine("Exception: {0}",
inner.GetType().Name);
}
}
finally
{
source.Dispose();
}
}
}
// The example displays output like the following:
// Task 1 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,502.47
// Random number mean: 0.5003
//
// Task 0 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,445.63
// Random number mean: 0.5002
//
// Task 2 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,556.04
// Random number mean: 0.5003
//
// Task 3 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,178.87
// Random number mean: 0.5001
//
// Task 4 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,819.17
// Random number mean: 0.4999
//
// Task 5 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,190.58
// Random number mean: 0.5001
//
// Task 6 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,720.21
// Random number mean: 0.4999
//
// Task 7 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,000.96
// Random number mean: 0.4995
//
// Task 8 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,499.33
// Random number mean: 0.4997
//
// Task 9 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 1,000,193.25
// Random number mean: 0.5001
//
// Task 10 finished execution.
// Random numbers generated: 2,000,000
// Sum of random numbers: 999,960.82
// Random number mean: 0.5000
//
//
// Total random numbers generated: 22,000,000
// Total sum of all random numbers: 11,000,067.33
// Random number mean: 0.5000

Ele difere do primeiro exemplo das seguintes maneiras:

As variáveis para acompanhar o número de números aleatórios gerados e sua


soma em cada tarefa são locais para a tarefa, portanto, não há necessidade de usar
o ThreadStaticAttribute atributo.
O método estático Task.WaitAll é usado para garantir que o thread principal não
seja concluído antes que todas as tarefas tenham sido concluídas. Não há
necessidade do CountdownEvent objeto.
A exceção resultante do cancelamento da tarefa aparece no Task.WaitAll método.
No exemplo anterior, ele é manipulado por cada thread.

Gerar diferentes tipos de números aleatórios


O gerador de números aleatórios fornece métodos que permitem gerar os seguintes
tipos de números aleatórios:

Uma série de Byte valores. Você determina o número de valores de byte passando
uma matriz inicializada para o número de elementos que você deseja que o
método retorne ao NextBytes método. O exemplo a seguir gera 20 bytes.

C#

Random rnd = new Random();


Byte[] bytes = new Byte[20];
rnd.NextBytes(bytes);
for (int ctr = 1; ctr <= bytes.Length; ctr++)
{
Console.Write("{0,3} ", bytes[ctr - 1]);
if (ctr % 10 == 0) Console.WriteLine();
}

// The example displays output like the following:


// 141 48 189 66 134 212 211 71 161 56
// 181 166 220 133 9 252 222 57 62 62

Um único inteiro. Você pode escolher se deseja um inteiro de 0 a um valor máximo


(Int32.MaxValue - 1) chamando o método, um inteiro entre 0 e um valor específico
chamando o método ou um inteiro dentro de um intervalo de valores chamando o
Next()Next(Int32)Next(Int32, Int32) método. Nas sobrecargas parametrizadas, o
valor máximo especificado é exclusivo; ou seja, o número máximo real gerado é
um a menos que o valor especificado.

O exemplo a seguir chama o Next(Int32, Int32) método para gerar 10 números


aleatórios entre -10 e 10. Observe que o segundo argumento para o método
especifica o limite superior exclusivo do intervalo de valores aleatórios retornados
pelo método. Em outras palavras, o maior inteiro que o método pode retornar é
um a menos que esse valor.

C#

Random rnd = new Random();


for (int ctr = 0; ctr < 10; ctr++)
{
Console.Write("{0,3} ", rnd.Next(-10, 11));
}

// The example displays output like the following:


// 2 9 -3 2 4 -7 -3 -8 -8 5

Um único valor de ponto flutuante de 0,0 a menos de 1,0 chamando o NextDouble


método. O limite superior exclusivo do número aleatório retornado pelo método é
1, portanto, seu limite superior real é 0,999999999999978. O exemplo a seguir gera
10 números aleatórios de ponto flutuante.

C#

Random rnd = new Random();


for (int ctr = 0; ctr < 10; ctr++)
{
Console.Write("{0,-19:R} ", rnd.NextDouble());
if ((ctr + 1) % 3 == 0) Console.WriteLine();
}

// The example displays output like the following:


// 0.7911680553998649 0.0903414949264105 0.79776258291572455
// 0.615568345233597 0.652644504165577 0.84023809378977776
// 0.099662564741290441 0.91341467383942321 0.96018602045261581
// 0.74772306473354022

) Importante

O Next(Int32, Int32) método permite especificar o intervalo do número aleatório


retornado. No entanto, o parâmetro, que especifica o maxValue número retornado
do intervalo superior, é um valor exclusivo, não inclusivo. Isso significa que a
chamada Next(0, 100) de método retorna um valor entre 0 e 99, e não entre 0 e
100.

Você também pode usar a Random classe para tarefas como gerar valores booleanos
aleatórios, gerar valores de ponto flutuante aleatórios em um intervalo especificado,
gerar inteiros aleatórios de 64 bits e recuperar um elemento exclusivo de uma matriz ou
coleção.

Substitua seu próprio algoritmo


Você pode implementar seu próprio gerador de números aleatórios herdando da
Random classe e fornecendo seu algoritmo de geração de números aleatórios. Para
fornecer seu próprio algoritmo, você deve substituir o método, que implementa o
Sample algoritmo de geração de números aleatórios. Você também deve substituir os
Next()métodos , Next(Int32, Int32)e para garantir que eles chamem NextBytes seu
método substituído Sample . Você não precisa substituir os Next(Int32) métodos e
NextDouble .

Para obter um exemplo que deriva Random da classe e modifica seu gerador de
números pseudoaleatórios padrão, consulte a Sample página de referência.

Recuperar a mesma sequência de valores


aleatórios
Às vezes, você deseja gerar a mesma sequência de números aleatórios em cenários de
teste de software e no jogo. O teste com a mesma sequência de números aleatórios
permite detectar regressões e confirmar correções de bugs. Usar a mesma sequência de
números aleatórios em jogos permite que você repita jogos anteriores.

Você pode gerar a mesma sequência de números aleatórios fornecendo o mesmo valor
de semente para o Random(Int32) construtor. O valor de semente fornece um valor
inicial para o algoritmo de geração de números pseudoaleatórios. O exemplo a seguir
usa 100100 como um valor de propagação arbitrário para instanciar o objeto, exibe 20
valores de ponto flutuante aleatórios e persiste o Random valor de propagação. Em
seguida, restaura o valor de semente, instancia um novo gerador de números aleatórios
e exibe os mesmos 20 valores de ponto flutuante aleatórios. Observe que o exemplo
pode produzir sequências diferentes de números aleatórios se executado em versões
diferentes do .NET.

C#

using System;
using System.IO;

public class Example12


{
public static void Main()
{
int seed = 100100;
ShowRandomNumbers(seed);
Console.WriteLine();

PersistSeed(seed);

DisplayNewRandomNumbers();
}

private static void ShowRandomNumbers(int seed)


{
Random rnd = new Random(seed);
for (int ctr = 0; ctr <= 20; ctr++)
Console.WriteLine(rnd.NextDouble());
}

private static void PersistSeed(int seed)


{
FileStream fs = new FileStream(@".\seed.dat", FileMode.Create);
BinaryWriter bin = new BinaryWriter(fs);
bin.Write(seed);
bin.Close();
}

private static void DisplayNewRandomNumbers()


{
FileStream fs = new FileStream(@".\seed.dat", FileMode.Open);
BinaryReader bin = new BinaryReader(fs);
int seed = bin.ReadInt32();
bin.Close();

Random rnd = new Random(seed);


for (int ctr = 0; ctr <= 20; ctr++)
Console.WriteLine(rnd.NextDouble());
}
}
// The example displays output like the following:
// 0.500193602172748
// 0.0209461245783354
// 0.465869495396442
// 0.195512794514891
// 0.928583675496552
// 0.729333720509584
// 0.381455668891527
// 0.0508996467343064
// 0.019261200921266
// 0.258578445417145
// 0.0177532266908107
// 0.983277184415272
// 0.483650274334313
// 0.0219647376900375
// 0.165910115077118
// 0.572085966622497
// 0.805291457942357
// 0.927985211335116
// 0.4228545699375
// 0.523320379910674
// 0.157783938645285
//
// 0.500193602172748
// 0.0209461245783354
// 0.465869495396442
// 0.195512794514891
// 0.928583675496552
// 0.729333720509584
// 0.381455668891527
// 0.0508996467343064
// 0.019261200921266
// 0.258578445417145
// 0.0177532266908107
// 0.983277184415272
// 0.483650274334313
// 0.0219647376900375
// 0.165910115077118
// 0.572085966622497
// 0.805291457942357
// 0.927985211335116
// 0.4228545699375
// 0.523320379910674
// 0.157783938645285

Recuperar sequências exclusivas de números


aleatórios
Fornecer valores de semente diferentes para instâncias da classe faz com que cada
gerador de Random números aleatórios produza uma sequência diferente de valores.
Você pode fornecer um valor de semente explicitamente chamando o construtor ou
implicitamente chamando o Random(Int32)Random() construtor. A maioria dos
desenvolvedores chama o construtor sem parâmetros, que usa o relógio do sistema. O
exemplo a seguir usa essa abordagem para instanciar duas Random instâncias. Cada
instância exibe uma série de 10 inteiros aleatórios.

C#

using System;
using System.Threading;

public class Example16


{
public static void Main()
{
Console.WriteLine("Instantiating two random number generators...");
Random rnd1 = new Random();
Thread.Sleep(2000);
Random rnd2 = new Random();

Console.WriteLine("\nThe first random number generator:");


for (int ctr = 1; ctr <= 10; ctr++)
Console.WriteLine(" {0}", rnd1.Next());

Console.WriteLine("\nThe second random number generator:");


for (int ctr = 1; ctr <= 10; ctr++)
Console.WriteLine(" {0}", rnd2.Next());
}
}
// The example displays output like the following:
// Instantiating two random number generators...
//
// The first random number generator:
// 643164361
// 1606571630
// 1725607587
// 2138048432
// 496874898
// 1969147632
// 2034533749
// 1840964542
// 412380298
// 47518930
//
// The second random number generator:
// 1251659083
// 1514185439
// 1465798544
// 517841554
// 1821920222
// 195154223
// 1538948391
// 1548375095
// 546062716
// 897797880

No entanto, devido à sua resolução finita, o relógio do sistema não detecta diferenças
de tempo inferiores a aproximadamente 15 milissegundos. Portanto, se seu código
chama a Random() sobrecarga no .NET Framework para instanciar dois Random objetos
em sucessão, você pode inadvertidamente estar fornecendo os objetos com valores de
semente idênticos. (A Random classe no .NET Core não tem essa limitação.) Para ver isso
no exemplo anterior, comente a chamada do Thread.Sleep método e compile e execute
o exemplo novamente.

Para evitar que isso aconteça, recomendamos que você instancie um único Random
objeto em vez de vários. No entanto, como Random não é seguro para threads, você
deve usar algum dispositivo de sincronização se acessar uma Random instância de
vários threads, para obter mais informações, consulte a seção Segurança de threads .
Como alternativa, você pode usar um mecanismo de atraso, como o Sleep método
usado no exemplo anterior, para garantir que as instanciações ocorram com mais de 15
milissegundos de diferença.

Recuperar inteiros em um intervalo


especificado
Você pode recuperar inteiros em um intervalo especificado chamando o método, que
permite especificar o limite inferior e superior dos números que você gostaria que o
Next(Int32, Int32) gerador de números aleatórios retornasse. O limite superior é um
valor exclusivo, não inclusivo. Ou seja, ele não está incluído no intervalo de valores
retornados pelo método. O exemplo a seguir usa esse método para gerar inteiros
aleatórios entre -10 e 10. Observe que ele especifica 11, que é um maior do que o valor
desejado, como o valor do argumento na chamada de maxValue método.

C#

Random rnd = new Random();


for (int ctr = 1; ctr <= 15; ctr++)
{
Console.Write("{0,3} ", rnd.Next(-10, 11));
if (ctr % 5 == 0) Console.WriteLine();
}

// The example displays output like the following:


// -2 -5 -1 -2 10
// -3 6 -4 -8 3
// -7 10 5 -2 4
Recuperar inteiros com um número
especificado de dígitos
Você pode chamar o Next(Int32, Int32) método para recuperar números com um
número especificado de dígitos. Por exemplo, para recuperar números com quatro
dígitos (ou seja, números que variam de 1000 a 9999), você chama o método com um
valor de 1000 e um minValue maxValue valor de 10000, como mostra o Next(Int32, Int32)
exemplo a seguir.

C#

Random rnd = new Random();


for (int ctr = 1; ctr <= 50; ctr++)
{
Console.Write("{0,3} ", rnd.Next(1000, 10000));
if (ctr % 10 == 0) Console.WriteLine();
}

// The example displays output like the following:


// 9570 8979 5770 1606 3818 4735 8495 7196 7070
2313
// 5279 6577 5104 5734 4227 3373 7376 6007 8193
5540
// 7558 3934 3819 7392 1113 7191 6947 4963 9179
7907
// 3391 6667 7269 1838 7317 1981 5154 7377 3297
5320
// 9869 8694 2684 4949 2999 3019 2357 5211 9604
2593

Recuperar valores de ponto flutuante em um


intervalo especificado
O NextDouble método retorna valores de ponto flutuante aleatórios que variam de 0 a
menos de 1. No entanto, muitas vezes você desejará gerar valores aleatórios em algum
outro intervalo.

Se o intervalo entre os valores mínimo e máximo desejados for 1, você poderá adicionar
a diferença entre o intervalo inicial desejado e 0 ao número retornado pelo NextDouble
método. O exemplo a seguir faz isso para gerar 10 números aleatórios entre -1 e 0.

C#

Random rnd = new Random();


for (int ctr = 1; ctr <= 10; ctr++)
Console.WriteLine(rnd.NextDouble() - 1);

// The example displays output like the following:


// -0.930412760437658
// -0.164699016215605
// -0.9851692803135
// -0.43468508843085
// -0.177202483255976
// -0.776813320245972
// -0.0713201854710096
// -0.0912875561468711
// -0.540621722368813
// -0.232211863730201

Para gerar números aleatórios de ponto flutuante cujo limite inferior é 0, mas o limite
superior é maior que 1 (ou, no caso de números negativos, cujo limite inferior é menor
que -1 e limite superior é 0), multiplique o número aleatório pelo limite diferente de
zero. O exemplo a seguir faz isso para gerar 20 milhões de números aleatórios de ponto
flutuante que variam de 0 a Int64.MaxValue. Em também exibe a distribuição dos valores
aleatórios gerados pelo método.

C#

const long ONE_TENTH = 922337203685477581;

Random rnd = new Random();


double number;
int[] count = new int[10];

// Generate 20 million integer values between.


for (int ctr = 1; ctr <= 20000000; ctr++)
{
number = rnd.NextDouble() * Int64.MaxValue;
// Categorize random numbers into 10 groups.
count[(int)(number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}", ctr *
ONE_TENTH,
ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 :
Int64.MaxValue,
count[ctr], count[ctr] / 20000000.0);

// The example displays output like the following:


// Range Count
Pct.
//
// 0- 922,337,203,685,477,580 1,996,148 9.98
%
// 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00
%
// 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00
%
// 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00
%
// 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00
%
// 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99
%
// 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00
%
// 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01
%
// 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01
%
// 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00
%

Para gerar números aleatórios de ponto flutuante entre dois valores arbitrários, como o
Next(Int32, Int32) método faz para inteiros, use a seguinte fórmula:

C#

Random.NextDouble() * (maxValue - minValue) + minValue

O exemplo a seguir gera 1 milhão de números aleatórios que variam de 10,0 a 11,0 e
exibe sua distribuição.

C#

Random rnd = new Random();


int lowerBound = 10;
int upperBound = 11;
int[] range = new int[10];
for (int ctr = 1; ctr <= 1000000; ctr++)
{
Double value = rnd.NextDouble() * (upperBound - lowerBound) +
lowerBound;
range[(int)Math.Truncate((value - lowerBound) * 10)]++;
}

for (int ctr = 0; ctr <= 9; ctr++)


{
Double lowerRange = 10 + ctr * .1;
Console.WriteLine("{0:N1} to {1:N1}: {2,8:N0} ({3,7:P2})",
lowerRange, lowerRange + .1, range[ctr],
range[ctr] / 1000000.0);
}

// The example displays output like the following:


// 10.0 to 10.1: 99,929 ( 9.99 %)
// 10.1 to 10.2: 100,189 (10.02 %)
// 10.2 to 10.3: 99,384 ( 9.94 %)
// 10.3 to 10.4: 100,240 (10.02 %)
// 10.4 to 10.5: 99,397 ( 9.94 %)
// 10.5 to 10.6: 100,580 (10.06 %)
// 10.6 to 10.7: 100,293 (10.03 %)
// 10.7 to 10.8: 100,135 (10.01 %)
// 10.8 to 10.9: 99,905 ( 9.99 %)
// 10.9 to 11.0: 99,948 ( 9.99 %)

Gerar valores booleanos aleatórios


A Random classe não fornece métodos que geram Boolean valores. No entanto, você
pode definir sua própria classe ou método para fazer isso. O exemplo a seguir define
uma classe, , com um único método, BooleanGenerator NextBoolean . A BooleanGenerator
classe armazena um Random objeto como uma variável privada. O NextBoolean método
chama o método e passa o resultado para o
Convert.ToBoolean(Int32)Random.Next(Int32, Int32) método. Observe que 2 é usado
como argumento para especificar o limite superior do número aleatório. Como esse é
um valor exclusivo, a chamada de método retorna 0 ou 1.

C#

using System;

public class Example1


{
public static void Main()
{
// Instantiate the Boolean generator.
BooleanGenerator boolGen = new BooleanGenerator();
int totalTrue = 0, totalFalse = 0;

// Generate 1,0000 random Booleans, and keep a running total.


for (int ctr = 0; ctr < 1000000; ctr++)
{
bool value = boolGen.NextBoolean();
if (value)
totalTrue++;
else
totalFalse++;
}
Console.WriteLine("Number of true values: {0,7:N0} ({1:P3})",
totalTrue,
((double)totalTrue) / (totalTrue + totalFalse));
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
totalFalse,
((double)totalFalse) / (totalTrue + totalFalse));
}
}

public class BooleanGenerator


{
Random rnd;

public BooleanGenerator()
{
rnd = new Random();
}

public bool NextBoolean()


{
return rnd.Next(0, 2) == 1;
}
}
// The example displays output like the following:
// Number of true values: 500,004 (50.000 %)
// Number of false values: 499,996 (50.000 %)

Em vez de criar uma classe separada para gerar valores aleatórios Boolean , o exemplo
poderia simplesmente ter definido um único método. Nesse caso, no entanto, o
Random objeto deve ter sido definido como uma variável de nível de classe para evitar
instanciar uma nova Random instância em cada chamada de método. No Visual Basic, a
instância Random pode ser definida como uma variável estática no NextBoolean
método. O exemplo a seguir fornece uma implementação.

C#

Random rnd = new Random();

int totalTrue = 0, totalFalse = 0;

// Generate 1,000,000 random Booleans, and keep a running total.


for (int ctr = 0; ctr < 1000000; ctr++)
{
bool value = NextBoolean();
if (value)
totalTrue++;
else
totalFalse++;
}
Console.WriteLine("Number of true values: {0,7:N0} ({1:P3})",
totalTrue,
((double)totalTrue) / (totalTrue + totalFalse));
Console.WriteLine("Number of false values: {0,7:N0} ({1:P3})",
totalFalse,
((double)totalFalse) / (totalTrue + totalFalse));

bool NextBoolean()
{
return rnd.Next(0, 2) == 1;
}

// The example displays output like the following:


// Number of true values: 499,777 (49.978 %)
// Number of false values: 500,223 (50.022 %)

Gerar inteiros aleatórios de 64 bits


As sobrecargas do Next método retornam inteiros de 32 bits. No entanto, em alguns
casos, convém trabalhar com inteiros de 64 bits. Você pode fazer isso da seguinte
maneira:

1. Chame o NextDouble método para recuperar um valor de ponto flutuante de


precisão dupla.

2. Multiplique esse valor por Int64.MaxValue.

O exemplo a seguir usa essa técnica para gerar 20 milhões de inteiros longos aleatórios
e os categoriza em 10 grupos iguais. Em seguida, avalia a distribuição dos números
aleatórios contando o número em cada grupo de 0 a Int64.MaxValue. Como mostra a
saída do exemplo, os números são distribuídos mais ou menos igualmente através do
intervalo de um inteiro longo.

C#

const long ONE_TENTH = 922337203685477581;

Random rnd = new Random();


long number;
int[] count = new int[10];

// Generate 20 million long integers.


for (int ctr = 1; ctr <= 20000000; ctr++)
{
number = (long)(rnd.NextDouble() * Int64.MaxValue);
// Categorize random numbers.
count[(int)(number / ONE_TENTH)]++;
}
// Display breakdown by range.
Console.WriteLine("{0,28} {1,32} {2,7}\n", "Range", "Count", "Pct.");
for (int ctr = 0; ctr <= 9; ctr++)
Console.WriteLine("{0,25:N0}-{1,25:N0} {2,8:N0} {3,7:P2}", ctr *
ONE_TENTH,
ctr < 9 ? ctr * ONE_TENTH + ONE_TENTH - 1 :
Int64.MaxValue,
count[ctr], count[ctr] / 20000000.0);
// The example displays output like the following:
// Range Count
Pct.
//
// 0- 922,337,203,685,477,580 1,996,148 9.98
%
// 922,337,203,685,477,581-1,844,674,407,370,955,161 2,000,293 10.00
%
// 1,844,674,407,370,955,162-2,767,011,611,056,432,742 2,000,094 10.00
%
// 2,767,011,611,056,432,743-3,689,348,814,741,910,323 2,000,159 10.00
%
// 3,689,348,814,741,910,324-4,611,686,018,427,387,904 1,999,552 10.00
%
// 4,611,686,018,427,387,905-5,534,023,222,112,865,485 1,998,248 9.99
%
// 5,534,023,222,112,865,486-6,456,360,425,798,343,066 2,000,696 10.00
%
// 6,456,360,425,798,343,067-7,378,697,629,483,820,647 2,001,637 10.01
%
// 7,378,697,629,483,820,648-8,301,034,833,169,298,228 2,002,870 10.01
%
// 8,301,034,833,169,298,229-9,223,372,036,854,775,807 2,000,303 10.00
%

Uma técnica alternativa que usa manipulação de bits não gera números
verdadeiramente aleatórios. Essa técnica chama Next() para gerar dois inteiros,
deslocamentos para a esquerda um por 32 bits e OU eles juntos. Esta técnica tem duas
limitações:

1. Como o bit 31 é o bit de sinal, o valor no bit 31 do inteiro longo resultante é


sempre 0. Isso pode ser resolvido gerando um 0 ou 1 aleatório, deslocando-o para
a esquerda em 31 bits e ORando-o com o inteiro longo aleatório original.

2. Mais grave, porque a probabilidade de que o valor retornado por Next() será 0,
haverá poucos ou nenhum número aleatório no intervalo 0x0-
0x00000000FFFFFFFF.

Recuperar bytes em um intervalo especificado


As sobrecargas do Next método permitem que você especifique o intervalo de números
aleatórios, mas o NextBytes método não. O exemplo a seguir implementa um NextBytes
método que permite especificar o intervalo dos bytes retornados. Ele define uma
Random2 classe que deriva e Random sobrecarrega seu NextBytes método.

C#
using System;

public class Example3


{
public static void Main()
{
Random2 rnd = new Random2();
Byte[] bytes = new Byte[10000];
int[] total = new int[101];
rnd.NextBytes(bytes, 0, 101);

// Calculate how many of each value we have.


foreach (var value in bytes)
total[value]++;

// Display the results.


for (int ctr = 0; ctr < total.Length; ctr++)
{
Console.Write("{0,3}: {1,-3} ", ctr, total[ctr]);
if ((ctr + 1) % 5 == 0) Console.WriteLine();
}
}
}

public class Random2 : Random


{
public Random2() : base()
{ }

public Random2(int seed) : base(seed)


{ }

public void NextBytes(byte[] bytes, byte minValue, byte maxValue)


{
for (int ctr = bytes.GetLowerBound(0); ctr <=
bytes.GetUpperBound(0); ctr++)
bytes[ctr] = (byte)Next(minValue, maxValue);
}
}
// The example displays output like the following:
// 0: 115 1: 119 2: 92 3: 98 4: 92
// 5: 102 6: 103 7: 84 8: 93 9: 116
// 10: 91 11: 98 12: 106 13: 91 14: 92
// 15: 101 16: 100 17: 96 18: 97 19: 100
// 20: 101 21: 106 22: 112 23: 82 24: 85
// 25: 102 26: 107 27: 98 28: 106 29: 102
// 30: 109 31: 108 32: 94 33: 101 34: 107
// 35: 101 36: 86 37: 100 38: 101 39: 102
// 40: 113 41: 95 42: 96 43: 89 44: 99
// 45: 81 46: 89 47: 105 48: 100 49: 85
// 50: 103 51: 103 52: 93 53: 89 54: 91
// 55: 97 56: 105 57: 97 58: 110 59: 86
// 60: 116 61: 94 62: 117 63: 98 64: 110
// 65: 93 66: 102 67: 100 68: 105 69: 83
// 70: 81 71: 97 72: 85 73: 70 74: 98
// 75: 100 76: 110 77: 114 78: 83 79: 90
// 80: 96 81: 112 82: 102 83: 102 84: 99
// 85: 81 86: 100 87: 93 88: 99 89: 118
// 90: 95 91: 124 92: 108 93: 96 94: 104
// 95: 106 96: 99 97: 99 98: 92 99: 99
// 100: 108

O NextBytes(Byte[], Byte, Byte) método encapsula uma chamada para o método e


especifica o valor mínimo e um maior que o Next(Int32, Int32) valor máximo (neste caso,
0 e 101) que queremos retornar na matriz de bytes. Como temos certeza de que os
valores inteiros retornados pelo Next método estão dentro do intervalo do tipo de
dados, podemos convertê-los com segurança (em C# e F#) ou convertê-los (no Visual
Basic) de Byte inteiros em bytes.

Recuperar um elemento de uma matriz ou


coleção aleatoriamente
Números aleatórios geralmente servem como índices para recuperar valores de matrizes
ou coleções. Para recuperar um valor de índice aleatório, você pode chamar o método e
usar o limite inferior da matriz como o valor de seu argumento e um maior que o limite
superior da matriz como o Next(Int32, Int32) valor de seu minValue maxValue
argumento. Para uma matriz baseada em zero, isso é equivalente à sua Length
propriedade ou um valor maior que o valor retornado pelo Array.GetUpperBound
método. O exemplo a seguir recupera aleatoriamente o nome de uma cidade nos
Estados Unidos de uma matriz de cidades.

C#

String[] cities = { "Atlanta", "Boston", "Chicago", "Detroit",


"Fort Wayne", "Greensboro", "Honolulu", "Indianapolis",
"Jersey City", "Kansas City", "Los Angeles",
"Milwaukee", "New York", "Omaha", "Philadelphia",
"Raleigh", "San Francisco", "Tulsa", "Washington" };
Random rnd = new Random();
int index = rnd.Next(0, cities.Length);
Console.WriteLine("Today's city of the day: {0}",
cities[index]);

// The example displays output like the following:


// Today's city of the day: Honolulu
Recuperar um elemento exclusivo de uma
matriz ou coleção
Um gerador de números aleatórios sempre pode retornar valores duplicados. À medida
que o intervalo de números se torna menor ou o número de valores gerados se torna
maior, a probabilidade de duplicatas aumenta. Se os valores aleatórios devem ser
exclusivos, mais números são gerados para compensar duplicatas, resultando em um
desempenho cada vez mais ruim.

Há uma série de técnicas para lidar com esse cenário. Uma solução comum é criar uma
matriz ou coleção que contenha os valores a serem recuperados e uma matriz paralela
que contenha números aleatórios de ponto flutuante. A segunda matriz é preenchida
com números aleatórios no momento em que a primeira matriz é criada, e o
Array.Sort(Array, Array) método é usado para classificar a primeira matriz usando os
valores na matriz paralela.

Por exemplo, se você estiver desenvolvendo um jogo Solitaire, você deseja garantir que
cada carta seja usada apenas uma vez. Em vez de gerar números aleatórios para
recuperar uma carta e rastrear se essa carta já foi distribuída, você pode criar uma
matriz paralela de números aleatórios que podem ser usados para classificar o baralho.
Depois que o baralho é classificado, seu aplicativo pode manter um ponteiro para
indicar o índice da próxima carta no baralho.

O exemplo a seguir ilustra esta abordagem. Ele define uma classe que representa uma
carta de baralho e uma Card Dealer classe que lida com um baralho de cartas
embaralhadas. O Dealer construtor de classe preenche duas matrizes: uma matriz que
tem escopo de classe e que representa todas as cartas no baralho; e uma deck matriz
local order que tem o mesmo número de elementos que a deck matriz e é preenchida
com valores gerados Double aleatoriamente. O Array.Sort(Array, Array) método é então
chamado para classificar a deck matriz com base nos valores na order matriz.

C#

using System;

// A class that represents an individual card in a playing deck.


public class Card
{
public Suit Suit;
public FaceValue FaceValue;

public override String ToString()


{
return String.Format("{0:F} of {1:F}", this.FaceValue, this.Suit);
}
}

public enum Suit { Hearts, Diamonds, Spades, Clubs };

public enum FaceValue


{
Ace = 1, Two, Three, Four, Five, Six,
Seven, Eight, Nine, Ten, Jack, Queen,
King
};

public class Dealer


{
Random rnd;
// A deck of cards, without Jokers.
Card[] deck = new Card[52];
// Parallel array for sorting cards.
Double[] order = new Double[52];
// A pointer to the next card to deal.
int ptr = 0;
// A flag to indicate the deck is used.
bool mustReshuffle = false;

public Dealer()
{
rnd = new Random();
// Initialize the deck.
int deckCtr = 0;
foreach (var suit in Enum.GetValues(typeof(Suit)))
{
foreach (var faceValue in Enum.GetValues(typeof(FaceValue)))
{
Card card = new Card();
card.Suit = (Suit)suit;
card.FaceValue = (FaceValue)faceValue;
deck[deckCtr] = card;
deckCtr++;
}
}

for (int ctr = 0; ctr < order.Length; ctr++)


order[ctr] = rnd.NextDouble();

Array.Sort(order, deck);
}

public Card[] Deal(int numberToDeal)


{
if (mustReshuffle)
{
Console.WriteLine("There are no cards left in the deck");
return null;
}
Card[] cardsDealt = new Card[numberToDeal];
for (int ctr = 0; ctr < numberToDeal; ctr++)
{
cardsDealt[ctr] = deck[ptr];
ptr++;
if (ptr == deck.Length)
mustReshuffle = true;

if (mustReshuffle & ctr < numberToDeal - 1)


{
Console.WriteLine("Can only deal the {0} cards remaining on
the deck.",
ctr + 1);
return cardsDealt;
}
}
return cardsDealt;
}
}

public class Example17


{
public static void Main()
{
Dealer dealer = new Dealer();
ShowCards(dealer.Deal(20));
}

private static void ShowCards(Card[] cards)


{
foreach (var card in cards)
if (card != null)
Console.WriteLine("{0} of {1}", card.FaceValue, card.Suit);
}
}
// The example displays output like the following:
// Six of Diamonds
// King of Clubs
// Eight of Clubs
// Seven of Clubs
// Queen of Clubs
// King of Hearts
// Three of Spades
// Ace of Clubs
// Four of Hearts
// Three of Diamonds
// Nine of Diamonds
// Two of Hearts
// Ace of Hearts
// Three of Hearts
// Four of Spades
// Eight of Hearts
// Queen of Diamonds
// Two of Clubs
// Four of Diamonds
// Jack of Hearts

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Injeção de dependência do .NET
Artigo • 15/03/2024

O .NET dá suporte ao padrão de design de software de DI (injeção de dependência), que


é uma técnica para conseguir IoC (inversão de controle) entre classes e suas
dependências. A injeção de dependência no .NET é uma parte interna da estrutura,
juntamente com a configuração, o registro em log e o padrão de opções.

Uma dependência é um objeto do qual outro objeto depende. Examine a classe


MessageWriter a seguir com um método Write do qual outras classes em um aplicativo

dependem:

C#

public class MessageWriter


{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}

Uma classe pode criar uma instância da classe MessageWriter para usar seu método
Write . No exemplo a seguir, a classe MessageWriter é uma dependência da classe

Worker :

C#

public class Worker : BackgroundService


{
private readonly MessageWriter _messageWriter = new();

protected override async Task ExecuteAsync(CancellationToken


stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_messageWriter.Write($"Worker running at:
{DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}
A classe cria e depende diretamente da instância MessageWriter . As dependências
embutidas em código (como no exemplo anterior) são problemáticas e devem ser
evitadas por estes motivos:

Para substituir MessageWriter por uma implementação diferente, a classe Worker


deve ser modificada.
Se MessageWriter tiver dependências, elas também deverão ser configuradas pela
classe Worker . Em um projeto grande com várias classes dependendo da
MessageWriter , o código de configuração fica pulverizado por todo o aplicativo.

É difícil testar a unidade dessa implementação. O aplicativo deve usar uma


simulação ou stub da classe MessageWriter , o que não é possível com essa
abordagem.

Injeção de dependência trata desses problemas da seguinte maneira:

O uso de uma interface ou classe base para abstrair a implementação da


dependência.
Registrando a dependência em um contêiner de serviço. O .NET fornece um
contêiner de serviço interno, o IServiceProvider. Normalmente, os serviços são
registrados na inicialização do aplicativo e acrescentados a um IServiceCollection.
Depois que todos os serviços forem adicionados, você usará BuildServiceProvider
para criar o contêiner de serviço.
Injeção do serviço no construtor da classe na qual ele é usado. A estrutura assume
a responsabilidade de criar uma instância da dependência e de descartá-la quando
não for mais necessária.

Por exemplo, a interface IMessageWriter define o método Write :

C#

namespace DependencyInjection.Example;

public interface IMessageWriter


{
void Write(string message);
}

Essa interface é implementada por um tipo concreto, MessageWriter :

C#

namespace DependencyInjection.Example;

public class MessageWriter : IMessageWriter


{
public void Write(string message)
{
Console.WriteLine($"MessageWriter.Write(message: \"{message}\")");
}
}

O código de amostra registra o serviço IMessageWriter com o tipo concreto


MessageWriter . O método AddSingleton registra o serviço com um tempo de vida

singleton, o tempo de vida do aplicativo. O Tempo de vida de serviço é descrito


posteriormente neste artigo.

C#

using DependencyInjection.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService<Worker>();
builder.Services.AddSingleton<IMessageWriter, MessageWriter>();

using IHost host = builder.Build();

host.Run();

No código anterior, o aplicativo de exemplo:

Cria uma instância do construtor de aplicativos do host.

Configura os serviços registrando:


O Worker como um serviço hospedado. Para obter mais informações, confira
Worker Services no .NET.
A interface IMessageWriter como um serviço singleton com uma
implementação correspondente da classe MessageWriter .

Cria o host e o executa.

O host contém o provedor de serviços de injeção de dependência. Ele também contém


todos os outros serviços relevantes necessários para criar automaticamente uma
instância de Worker e fornecer a implementação de IMessageWriter correspondente
como argumento.

C#

namespace DependencyInjection.Example;

public sealed class Worker(IMessageWriter messageWriter) : BackgroundService


{
protected override async Task ExecuteAsync(CancellationToken
stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
messageWriter.Write($"Worker running at: {DateTimeOffset.Now}");
await Task.Delay(1_000, stoppingToken);
}
}
}

Usando o padrão DI, o serviço de trabalho:

Não usa o tipo concreto MessageWriter , apenas a interface IMessageWriter que o


implementa. Isso facilita a alteração da implementação que o serviço de trabalho
usa sem modificar o serviço de trabalho.
Não cria uma instância de MessageWriter . A instância é criada pelo contêiner DI.

A implementação da interface IMessageWriter pode ser aprimorada usando a API de log


interna:

C#

namespace DependencyInjection.Example;

public class LoggingMessageWriter(


ILogger<LoggingMessageWriter> logger) : IMessageWriter
{
public void Write(string message) =>
logger.LogInformation("Info: {Msg}", message);
}

O método atualizado AddSingleton registra a nova implementação IMessageWriter :

C#

builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();

O tipo HostApplicationBuilder ( builder ) faz parte do pacote NuGet


Microsoft.Extensions.Hosting .

LoggingMessageWriter depende ILogger<TCategoryName>do que ele solicita no

construtor. ILogger<TCategoryName> é um serviço fornecido pela estrutura.

Não é incomum usar a injeção de dependência de uma maneira encadeada. Por sua vez,
cada dependência solicitada solicita suas próprias dependências. O contêiner resolve as
dependências no grafo e retorna o serviço totalmente resolvido. O conjunto de
dependências que precisa ser resolvido normalmente é chamado de árvore de
dependência, grafo de dependência ou grafo de objeto.

O contêiner resolve ILogger<TCategoryName> aproveitando os tipos abertos (genéricos),


eliminando a necessidade de registrar todo tipo construído (genérico).

Com a terminologia de injeção de dependência, um serviço:

Normalmente, é um objeto que fornece um serviço para outros objetos, como o


serviço IMessageWriter .
Não está relacionado a um serviço Web, embora o serviço possa usar um serviço
Web.

A estrutura fornece um sistema de registro em log robusto. As implementações


IMessageWriter mostradas nos exemplos anteriores foram gravadas para demonstrar a

DI básica, não para implementar o registro em log. A maioria dos aplicativos não deve
precisar escrever agentes. O código a seguir demonstra o uso do registro padrão, que
requer apenas que o Worker seja registrado como um serviço hospedado
AddHostedService:

C#

public class Worker : BackgroundService


{
private readonly ILogger<Worker> _logger;

public Worker(ILogger<Worker> logger) =>


_logger = logger;

protected override async Task ExecuteAsync(CancellationToken


stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}",
DateTimeOffset.Now);
await Task.Delay(1_000, stoppingToken);
}
}
}

Usando o código anterior, não é necessário atualizar Program.cs, pois o log é fornecido
pela estrutura.

Várias regras de descoberta de construtor


Quando um tipo define mais de um construtor, o provedor de serviços tem lógica para
determinar qual construtor usar. O construtor com mais parâmetros em que os tipos
são resolvíveis por DI é selecionado. Considere o seguinte serviço de exemplo em C#:

C#

public class ExampleService


{
public ExampleService()
{
}

public ExampleService(ILogger<ExampleService> logger)


{
// omitted for brevity
}

public ExampleService(FooService fooService, BarService barService)


{
// omitted for brevity
}
}

No código anterior, suponha que o registro em log tenha sido adicionado e seja
resolvível do provedor de serviços, mas isso não acontece com os tipos FooService e
BarService . O construtor com o parâmetro ILogger<ExampleService> é usado para

resolver a instância ExampleService . Embora haja um construtor que defina mais


parâmetros, os tipos FooService e BarService não são resolvíveis por DI.

Se houver ambiguidade ao descobrir construtores, uma exceção será gerada. Considere


o seguinte serviço de exemplo em C#:

C#

public class ExampleService


{
public ExampleService()
{
}

public ExampleService(ILogger<ExampleService> logger)


{
// omitted for brevity
}

public ExampleService(IOptions<ExampleOptions> options)


{
// omitted for brevity
}
}

2 Aviso

O código ExampleService com parâmetros de tipo solucionáveis di ambíguos


geraria uma exceção. Não faça isso — a intenção é mostrar o que se destina a
"tipos ambíguos resolvíveis por DI".

No exemplo anterior, há três construtores. O primeiro construtor é sem parâmetros e


não requer nenhum serviço do provedor de serviços. Suponha que o registro em log e
as opções tenham sido adicionados ao contêiner di e sejam serviços resolvíveis por DI.
Quando o contêiner di tentar resolver o tipo ExampleService , ele gerará uma exceção,
pois os dois construtores são ambíguos.

Você pode evitar a ambiguidade definindo um construtor que aceita ambos os tipos
resolvíveis por DI:

C#

public class ExampleService


{
public ExampleService()
{
}

public ExampleService(
ILogger<ExampleService> logger,
IOptions<ExampleOptions> options)
{
// omitted for brevity
}
}

Registrar grupos de serviços com métodos de


extensão
O Microsoft Extensions usa uma convenção para registrar um grupo de serviços
relacionados. A convenção é usar um único método de extensão Add{GROUP_NAME} para
registrar todos os serviços exigidos por um recurso de estrutura. Por exemplo, o método
de extensão AddOptions registra todos os serviços necessários para usar opções.
Serviços fornecidos pela estrutura
Ao usar qualquer um dos padrões disponíveis do host ou do construtor de aplicativos,
os padrões são aplicados e os serviços são registrados pela estrutura. Considere alguns
dos padrões mais populares do host e do construtor de aplicativos:

Host.CreateDefaultBuilder()
Host.CreateApplicationBuilder()
WebHost.CreateDefaultBuilder()
WebApplication.CreateBuilder()
WebAssemblyHostBuilder.CreateDefault
MauiApp.CreateBuilder

Depois de criar um construtor a partir de qualquer uma dessas APIs, o


IServiceCollection tem serviços definidos pela estrutura, dependendo de como o host

foi configurado. Para aplicativos com base nos modelos do .NET, a estrutura pode
registrar centenas de serviços.

A tabela a seguir lista um pequeno exemplo desses serviços registrados por estrutura:

ノ Expandir a tabela

Tipo de Serviço Tempo de vida

Microsoft.Extensions.DependencyInjection.IServiceScopeFactory Singleton

IHostApplicationLifetime Singleton

Microsoft.Extensions.Logging.ILogger<TCategoryName> Singleton

Microsoft.Extensions.Logging.ILoggerFactory Singleton

Microsoft.Extensions.ObjectPool.ObjectPoolProvider Singleton

Microsoft.Extensions.Options.IConfigureOptions<TOptions> Transitório

Microsoft.Extensions.Options.IOptions<TOptions> Singleton

System.Diagnostics.DiagnosticListener Singleton

System.Diagnostics.DiagnosticSource Singleton

Tempos de vida do serviço


Os serviços podem ser registrados com um dos seguintes tempos de vida:
Transitório
Com escopo
Singleton

As seções a seguir descrevem cada um dos tempos de vida anteriores. Escolha um


tempo de vida apropriado para cada serviço registrado.

Transitório
Serviços temporários de tempo de vida são criados cada vez que são solicitados pelo
contêiner de serviço. Para registrar um serviço como transitório, chame AddTransient.

Em aplicativos que processam solicitações, os serviços transitórios são descartados no


final da solicitação. Esse tempo de vida incorre em alocações por solicitação, pois os
serviços são resolvidos e construídos sempre. Para obter mais informações, consulte
Diretrizes de Injeção de Dependência: diretrizes IDisposable para instâncias transitórias
e compartilhadas.

Com escopo
Para aplicativos Web, um tempo de vida com escopo indica que os serviços são criados
uma vez por solicitação de cliente (conexão). Registre serviços transitórios com
AddScoped.

Em aplicativos que processam solicitações, os serviços com escopo são descartados no


final da solicitação.

Ao usar o Entity Framework Core, o método de extensão AddDbContext registra


DbContext tipos com um tempo de vida com escopo por padrão.

7 Observação

Não resolva um serviço com escopo de um singleton e tome cuidado para não
fazê-lo indiretamente, por exemplo, por meio de um serviço temporário. Pode fazer
com que o serviço tenha um estado incorreto durante o processamento das
solicitações seguintes. É possível:

Resolver um serviço singleton de um serviço com escopo ou transitório.


Resolver um serviço em escopo de outro serviço com escopo ou transitório.

Por padrão, no ambiente de desenvolvimento, resolver um serviço de outro serviço com


um tempo de vida mais longo gera uma exceção. Para obter mais informações, confira
Validação de escopo.

Singleton
Os serviços de tempo de vida singleton são criados:

A primeira vez que eles são solicitados.


Pelo desenvolvedor, ao fornecer uma instância de implementação diretamente
para o contêiner. Essa abordagem raramente é necessária.

Cada solicitação subsequente da implementação do serviço do contêiner de injeção de


dependência usa a mesma instância. Se o aplicativo exigir comportamento singleton,
permita que o contêiner de serviço gerencie a vida útil do serviço. Não implemente o
padrão de design singleton e forneça código para descartar o singleton. Os serviços
nunca devem ser descartados pelo código que resolveu o serviço do contêiner. Se um
tipo ou um alocador for registrado como singleton, o contêiner descartará o singleton
automaticamente.

Registrar serviços singleton com AddSingleton. Os serviços singleton devem ser thread
safe e geralmente são usados em serviços sem estado.

Em aplicativos que processam solicitações, os serviços singleton são descartados


quando ServiceProvider é descartado no desligamento do aplicativo. Como a memória
não é liberada até que o aplicativo seja desligado, considere o uso de memória com um
serviço singleton.

Métodos de registro do serviço


A estrutura fornece métodos de extensão de registro de serviço que são úteis em
cenários específicos:

ノ Expandir a tabela

Método Automática Vários Passar


objeto implementações argumentos
descarte

Add{LIFETIME}<{SERVICE}, Sim Sim Não


{IMPLEMENTATION}>()

Exemplo:

services.AddSingleton<IMyDep, MyDep>();
Método Automática Vários Passar
objeto implementações argumentos
descarte

Add{LIFETIME}<{SERVICE}>(sp => new Sim Sim Sim


{IMPLEMENTATION})

Exemplos:

services.AddSingleton<IMyDep>(sp => new


MyDep());
services.AddSingleton<IMyDep>(sp => new
MyDep(99));

Add{LIFETIME}<{IMPLEMENTATION}>() Sim Não Não

Exemplo:

services.AddSingleton<MyDep>();

AddSingleton<{SERVICE}>(new No Sim Yes


{IMPLEMENTATION})

Exemplos:

services.AddSingleton<IMyDep>(new
MyDep());
services.AddSingleton<IMyDep>(new
MyDep(99));

AddSingleton(new {IMPLEMENTATION}) No No Sim

Exemplos:

services.AddSingleton(new MyDep());
services.AddSingleton(new MyDep(99));

Para obter mais informações sobre o descarte de tipos, consulte a seção Descarte de
serviços.

Registrar um serviço com apenas um tipo de implementação equivale a registrar esse


serviço com o mesmo tipo de implementação e serviço. É por isso que várias
implementações de um serviço não podem ser registradas usando os métodos que não
usam um tipo de serviço explícito. Esses métodos podem registrar várias instâncias de
um serviço, mas todos eles terão o mesmo tipo de implementação.
Qualquer um dos métodos de registro de serviço acima pode ser usado para registrar
várias instâncias de serviço do mesmo tipo de serviço. No exemplo a seguir,
AddSingleton é chamado duas vezes com IMessageWriter como tipo de serviço. A

segunda chamada para AddSingleton substitui a anterior quando resolvida como


IMessageWriter e adiciona à anterior quando vários serviços são resolvidos por meio de

IEnumerable<IMessageWriter> . Os serviços aparecem na ordem em que foram

registrados quando resolvidos por meio de IEnumerable<{SERVICE}> .

C#

using ConsoleDI.IEnumerableExample;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
builder.Services.AddSingleton<IMessageWriter, LoggingMessageWriter>();
builder.Services.AddSingleton<ExampleService>();

using IHost host = builder.Build();

_ = host.Services.GetService<ExampleService>();

await host.RunAsync();

O código-fonte de exemplo anterior registra duas implementações do IMessageWriter .

C#

using System.Diagnostics;

namespace ConsoleDI.IEnumerableExample;

public sealed class ExampleService


{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is LoggingMessageWriter);

var dependencyArray = messageWriters.ToArray();


Trace.Assert(dependencyArray[0] is ConsoleMessageWriter);
Trace.Assert(dependencyArray[1] is LoggingMessageWriter);
}
}
O ExampleService define dois parâmetros de construtor; um único IMessageWriter e um
IEnumerable<IMessageWriter> . O único IMessageWriter é a última implementação a ser

registrada, enquanto IEnumerable<IMessageWriter> representa todas as implementações


registradas.

A estrutura também fornece métodos de extensão TryAdd{LIFETIME} , que registram o


serviço somente se ainda não houver uma implementação registrada.

No exemplo a seguir, a chamada para AddSingleton se registra ConsoleMessageWriter


como uma implementação para IMessageWriter . A chamada para TryAddSingleton não
tem efeito porque IMessageWriter já possui uma implementação registrada:

C#

services.AddSingleton<IMessageWriter, ConsoleMessageWriter>();
services.TryAddSingleton<IMessageWriter, LoggingMessageWriter>();

A TryAddSingleton não tem efeito, pois já foi adicionado e a "tentativa" falhará. A


declaração ExampleService seria a seguinte:

C#

public class ExampleService


{
public ExampleService(
IMessageWriter messageWriter,
IEnumerable<IMessageWriter> messageWriters)
{
Trace.Assert(messageWriter is ConsoleMessageWriter);
Trace.Assert(messageWriters.Single() is ConsoleMessageWriter);
}
}

Para obter mais informações, consulte:

TryAdd
TryAddTransient
TryAddScoped
TryAddSingleton

Os métodos TryAddEnumerable(ServiceDescriptor) registram o serviço somente se ainda


não houver uma implementação do mesmo tipo. Vários serviços são resolvidos via
IEnumerable<{SERVICE}> . Ao registrar serviços, adicione uma instância se um dos

mesmos tipos ainda não tiver sido adicionado. Os autores da biblioteca usam
TryAddEnumerable para evitar o registro de várias cópias de uma implementação no

contêiner.

No exemplo a seguir, a primeira chamada para TryAddEnumerable registra MessageWriter


como uma implementação para IMessageWriter1 . A segunda chamada registra
MessageWriter para IMessageWriter2 . A terceira chamada não tem nenhum efeito

porque IMessageWriter1 já tem uma implementação registrada de MessageWriter :

C#

public interface IMessageWriter1 { }


public interface IMessageWriter2 { }

public class MessageWriter : IMessageWriter1, IMessageWriter2


{
}

services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1,
MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter2,
MessageWriter>());
services.TryAddEnumerable(ServiceDescriptor.Singleton<IMessageWriter1,
MessageWriter>());

O registro de serviço geralmente é independente de ordem, exceto ao registrar várias


implementações do mesmo tipo.

IServiceCollection é uma coleção de objetos ServiceDescriptor. O exemplo a seguir

mostra como registrar um serviço criando e adicionando um ServiceDescriptor :

C#

string secretKey = Configuration["SecretKey"];


var descriptor = new ServiceDescriptor(
typeof(IMessageWriter),
_ => new DefaultMessageWriter(secretKey),
ServiceLifetime.Transient);

services.Add(descriptor);

Os métodos internos Add{LIFETIME} usam a mesma abordagem. Por exemplo, consulte


o código-fonte AddScoped .

Comportamento da injeção de construtor


Os serviços podem ser resolvidos usando:
IServiceProvider
ActivatorUtilities:
Cria objetos que não estão registrados no contêiner.
Usado com alguns recursos de estrutura.

Os construtores podem aceitar argumentos que não são fornecidos pela injeção de
dependência, mas que precisam atribuir valores padrão.

Quando os serviços são resolvidos por IServiceProvider ou ActivatorUtilities , a


injeção do construtor exige um construtor público.

Quando os serviços são resolvidos por ActivatorUtilities , a injeção de construtor


exige a existência de apenas de um construtor aplicável. Há suporte para sobrecargas
de construtor, mas somente uma sobrecarga pode existir, cujos argumentos podem ser
todos atendidos pela injeção de dependência.

Validação de escopo
Quando o aplicativo é executado no ambiente Development e chama
CreateDefaultBuilder para criar o host, o provedor de serviços padrão executa
verificações para verificar se:

Os serviços com escopo não são resolvidos do provedor de serviços raiz.


Os serviços com escopo não são injetados em singletons.

O provedor de serviços raiz é criado quando BuildServiceProvider é chamado. O tempo


de vida do provedor de serviço raiz corresponde ao tempo de vida do aplicativo quando
o provedor começa com o aplicativo e é descartado quando o aplicativo é desligado.

Os serviços com escopo são descartados pelo contêiner que os criou. Se um serviço
com escopo é criado no contêiner raiz, o tempo de vida do serviço é promovido
efetivamente para singleton, porque ele só é descartado pelo contêiner raiz quando o
aplicativo é desligado. A validação dos escopos de serviço detecta essas situações
quando BuildServiceProvider é chamado.

Cenários no escopo
IServiceScopeFactory é sempre registrado como singleton, mas IServiceProvider pode
variar de acordo com o tempo de vida da classe que contém. Por exemplo, se você
resolver serviços de um escopo e qualquer um desses serviços obter IServiceProvider,
será uma instância com escopo.
Para obter serviços de escopo em implementações de IHostedService, como
BackgroundService, não injete as dependências de serviço por meio de injeção de
construtor. Em vez disso, injete IServiceScopeFactory, crie um escopo e resolva
dependências do escopo para usar o tempo de vida do serviço apropriado.

C#

namespace WorkerScope.Example;

public sealed class Worker(


ILogger<Worker> logger,
IServiceScopeFactory serviceScopeFactory)
: BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken
stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using (IServiceScope scope = serviceScopeFactory.CreateScope())
{
try
{
logger.LogInformation(
"Starting scoped work, provider hash: {hash}.",
scope.ServiceProvider.GetHashCode());

var store =
scope.ServiceProvider.GetRequiredService<IObjectStore>();
var next = await store.GetNextAsync();
logger.LogInformation("{next}", next);

var processor =
scope.ServiceProvider.GetRequiredService<IObjectProcessor>();
await processor.ProcessAsync(next);
logger.LogInformation("Processing {name}.", next.Name);

var relay =
scope.ServiceProvider.GetRequiredService<IObjectRelay>();
await relay.RelayAsync(next);
logger.LogInformation("Processed results have been
relayed.");

var marked = await store.MarkAsync(next);


logger.LogInformation("Marked as processed: {next}",
marked);
}
finally
{
logger.LogInformation(
"Finished scoped work, provider hash: {hash}.{nl}",
scope.ServiceProvider.GetHashCode(),
Environment.NewLine);
}
}
}
}
}

No código anterior, enquanto o aplicativo está em execução, o serviço em segundo


plano:

Depende de IServiceScopeFactory.
Cria um IServiceScope para resolver serviços adicionais.
Resolve serviços com escopo para consumo.
Funciona no processamento de objetos e, em seguida, retransmissão deles e,
finalmente, os marca como processados.

No código-fonte de exemplo, você pode ver como as implementações de


IHostedService podem se beneficiar de tempos de vida de serviço com escopo.

Serviços com chave


A partir do .NET 8, há suporte para registros e pesquisas de serviços com base em uma
chave, o que significa que é possível registrar vários serviços com uma chave diferente e
usar essa chave para a pesquisa.

Por exemplo, considere o caso em que você tem implementações diferentes da interface
IMessageWriter : MemoryMessageWriter e QueueMessageWriter .

Você pode registrar esses serviços usando a sobrecarga dos métodos de registro do
serviço (vistos anteriormente) que dá suporte a uma chave como parâmetro:

C#

services.AddKeyedSingleton<IMessageWriter, MemoryMessageWriter>("memory");
services.AddKeyedSingleton<IMessageWriter, QueueMessageWriter>("queue");

O key não está limitado a string , pode ser qualquer object que desejar, desde que o
tipo implemente corretamente Equals .

No construtor da classe que usa IMessageWriter , adicione FromKeyedServicesAttribute


para especificar a chave do serviço a ser resolvido:

C#
public class ExampleService
{
public ExampleService(
[FromKeyedServices("queue")] IMessageWriter writer)
{
// Omitted for brevity...
}
}

Confira também
Usar injeção de dependência no .NET
Diretrizes de injeção de dependência
Injeção de dependência no ASP.NET Core
Padrões de conferência NDC para desenvolvimento de aplicativos DI
Princípio de dependências explícitas
Inversão de Contêineres de Controle e o Padrão de Injeção de Dependência
(Martin Fowler)
Bugs de DI devem ser criados no repositório github.com/dotnet/extensions

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Tutorial: usar injeção de dependência no
.NET
Artigo • 02/02/2024

Este tutorial mostra como usar a DI (injeção de dependência) no .NET. Com as Extensões
da Microsoft, a DI é gerenciada adicionando serviços e configurando-os em um
IServiceCollection. A interface IHost expõe a instância IServiceProvider, que atua como
um contêiner de todos os serviços registrados.

Neste tutorial, você aprenderá a:

" Criar um aplicativo de console do .NET que usa injeção de dependência


" Criar e configurar um host genérico
" Gravar várias interfaces e implementações correspondentes
" Usar o tempo de vida do serviço e o escopo para a DI

Pré-requisitos
SDK do .NET Core 3.1 ou posterior.
Familiaridade com a criação de novos aplicativos .NET e a instalação de pacotes
NuGet.

Crie um novo aplicativo de console


Usando o comando dotnet new ou um novo assistente de projeto do IDE, crie um novo
aplicativo de console .NET chamado ConsoleDI.Example. Adicione o pacote NuGet
Microsoft.Extensions.Hosting ao projeto.

Seu novo arquivo de projeto de aplicativo do console deve ser semelhante ao seguinte:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<RootNamespace>ConsoleDI.Example</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"
/>
</ItemGroup>

</Project>

) Importante

Neste exemplo, o pacote NuGet Microsoft.Extensions.Hosting é necessário para


compilar e executar o aplicativo. Alguns metapacotes podem conter o pacote
Microsoft.Extensions.Hosting , caso em que não é necessária uma referência de

pacote explícita.

Adicionar interfaces
Neste aplicativo de exemplo, você aprenderá como a injeção de dependência lida com o
tempo de vida do serviço. Você criará diversas interfaces que representam tempos de
vida de serviço diferentes. Adicione as seguintes interfaces ao diretório raiz do projeto:

IReportServiceLifetime.cs

C#

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IReportServiceLifetime


{
Guid Id { get; }

ServiceLifetime Lifetime { get; }


}

A interface IReportServiceLifetime define o seguinte:

Uma propriedade Guid Id que representa o identificador exclusivo do serviço.


Uma propriedade ServiceLifetime que representa o tempo de vida do serviço.

IExampleTransientService.cs

C#

using Microsoft.Extensions.DependencyInjection;
namespace ConsoleDI.Example;

public interface IExampleTransientService : IReportServiceLifetime


{
ServiceLifetime IReportServiceLifetime.Lifetime =>
ServiceLifetime.Transient;
}

IExampleScopedService.cs

C#

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IExampleScopedService : IReportServiceLifetime


{
ServiceLifetime IReportServiceLifetime.Lifetime =>
ServiceLifetime.Scoped;
}

IExampleSingletonService.cs

C#

using Microsoft.Extensions.DependencyInjection;

namespace ConsoleDI.Example;

public interface IExampleSingletonService : IReportServiceLifetime


{
ServiceLifetime IReportServiceLifetime.Lifetime =>
ServiceLifetime.Singleton;
}

Todas as subinterfaces de IReportServiceLifetime implementam


IReportServiceLifetime.Lifetime explicitamente com um padrão. Por exemplo,

IExampleTransientService implementa IReportServiceLifetime.Lifetime explicitamente

com o valor ServiceLifetime.Transient .

Adicionar implementações padrão


Todas as implementações de exemplo inicializam as respectivas propriedades Id com o
resultado de Guid.NewGuid(). Adicione ao diretório raiz do projeto as seguintes classes
de implementação padrão para os diversos serviços:
ExampleTransientService.cs

C#

namespace ConsoleDI.Example;

internal sealed class ExampleTransientService : IExampleTransientService


{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

ExampleScopedService.cs

C#

namespace ConsoleDI.Example;

internal sealed class ExampleScopedService : IExampleScopedService


{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

ExampleSingletonService.cs

C#

namespace ConsoleDI.Example;

internal sealed class ExampleSingletonService : IExampleSingletonService


{
Guid IReportServiceLifetime.Id { get; } = Guid.NewGuid();
}

Cada implementação é definida como internal sealed e implementa a interface


correspondente. Por exemplo, ExampleSingletonService implementa
IExampleSingletonService .

Adicionar um serviço que requer uma DI


Adicione a seguinte classe de relator de tempo de vida do serviço, que atua como um
serviço para o aplicativo de console:

ServiceLifetimeReporter.cs

C#
namespace ConsoleDI.Example;

internal sealed class ServiceLifetimeReporter(


IExampleTransientService transientService,
IExampleScopedService scopedService,
IExampleSingletonService singletonService)
{
public void ReportServiceLifetimeDetails(string lifetimeDetails)
{
Console.WriteLine(lifetimeDetails);

LogService(transientService, "Always different");


LogService(scopedService, "Changes only with lifetime");
LogService(singletonService, "Always the same");
}

private static void LogService<T>(T service, string message)


where T : IReportServiceLifetime =>
Console.WriteLine(
$" {typeof(T).Name}: {service.Id} ({message})");
}

O ServiceLifetimeReporter define um construtor que requer cada uma das interfaces


de serviço mencionadas, ou seja, IExampleTransientService , IExampleScopedService e
IExampleSingletonService . O objeto expõe um único método que permite ao
consumidor realizar um relato sobre o serviço com um determinado parâmetro
lifetimeDetails . Quando invocado, o método ReportServiceLifetimeDetails registra o

identificador exclusivo de cada serviço com a respectiva mensagem de tempo de vida.


As mensagens de log ajudam a visualizar o tempo de vida do serviço.

Registrar serviços para a DI


Atualize Program.cs com o seguinte código:

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using ConsoleDI.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddTransient<IExampleTransientService,
ExampleTransientService>();
builder.Services.AddScoped<IExampleScopedService, ExampleScopedService>();
builder.Services.AddSingleton<IExampleSingletonService,
ExampleSingletonService>();
builder.Services.AddTransient<ServiceLifetimeReporter>();
using IHost host = builder.Build();

ExemplifyServiceLifetime(host.Services, "Lifetime 1");


ExemplifyServiceLifetime(host.Services, "Lifetime 2");

await host.RunAsync();

static void ExemplifyServiceLifetime(IServiceProvider hostProvider, string


lifetime)
{
using IServiceScope serviceScope = hostProvider.CreateScope();
IServiceProvider provider = serviceScope.ServiceProvider;
ServiceLifetimeReporter logger =
provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 1 to
provider.GetRequiredService<ServiceLifetimeReporter>()");

Console.WriteLine("...");

logger = provider.GetRequiredService<ServiceLifetimeReporter>();
logger.ReportServiceLifetimeDetails(
$"{lifetime}: Call 2 to
provider.GetRequiredService<ServiceLifetimeReporter>()");

Console.WriteLine();
}

Cada método de extensão services.Add{LIFETIME}<{SERVICE}> adiciona (e


possivelmente configura) serviços. Recomendamos que os aplicativos sigam essa
convenção. Não coloque métodos de extensão no namespace
Microsoft.Extensions.DependencyInjection, a menos que você esteja criando um pacote
oficial da Microsoft. Métodos de extensão definidos no namespace
Microsoft.Extensions.DependencyInjection :

São exibidos no IntelliSense sem a necessidade de blocos adicionais de using .


Reduza o número de instruções using necessárias nas classes Program ou Startup
em que esses métodos de extensão normalmente são chamados.

O aplicativo:

Cria uma instância IHostBuilder com as configurações de compilação de host.


Configura os serviços e os adiciona com o correspondente tempo de vida do
serviço.
Chama Build() e atribui uma instância de IHost.
Chama ExemplifyScoping , passando em IHost.Services.
Conclusão
Neste aplicativo de exemplo, você criou diversas interfaces e implementações
correspondentes. Cada um desses serviços é identificado exclusivamente e emparelhado
com um ServiceLifetime. O aplicativo de exemplo demonstra o registro de
implementações de serviço em uma interface e como registrar classes puras sem
interfaces de suporte. Ele também demonstra como as dependências definidas como
parâmetros do construtor são resolvidas no runtime.

Ao executar o aplicativo, ele exibe uma saída semelhante à seguinte:

C#

// Sample output:
// Lifetime 1: Call 1 to
provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: d08a27fa-87d2-4a06-98d7-2773af886125
(Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes
only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b
(Always the same)
// ...
// Lifetime 1: Call 2 to
provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: b43d68fb-2c7b-4a9b-8f02-fc507c164326
(Always different)
// IExampleScopedService: 402c83c9-b4ed-4be1-b78c-86be1b1d908d (Changes
only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b
(Always the same)
//
// Lifetime 2: Call 1 to
provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: f3856b59-ab3f-4bbd-876f-7bab0013d392
(Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes
only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b
(Always the same)
// ...
// Lifetime 2: Call 2 to
provider.GetRequiredService<ServiceLifetimeReporter>()
// IExampleTransientService: a8015c6a-08cd-4799-9ec3-2f2af9cbbfd2
(Always different)
// IExampleScopedService: bba80089-1157-4041-936d-e96d81dd9d1c (Changes
only with lifetime)
// IExampleSingletonService: a61f1ff4-0b14-4508-bd41-21d852484a7b
(Always the same)
Na saída do aplicativo, você pode ver que:

Os serviços Transient são sempre diferentes e uma nova instância é criada a cada
recuperação de serviço.
Os serviços Scoped mudam somente com um novo escopo, mas são a mesma
instância em um escopo.
Os serviços Singleton são sempre os mesmos e uma nova instância é criada
somente uma vez.

Confira também
Diretrizes de injeção de dependência
Injeção de dependência no ASP.NET Core

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Diretrizes de injeção de dependência
Artigo • 28/02/2024

Este artigo fornece diretrizes gerais e práticas recomendadas para implementar a


injeção de dependência em aplicativos .NET.

Projetar serviços para injeção de dependência


Ao criar serviços para injeção de dependência:

Evite membros e classes estáticos com estado. Evite criar um estado global
projetando aplicativos para usar serviços singleton.
Evite a instanciação direta das classes dependentes em serviços. A instanciação
direta acopla o código a uma implementação específica.
Deixe os serviços pequenos, bem fatorados e fáceis de serem testados.

Se uma classe tiver muitas dependências injetadas, isso poderá ser um sinal de que a
classe tem muitas responsabilidades e violará o SRP (princípio de responsabilidade
única). Tente refatorar a classe movendo algumas das responsabilidades para uma nova
classe.

Descarte de serviços
O contêiner é responsável pela limpeza dos tipos que cria e chama Dispose em
instâncias de IDisposable. Os serviços resolvidos do contêiner nunca devem ser
descartados pelo desenvolvedor. Se um tipo ou um alocador for registrado como
singleton, o contêiner descartará o singleton automaticamente.

No exemplo a seguir, os serviços são criados pelo contêiner de serviço e descartados


automaticamente:

C#

namespace ConsoleDisposable.Example;

public sealed class TransientDisposable : IDisposable


{
public void Dispose() => Console.WriteLine($"
{nameof(TransientDisposable)}.Dispose()");
}

O descartável anterior deve ter um tempo de vida transitório.


C#

namespace ConsoleDisposable.Example;

public sealed class ScopedDisposable : IDisposable


{
public void Dispose() => Console.WriteLine($"
{nameof(ScopedDisposable)}.Dispose()");
}

O descartável anterior deve ter um tempo de vida com escopo.

C#

namespace ConsoleDisposable.Example;

public sealed class SingletonDisposable : IDisposable


{
public void Dispose() => Console.WriteLine($"
{nameof(SingletonDisposable)}.Dispose()");
}

O descartável anterior deve ter um tempo de vida singleton.

C#

using ConsoleDisposable.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);


builder.Services.AddTransient<TransientDisposable>();
builder.Services.AddScoped<ScopedDisposable>();
builder.Services.AddSingleton<SingletonDisposable>();

using IHost host = builder.Build();

ExemplifyDisposableScoping(host.Services, "Scope 1");


Console.WriteLine();

ExemplifyDisposableScoping(host.Services, "Scope 2");


Console.WriteLine();

await host.RunAsync();

static void ExemplifyDisposableScoping(IServiceProvider services, string


scope)
{
Console.WriteLine($"{scope}...");

using IServiceScope serviceScope = services.CreateScope();


IServiceProvider provider = serviceScope.ServiceProvider;

_ = provider.GetRequiredService<TransientDisposable>();
_ = provider.GetRequiredService<ScopedDisposable>();
_ = provider.GetRequiredService<SingletonDisposable>();
}

O console de depuração mostra a seguinte saída de exemplo após a execução:

Console

Scope 1...
ScopedDisposable.Dispose()
TransientDisposable.Dispose()

Scope 2...
ScopedDisposable.Dispose()
TransientDisposable.Dispose()

info: Microsoft.Hosting.Lifetime[0]
Application started.Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: .\configuration\console-di-
disposable\bin\Debug\net5.0
info: Microsoft.Hosting.Lifetime[0]
Application is shutting down...
SingletonDisposable.Dispose()

Serviços não criados pelo contêiner de serviço


Considere o seguinte código:

C#

// Register example service in IServiceCollection


builder.Services.AddSingleton(new ExampleService());

No código anterior:

A instância de ExampleService não é criada pelo contêiner de serviço.


A estrutura não descarta os serviços automaticamente.
O desenvolvedor é responsável por descartar os serviços.
Diretrizes de IDisposable para instâncias transitórias e
compartilhadas

Tempo de vida transitório e limitado


Cenário

O aplicativo requer uma instância de IDisposable com um tempo de vida transitório


para os seguintes cenários:

A instância é resolvida no escopo raiz (contêiner raiz).


A instância deve ser descartada antes que o escopo termine.

Solução

Use o padrão do alocador para criar uma instância fora do escopo pai. Nessa situação, o
aplicativo geralmente tem um método Create que chama diretamente o construtor do
tipo final. Se o tipo final tiver outras dependências, o alocador poderá:

Receber um IServiceProvider em seu construtor.


Usar ActivatorUtilities.CreateInstance para criar uma instância da instância fora do
contêiner, usando o contêiner para suas dependências.

Instância compartilhada, tempo de vida limitado

Cenário

O aplicativo requer uma instância compartilhada de IDisposable em vários serviços, mas


a instância de IDisposable deve ter um tempo de vida limitado.

Solução

Registre a instância com um tempo de vida com escopo. Use


IServiceScopeFactory.CreateScope para criar um IServiceScope. Use o IServiceProvider
do escopo para obter os serviços necessários. Descarte o escopo quando ele não for
mais necessário.

Diretrizes gerais de IDisposable


Não registre instâncias de IDisposable com um tempo de vida transitório. Use o
padrão do alocador.
Não resolva instâncias de IDisposable com um tempo de vida transitório ou com
escopo no escopo raiz. A única exceção é se o aplicativo cria/recria e descarta
IServiceProvider, mas esse não é um padrão ideal.
O recebimento de uma dependência de IDisposable via DI não exige que o próprio
receptor implemente IDisposable. O receptor da dependência IDisposable não
deve chamar Dispose nessa dependência.
Use escopos para controlar o tempo de vida dos serviços. Os escopos não são
hierárquicos e não há nenhuma conexão especial entre os escopos.

Para obter mais informações sobre a limpeza de recursos, confira Implementar um


método Dispose ou implementar um método DisposeAsync. Além disso, considere o
cenário Serviços transitórios descartáveis capturados pelo contêiner, em relação à
limpeza de recursos.

Substituição do contêiner de serviço padrão


O contêiner de serviço interno deve atender às necessidades da estrutura e da maioria
dos aplicativos de consumo. É recomendado usar o contêiner interno, a menos que você
precise de um recurso específico que não seja compatível com ele, como:

Injeção de propriedade
Injeção baseada apenas no nome (.NET 7 e versões anteriores. Para obter mais
informações, confira Serviços com chave.)
Contêineres filho
Gerenciamento de tempo de vida personalizado
Suporte de Func<T> para inicialização lenta
Registro baseado em convenção

Os seguintes contêineres de terceiros podem ser usados com aplicativos ASP.NET Core:

Autofac
DryIoc
Grace
LightInject
Lamar
Stashbox
Simple Injector

Acesso thread-safe
Crie serviços singleton thread-safe. Se um serviço singleton tiver uma dependência de
um serviço transitório, o serviço transitório também precisará ter acesso thread-safe,
dependendo de como ele for usado pelo singleton.
O método do alocador de um serviço singleton, como o segundo argumento de
AddSingleton<TService>(IServiceCollection, Func<IServiceProvider,TService>) não
precisa ser thread-safe. Como um construtor do tipo ( static ), ele tem garantia de ser
chamado uma vez por um só thread.

Recomendações
Não há suporte para a resolução de serviço baseada em async/await e Task .
Como o C# não dá suporte a construtores assíncronos, use métodos assíncronos
depois de resolver o serviço de maneira síncrona.
Evite armazenar dados e a configuração diretamente no contêiner do serviço. Por
exemplo, o carrinho de compras de um usuário normalmente não deve ser
adicionado ao contêiner do serviço. A configuração deve usar o padrão de opções.
Da mesma forma, evite objetos de "suporte de dados" que existem somente para
permitir o acesso a outro objeto. É melhor solicitar o item real por meio da DI.
Evite o acesso estático aos serviços. Por exemplo, evite capturar
IApplicationBuilder.ApplicationServices como um campo estático ou uma
propriedade para uso em outro lugar.
Mantenha os alocadores de DI rápidos e síncronos.
Evite usar o padrão do localizador de serviço. Por exemplo, não invoque GetService
para obter uma instância de serviço quando for possível usar a DI.
Outra variação de localizador de serviço a ser evitada é injetar um alocador que
resolva as dependências em tempo de execução. Essas duas práticas misturam
estratégias de inversão de controle.
Evite chamadas para BuildServiceProvider ao configurar os serviços. A chamada
para BuildServiceProvider normalmente ocorre quando o desenvolvedor quer
resolver um serviço ao registrar outro serviço. Em vez disso, use uma sobrecarga
que inclua o IServiceProvider por esse motivo.
Os serviços transitórios descartáveis são capturados pelo contêiner para descarte.
Isso pode se transformar em uma perda de memória quando resolvido por meio
do contêiner de nível superior.
Habilite a validação de escopo para garantir que o aplicativo não tenha singletons
que capturem serviços com escopo. Para obter mais informações, confira Validação
de escopo.

Como todos os conjuntos de recomendações, talvez você encontre situações em que é


necessário ignorar uma recomendação. As exceções são raras, ocorrendo em casos
especiais dentro da própria estrutura.

A DI é uma alternativa aos padrões de acesso a objeto estático/global. Talvez você não
obtenha os benefícios da DI se combiná-lo com o acesso a objeto estático.
Exemplo de antipadrões
Além das diretrizes deste artigo, há vários antipadrões que você deve evitar. Alguns
desses antipadrões foram aprendidos durante o desenvolvimento dos próprios
runtimes.

2 Aviso

Estes são exemplos de anti-padrões, não copie o código, não use esses padrões e
evite-os a todo custo.

Serviços transitórios descartáveis capturados pelo


contêiner
Quando você registrar serviços transitórios que implementem IDisposable, por padrão, o
contêiner de DI manterá essas referências e não executará Dispose() nelas até que o
contêiner seja descartado quando o aplicativo for interrompido, se eles forem resolvidos
em um contêiner, ou até que o escopo seja descartado, se eles forem resolvidos em um
escopo. Isso pode se transformar em uma perda de memória quando resolvido no nível
do contêiner.

No antipadrão anterior, uma instância foi criada em 1.000 objetos ExampleDisposable e


eles ficaram com root. Eles não serão descartados até que a instância de
serviceProvider seja descartada.

Para obter mais informações sobre como depurar perdas de memória, confira Depurar
uma perda de memória no .NET.

Alocadores de DI assíncronos podem causar deadlocks


O termo "alocadores de DI" se refere aos métodos de sobrecarga que existem ao
chamar Add{LIFETIME} . Há sobrecargas que aceitam um Func<IServiceProvider, T> em
que T é o serviço está sendo registrado e o parâmetro se chama
implementationFactory . O método implementationFactory pode ser fornecido como

uma expressão lambda, uma função local ou um método. Se o alocador for assíncrono e
você usar Task<TResult>.Result, isso causará um deadlock.

No código anterior, implementationFactory recebe uma expressão lambda em que o


corpo chama Task<TResult>.Result em um método de retorno Task<Bar> . Isso causa um
deadlock. O método GetBarAsync simplesmente emula uma operação de trabalho
assíncrona com Task.Delay e depois chama GetRequiredService<T>(IServiceProvider).

Para obter mais informações sobre diretrizes assíncronas, confira Programação


assíncrona: informações e recomendações importantes. Para obter mais informações
sobre a depuração de deadlocks, confira Depurar um deadlock no .NET.

Quando você estiver executando esse antipadrão e o deadlock ocorrer, você poderá
exibir os dois threads que estão esperando na janela Pilhas Paralelas do Visual Studio.
Para obter mais informações, confira Exibir threads e tarefas na janela Pilhas Paralelas.

Dependência cativa
O termo "dependência cativa" foi cunhado por Mark Seemann e se refere à
configuração incorreta do tempo de vida de serviço, em que um serviço com vida mais
longa mantém cativo um serviço de vida mais curta.

No código anterior, Foo é registrado como singleton e Bar recebe um escopo, o que
parece válido superficialmente. No entanto, considere a implementação de Foo .

C#

namespace DependencyInjection.AntiPatterns;

public class Foo(Bar bar)


{
}

O objeto Foo requer um objeto Bar e, como Foo é um singleton e Bar tem um escopo
definido, essa é uma configuração incorreta. Assim, a instância de Foo só seria criada
uma vez e ficaria atrelada a Bar durante seu tempo de vida, que é maior do que o
tempo de vida com escopo pretendido de Bar . Você deve considerar a validação de
escopos passando validateScopes: true para o BuildServiceProvider(IServiceCollection,
Boolean). Ao validar os escopos, você obterá uma InvalidOperationException com uma
mensagem semelhante a "Não é possível consumir o serviço com escopo 'Bar' do
singleton 'Foo'.".

Para obter mais informações, confira Validação de escopo.

Serviço com escopo como singleton


Ao usar serviços com escopo, se você não estiver criando um escopo ou se o serviço
não estiver em um escopo existente, o serviço se tornará um singleton.

C#
static void ScopedServiceBecomesSingleton()
{
var services = new ServiceCollection();
services.AddScoped<Bar>();

using ServiceProvider serviceProvider =


services.BuildServiceProvider(validateScopes: true);
using (IServiceScope scope = serviceProvider.CreateScope())
{
// Correctly scoped resolution
Bar correct = scope.ServiceProvider.GetRequiredService<Bar>();
}

// Not within a scope, becomes a singleton


Bar avoid = serviceProvider.GetRequiredService<Bar>();
}

No código anterior, Bar é recuperado dentro de um IServiceScope, o que está correto.


O anti-padrão é a recuperação de Bar fora do escopo e a variável é chamada de avoid
para mostrar qual exemplo de recuperação está incorreto.

Confira também
Injeção de dependência no .NET
Tutorial: usar a injeção de dependência no .NET

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Configuração em .NET
Artigo • 19/07/2023

A configuração no .NET é realizada usando um ou mais provedores de configuração. Os


provedores de configuração leem os dados de configuração de pares chave-valor
usando várias de fontes de configuração:

Arquivos de configurações, como appsettings.json


Variáveis de ambiente
Azure Key Vault
Configuração de Aplicativos do Azure
Argumentos de linha de comando
Provedores personalizados, instalados ou criados
Arquivos de diretório
Objetos do .NET na memória
Provedores de terceiros

7 Observação

Para obter informações sobre como configurar o próprio runtime do .NET, confira
Configurações do .NET Runtime.

Conceitos e abstrações
Considerando uma ou mais fontes de configuração, o tipo IConfiguration fornece uma
exibição unificada dos dados de configuração. A configuração é somente leitura e o
padrão de configuração não foi projetado para ser programaticamente gravável. A
interface IConfiguration é uma representação única de todas as fontes de
configuração, conforme mostrado no diagrama a seguir:
appsettings.{Environment}.json

Environment variables

IConfiguration

Command-line arguments

*Other sources 

Configurar aplicativos de console


Os aplicativos de console do .NET criados usando o novo modelo de comando dotnet
ou o Visual Studio por padrão não expõem as funcionalidades de configuração. Para
adicionar a configuração em um novo aplicativo de console .NET, adicione uma
referência de pacote ao Microsoft.Extensions.Configuration . Este pacote é a base para
a configuração em aplicativos .NET. Ele fornece o ConfigurationBuilder e os tipos
relacionados.

C#

using Microsoft.Extensions.Configuration;

var configuration = new ConfigurationBuilder()


.AddInMemoryCollection(new Dictionary<string, string?>()
{
["SomeKey"] = "SomeValue"
})
.Build();

Console.WriteLine(configuration["SomeKey"]);

// Outputs:
// SomeValue

O código anterior:

Cria uma nova instância ConfigurationBuilder.


Adiciona uma coleção na memória de pares chave-valor ao construtor de
configuração.
Chama o método Build() para criar uma instância de IConfiguration.
Grava o valor da chave SomeKey no console.

Embora este exemplo use uma configuração na memória, há muitos provedores de


configuração disponíveis, expondo a funcionalidade para fontes de configuração
baseadas em arquivo, em variáveis de ambiente, argumentos de linha de comando e
outras. Para obter mais informações, confira Provedores de configuração no .NET.

Abordagem de hospedagem alternativa


Normalmente, seus aplicativos farão mais do que apenas ler a configuração. Eles
provavelmente usarão injeção de dependência, registro em log e outros serviços. A
abordagem de Host genérico do .NET é recomendada para aplicativos que usam esses
serviços. Em vez disso, considere adicionar uma referência de pacote a
Microsoft.Extensions.Hosting . Modifique o arquivo Program.cs para corresponder ao
seguinte código:

C#

using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();

// Application code should start here.

await host.RunAsync();

O método Host.CreateApplicationBuilder(String[]) inicializado fornece a configuração


padrão para o aplicativo na seguinte ordem, da prioridade mais alta para a mais baixa:

1. Argumentos de linha de comando usando o Provedor de configuração de linha de


comando.
2. Variáveis de ambiente usando o Provedor de configuração de variáveis de
ambiente.
3. Segredos do aplicativo quando o aplicativo é executado no ambiente Development .
4. appsettings.json usando o provedor de configuração JSON .
5. appsettings. Environment .json usando o provedor de configuração JSON. Por
exemplo, appsettings.Production.json e appsettings.Development.json.
6. ChainedConfigurationProvider: adiciona um IConfiguration existente como fonte.

A adição de um provedor de configuração substitui os valores de configuração


anteriores. Por exemplo, o provedor de configuração de linha de comando substitui
todos os valores de outros provedores porque ele foi adicionado por último. Se SomeKey
for definido em appsettings.json e no ambiente, o valor do ambiente será usado porque
foi adicionado após appsettings.json.

Associação
Uma das principais vantagens de usar as abstrações de configuração do .NET é a
capacidade de associar valores de configuração a instâncias de objetos .NET. Por
exemplo, o provedor de configuração JSON pode ser usado para mapear arquivos
appsettings.json para objetos .NET e é usado com injeção de dependência. Isso permite
o padrão de opções, que usa classes para fornecer acesso fortemente tipado a grupos
de configurações relacionadas. A configuração do .NET fornece várias abstrações.
Considere as seguintes interfaces:

IConfiguration: Representa um conjunto de propriedades de configuração de


aplicativo de chave/valor.
IConfigurationRoot: representa a raiz de uma hierarquia de IConfiguration .
IConfigurationSection: Representa uma seção dos valores de configuração do
aplicativo.

Essas abstrações são independentes do provedor de configuração subjacente


(IConfigurationProvider). Em outras palavras, você pode usar uma instância
IConfiguration para acessar qualquer valor de configuração de vários provedores.

O associador pode usar diferentes abordagens para processar valores de configuração:

Desserialização direta (usando conversores internos) para tipos primitivos.


O TypeConverter para um tipo complexo quando o tipo tem um.
Reflexão para um tipo complexo que tem propriedades.

7 Observação

O associador tem algumas limitações:

As propriedades serão ignoradas se tiverem setters privados ou seu tipo não


puder ser convertido.
As propriedades sem chaves de configuração correspondentes são ignoradas.

Associando hierarquias
Os valores de configuração podem conter dados hierárquicos. Objetos hierárquicos são
representados com o uso do delimitador : nas chaves de configuração. Para acessar
um valor de configuração, use o caractere : para delimitar uma hierarquia. Por
exemplo, considere os seguintes valores de configuração:

JSON

{
"Parent": {
"FavoriteNumber": 7,
"Child": {
"Name": "Example",
"GrandChild": {
"Age": 3
}
}
}
}

A tabela a seguir representa as chaves de exemplo e seus valores correspondentes para


o exemplo anterior JSON:

ノ Expandir a tabela

Chave Valor

"Parent:FavoriteNumber" 7

"Parent:Child:Name" "Example"

"Parent:Child:GrandChild:Age" 3

Exemplo básico
Para acessar valores de configuração em sua forma básica, sem a assistência da
abordagem de host genérico, use o tipo ConfigurationBuilder diretamente.

 Dica

O tipo é System.Configuration.ConfigurationBuilder diferente do tipo


Microsoft.Extensions.Configuration.ConfigurationBuilder. Todo esse conteúdo é
específico para os pacotes e namespaces Microsoft.Extensions.* do NuGet.

Considere o seguinte projeto C#:

XML
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder"
Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json"
Version="8.0.0" />
<PackageReference
Include="Microsoft.Extensions.Configuration.EnvironmentVariables"
Version="8.0.0" />
</ItemGroup>

</Project>

O arquivo de projeto anterior faz referência a vários pacotes NuGet de configuração:

Microsoft.Extensions.Configuration.Binder : funcionalidade para associar um


objeto a dados em provedores de configuração para
Microsoft.Extensions.Configuration .

Microsoft.Extensions.Configuration.Json : implementação do provedor de


configuração JSON para Microsoft.Extensions.Configuration .
Microsoft.Extensions.Configuration.EnvironmentVariables : implementação do
provedor de configuração de variáveis de ambiente para
Microsoft.Extensions.Configuration .

Considere um exemplo de arquivo appsettings.json:

JSON

{
"Settings": {
"KeyOne": 1,
"KeyTwo": true,
"KeyThree": {
"Message": "Oh, that's nice...",
"SupportedVersions": {
"v1": "1.0.0",
"v3": "3.0.7"
}
},
"IPAddressRange": [
"46.36.198.121",
"46.36.198.122",
"46.36.198.123",
"46.36.198.124",
"46.36.198.125"
]
}
}

Agora, devido a esse arquivo JSON, aqui está um padrão de consumo de exemplo
usando o construtor de configuração diretamente:

C#

using Microsoft.Extensions.Configuration;

// Build a config object, using env vars and JSON providers.


IConfigurationRoot config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.AddEnvironmentVariables()
.Build();

// Get values from the config given their key and their target type.
Settings? settings = config.GetRequiredSection("Settings").Get<Settings>();

// Write the values to the console.


Console.WriteLine($"KeyOne = {settings?.KeyOne}");
Console.WriteLine($"KeyTwo = {settings?.KeyTwo}");
Console.WriteLine($"KeyThree:Message = {settings?.KeyThree?.Message}");

// Application code which might rely on the config could start here.

// This will output the following:


// KeyOne = 1
// KeyTwo = True
// KeyThree:Message = Oh, that's nice...

O código anterior do C#:

Instancia um ConfigurationBuilder.
Adiciona o arquivo "appsettings.json" a ser reconhecido pelo provedor de
configuração JSON.
Adiciona variáveis de ambiente como sendo reconhecidas pelo provedor de
configuração de Variável de Ambiente.
Obtém a seção "Settings" necessária e a instância correspondente Settings
usando a instância config .

O objeto Settings é moldado da seguinte maneira:

C#

public sealed class Settings


{
public required int KeyOne { get; set; }
public required bool KeyTwo { get; set; }
public required NestedSettings KeyThree { get; set; } = null!;
}

C#

public sealed class NestedSettings


{
public required string Message { get; set; } = null!;
}

Exemplo básico com hospedagem


Para acessar o valor IConfiguration , você pode confiar novamente no pacote NuGet
Microsoft.Extensions.Hosting . Crie um novo aplicativo de console e cole o seguinte
conteúdo de arquivo de projeto nele:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
</PropertyGroup>

<ItemGroup>
<Content Include="appsettings.json">
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="8.0.0"
/>
</ItemGroup>
</Project>

O arquivo de projeto anterior define que:

O aplicativo é um executável.
Um arquivo appsettings.json deve ser copiado para o diretório de saída quando o
projeto for compilado.
A referência do pacote NuGet Microsoft.Extensions.Hosting é adicionada.

Adicione o arquivo appsettings.json na raiz do projeto com o seguinte conteúdo:

JSON

{
"KeyOne": 1,
"KeyTwo": true,
"KeyThree": {
"Message": "Thanks for checking this out!"
}
}

Substitua o conteúdo do arquivo Program.cs pelo código C# a seguir:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();

// Ask the service provider for the configuration abstraction.


IConfiguration config = host.Services.GetRequiredService<IConfiguration>();

// Get values from the config given their key and their target type.
int keyOneValue = config.GetValue<int>("KeyOne");
bool keyTwoValue = config.GetValue<bool>("KeyTwo");
string? keyThreeNestedValue = config.GetValue<string>("KeyThree:Message");

// Write the values to the console.


Console.WriteLine($"KeyOne = {keyOneValue}");
Console.WriteLine($"KeyTwo = {keyTwoValue}");
Console.WriteLine($"KeyThree:Message = {keyThreeNestedValue}");

// Application code which might rely on the config could start here.

await host.RunAsync();

// This will output the following:


// KeyOne = 1
// KeyTwo = True
// KeyThree:Message = Thanks for checking this out!

Quando você executa esse aplicativo, o Host.CreateApplicationBuilder define o


comportamento para descobrir a configuração JSON e expô-la por meio da instância
IConfiguration . Na instância host , você pode solicitar o provedor de serviços para a

instância IConfiguration e, em seguida, solicitar valores.

 Dica

Usar a instância bruta IConfiguration dessa maneira, embora conveniente, não


dimensiona muito bem. Quando os aplicativos crescem em complexidade e suas
configurações correspondentes se tornam mais complexas, recomendamos que
você use o padrão de opções como alternativa.

Exemplo básico com hospedagem e uso da API do


indexador
Considere o mesmo conteúdo do arquivo appsettings.json do exemplo anterior:

JSON

{
"SupportedVersions": {
"v1": "1.0.0",
"v3": "3.0.7"
},
"IPAddressRange": [
"46.36.198.123",
"46.36.198.124",
"46.36.198.125"
]
}

Substitua o conteúdo do arquivo Program.cs pelo código C# a seguir:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();


// Ask the service provider for the configuration abstraction.
IConfiguration config = host.Services.GetRequiredService<IConfiguration>();

// Get values from the config given their key and their target type.
string? ipOne = config["IPAddressRange:0"];
string? ipTwo = config["IPAddressRange:1"];
string? ipThree = config["IPAddressRange:2"];
string? versionOne = config["SupportedVersions:v1"];
string? versionThree = config["SupportedVersions:v3"];

// Write the values to the console.


Console.WriteLine($"IPAddressRange:0 = {ipOne}");
Console.WriteLine($"IPAddressRange:1 = {ipTwo}");
Console.WriteLine($"IPAddressRange:2 = {ipThree}");
Console.WriteLine($"SupportedVersions:v1 = {versionOne}");
Console.WriteLine($"SupportedVersions:v3 = {versionThree}");

// Application code which might rely on the config could start here.

await host.RunAsync();

// This will output the following:


// IPAddressRange:0 = 46.36.198.123
// IPAddressRange:1 = 46.36.198.124
// IPAddressRange:2 = 46.36.198.125
// SupportedVersions:v1 = 1.0.0
// SupportedVersions:v3 = 3.0.7

Os valores são acessados usando a API do indexador em que cada chave é uma cadeia
de caracteres e o valor é uma cadeia de caracteres. A configuração dá suporte a
propriedades, objetos, matrizes e dicionários.

Provedores de configuração
A tabela a seguir mostra os provedores de configuração disponíveis para aplicativos
.NET Core.

ノ Expandir a tabela

Provedor Fornece a configuração de

Provedor de configuração do Aplicativo do Azure Configuração de Aplicativo do Azure

Provedor de configuração do Azure Key Vault Cofre de Chave do Azure

Provedor de configuração de linha de comando Parâmetros de linha de comando

Provedor de Configuração personalizado Fonte personalizada


Provedor Fornece a configuração de

Provedor de configuração de variáveis de ambiente Variáveis de ambiente

Provedor de configuração de arquivo Arquivos JSON, XML e INI

Provedor de configuração de chave por arquivo Arquivos de diretório

Provedor de configuração de memória Coleções na memória

Segredos do aplicativo (Gerenciador de Segredos) Arquivo no diretório de perfil do usuário

 Dica

A ordem na qual os provedores de configuração são adicionados é importante.


Quando vários provedores de configuração são usados e mais de um provedor
especifica a mesma chave, a última adicionada é usada.

Para obter mais informações sobre vários provedores de configuração, confira


Provedores de configuração no .NET.

Confira também
Provedores de configuração em .NET
Implementar um provedor de configuração personalizado
Os bugs de configuração devem ser criados no repositório
github.com/dotnet/runtime
Configuração no ASP.NET Core

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Provedores de configuração no .NET
Artigo • 23/06/2023

É possível fazer a configuração no .NET com provedores de configuração. Há vários


tipos de provedores que dependem de várias fontes de configuração. Este artigo
detalha todos os diferentes provedores de configuração e as origens correspondentes.

Provedor de configuração de arquivo


Provedor de configuração de variáveis de ambiente
Provedor de configuração de linha de comando
Provedor de configuração de chave por arquivo
Provedor de configuração de memória

Provedor de configuração de arquivo


FileConfigurationProvider é a classe base para carregamento da configuração do
sistema de arquivos. Os seguintes provedores de configuração derivam de
FileConfigurationProvider :

Provedor de configuração JSON


Provedor de configuração XML
Provedor de configuração INI

As chaves não diferenciam maiúsculas de minúsculas. Todos os provedores de


configuração de arquivo geram a FormatException quando chaves duplicadas são
encontradas em um só provedor.

Provedor de configuração JSON


A classe JsonConfigurationProvider carrega a configuração de um arquivo JSON. Instale
o pacote do NuGet Microsoft.Extensions.Configuration.Json .

As sobrecargas podem especificar:

Se o arquivo é opcional.
Se a configuração será recarregada caso o arquivo seja alterado.

Considere o seguinte código:

C#
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ConsoleJson.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.Sources.Clear();

IHostEnvironment env = builder.Environment;

builder.Configuration
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);

TransientFaultHandlingOptions options = new();


builder.Configuration.GetSection(nameof(TransientFaultHandlingOptions))
.Bind(options);

Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{options.AutoRetryDelay}");

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

O código anterior:

Limpa todos os provedores de configuração existentes que foram adicionados por


padrão no método CreateApplicationBuilder(String[]).
Configura o provedor de configuração JSON para carregar os arquivos
appsettings.json e appsettings. Environment .json com as seguintes opções:
optional: true : o arquivo é opcional.
reloadOnChange: true : o arquivo é recarregado quando as alterações são salvas.

) Importante

Ao adicionar provedores de configuração com IConfigurationBuilder.Add, o


provedor de configuração é adicionado ao final da lista IConfigurationSource .
Quando as chaves são encontradas por vários provedores, o último provedor a ler
a chave substitui os provedores anteriores.

Veja um arquivo appsettings.json de exemplo com várias configurações:


JSON

{
"SecretKey": "Secret key value",
"TransientFaultHandlingOptions": {
"Enabled": true,
"AutoRetryDelay": "00:00:07"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

Na instância IConfigurationBuilder, depois que os provedores de configuração são


adicionados, você pode chamar IConfigurationBuilder.Build() para obter o objeto
IConfigurationRoot. A raiz de configuração representa a raiz de uma hierarquia de
configuração. As seções da configuração podem ser associadas a instâncias de objetos
.NET e depois fornecidas como IOptions<TOptions> por meio de injeção de
dependência.

7 Observação

As propriedades Build Action e Copy to Output Directory do arquivo JSON precisam


ser definidas como Content e Copy if newer (or Copy always), respectivamente.

Considere a classe TransientFaultHandlingOptions definida da seguinte maneira:

C#

namespace ConsoleJson.Example;

public sealed class TransientFaultHandlingOptions


{
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
}

Este código cria a raiz de configuração, associa uma seção ao tipo de classe
TransientFaultHandlingOptions e imprime os valores associados na janela do console:

C#
TransientFaultHandlingOptions options = new();
builder.Configuration.GetSection(nameof(TransientFaultHandlingOptions))
.Bind(options);

Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{options.AutoRetryDelay}");

O aplicativo grava a seguinte saída de exemplo:

C#

// Sample output:
// TransientFaultHandlingOptions.Enabled=True
// TransientFaultHandlingOptions.AutoRetryDelay=00:00:07

Provedor de configuração XML


A classe XmlConfigurationProvider carrega a configuração de um arquivo XML em
tempo de execução. Instale o pacote do NuGet
Microsoft.Extensions.Configuration.Xml .

O código a seguir demonstra a configuração de arquivos XML usando o provedor de


configuração XML.

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.Sources.Clear();

builder.Configuration
.AddXmlFile("appsettings.xml", optional: true, reloadOnChange: true)
.AddXmlFile("repeating-example.xml", optional: true, reloadOnChange:
true);

builder.Configuration.AddEnvironmentVariables();

if (args is { Length: > 0 })


{
builder.Configuration.AddCommandLine(args);
}

using IHost host = builder.Build();


// Application code should start here.

await host.RunAsync();

O código anterior:

Limpa todos os provedores de configuração existentes que foram adicionados por


padrão no método CreateApplicationBuilder(String[]).
Configura o provedor de configuração XML para carregar os arquivos
appsettings.xml e repeating-example.xml com as seguintes opções:
optional: true : o arquivo é opcional.

reloadOnChange: true : o arquivo é recarregado quando as alterações são salvas.


Configura o provedor de configuração de variáveis de ambiente.
Configure o provedor de configuração de linha de comando se o args fornecido
contiver argumentos.

As configurações do XML são substituídas pelas configurações no Provedor de


configuração de variáveis de ambiente e no Provedor de configuração de linha de
comando.

Veja um arquivo appsettings.xml de exemplo com várias definições de configuração:

XML

<?xml version="1.0" encoding="utf-8" ?>


<configuration>
<SecretKey>Secret key value</SecretKey>
<TransientFaultHandlingOptions>
<Enabled>true</Enabled>
<AutoRetryDelay>00:00:07</AutoRetryDelay>
</TransientFaultHandlingOptions>
<Logging>
<LogLevel>
<Default>Information</Default>
<Microsoft>Warning</Microsoft>
</LogLevel>
</Logging>
</configuration>

 Dica

Para usar o tipo IConfiguration em aplicativos WinForms, adicione uma referência


ao pacote NuGet Microsoft.Extensions.Configuration.Xml . Instancie o
ConfigurationBuilder e encadeie as chamadas para AddXmlFile e Build(). Saiba
mais em .NET Docs Issue #29679 .

No .NET 5 e em versões anteriores, adicione o atributo name para distinguir elementos


repetidos que usam o mesmo nome de elemento. No .NET 6 e nas versões posteriores,
o provedor de configuração XML indexa automaticamente elementos repetidos. Isso
significa que você não precisa especificar o atributo name , exceto se quiser o índice "0"
na chave e houver apenas um elemento. (Se você estiver atualizando para o .NET 6 ou
posterior, poderá sofrer uma interrupção resultante dessa alteração no comportamento.
Para obter mais informações, confira Elementos XML repetidos incluem índice.)

XML

<?xml version="1.0" encoding="UTF-8"?>


<configuration>
<section name="section0">
<key name="key0">value 00</key>
<key name="key1">value 01</key>
</section>
<section name="section1">
<key name="key0">value 10</key>
<key name="key1">value 11</key>
</section>
</configuration>

O código a seguir lê o arquivo de configuração anterior e exibe as chaves e os valores:

C#

IConfigurationRoot configurationRoot = builder.Configuration;

string key00 = "section:section0:key:key0";


string key01 = "section:section0:key:key1";
string key10 = "section:section1:key:key0";
string key11 = "section:section1:key:key1";

string? val00 = configurationRoot[key00];


string? val01 = configurationRoot[key01];
string? val10 = configurationRoot[key10];
string? val11 = configurationRoot[key11];

Console.WriteLine($"{key00} = {val00}");
Console.WriteLine($"{key01} = {val01}");
Console.WriteLine($"{key10} = {val10}");
Console.WriteLine($"{key10} = {val11}");

O aplicativo gravaria a seguinte saída de exemplo:


C#

// Sample output:
// section:section0:key:key0 = value 00
// section:section0:key:key1 = value 01
// section:section1:key:key0 = value 10
// section:section1:key:key0 = value 11

É possível usar atributos para fornecer valores:

XML

<?xml version="1.0" encoding="UTF-8"?>


<configuration>
<key attribute="value" />
<section>
<key attribute="value" />
</section>
</configuration>

O arquivo de configuração anterior carrega as seguintes chaves com value :

key:attribute
section:key:attribute

Provedor de configuração INI


A classe IniConfigurationProvider carrega a configuração de um arquivo INI em tempo
de execução. Instale o pacote do NuGet Microsoft.Extensions.Configuration.Ini .

Este código limpa todos os provedores de configuração e adiciona


IniConfigurationProvider com dois arquivos INI como a origem:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);


builder.Configuration.Sources.Clear();

IHostEnvironment env = builder.Environment;

builder.Configuration
.AddIniFile("appsettings.ini", optional: true, reloadOnChange: true)
.AddIniFile($"appsettings.{env.EnvironmentName}.ini", true, true);

using IHost host = builder.Build();


// Application code should start here.

await host.RunAsync();

Veja um arquivo appsettings.json de exemplo com várias definições de configuração:

ini

SecretKey="Secret key value"

[TransientFaultHandlingOptions]
Enabled=True
AutoRetryDelay="00:00:07"

[Logging:LogLevel]
Default=Information
Microsoft=Warning

Este código exibe as definições de configuração anteriores escrevendo-as na janela do


console:

C#

foreach ((string key, string? value) in


builder.Configuration.AsEnumerable().Where(t => t.Value is not null))
{
Console.WriteLine($"{key}={value}");
}

O aplicativo gravaria a seguinte saída de exemplo:

C#

// Sample output:
// TransientFaultHandlingOptions:Enabled=True
// TransientFaultHandlingOptions:AutoRetryDelay=00:00:07
// SecretKey=Secret key value
// Logging:LogLevel:Microsoft=Warning
// Logging:LogLevel:Default=Information

Provedor de configuração de variáveis de


ambiente
Usando a configuração padrão, EnvironmentVariablesConfigurationProvider carrega a
configuração de pares chave-valor de variáveis de ambiente depois de ler
appsettings.json, appsettings. Environment .json e o gerente de segredos. Portanto, os
valores de chave lidos do ambiente substituem os valores lidos de appsettings.json,
appsettings. Environment .json e do gerente de segredos.

O delimitador : não funciona com chaves hierárquicas de variáveis de ambiente em


todas as plataformas. Por exemplo, o delimitador : não é compatível com Bash . O
sublinhado duplo ( __ ), que tem suporte em todas as plataformas, substitui
automaticamente qualquer delimitador : em variáveis de ambiente.

Considere a classe TransientFaultHandlingOptions :

C#

public class TransientFaultHandlingOptions


{
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
}

Os comandos set a seguir definem as chaves de ambiente e os valores de SecretKey e


TransientFaultHandlingOptions .

CLI do .NET

set SecretKey="Secret key from environment"


set TransientFaultHandlingOptions__Enabled="true"
set TransientFaultHandlingOptions__AutoRetryDelay="00:00:13"

Essas configurações de ambiente são definidas apenas em processos iniciados na janela


de comando em que foram definidas. Elas não serão lidas por aplicativos Web iniciados
com o Visual Studio.

Com o Visual Studio 2019 e posteriores, você pode especificar variáveis de ambiente
usando o diálogo Perfis de Inicialização.

Os comandos setx a seguir podem ser usados para definir as chaves de ambiente e os
valores no Windows. Diferente de set , as configurações setx são persistentes. /M
define a variável no ambiente do sistema. Se o comutador /M não for usado, uma
variável de ambiente do usuário será definida.

CLI do .NET

setx SecretKey "Secret key from setx environment" /M


setx TransientFaultHandlingOptions__Enabled "true" /M
setx TransientFaultHandlingOptions__AutoRetryDelay "00:00:05" /M

Para testar se os comandos anteriores substituem alguma configuração appsettings.json


e appsettings. Environment .json:

Com o Visual Studio: saia e reinicie o Visual Studio.


Com a CLI: inicie uma nova janela de comando e insira dotnet run .

Prefixos
Para especificar um prefixo para variáveis de ambiente, chame AddEnvironmentVariables
com uma cadeia de caracteres:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.AddEnvironmentVariables(prefix: "CustomPrefix_");
using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

No código anterior:

config.AddEnvironmentVariables(prefix: "CustomPrefix_") é adicionado após os


provedores de configuração padrão. Para obter um exemplo de ordenação dos
provedores de configuração, confira Provedor de configuração XML.
As variáveis de ambiente definidas com o prefixo CustomPrefix_ substituem os
provedores de configuração padrão. Isso inclui variáveis de ambiente sem o
prefixo.

O prefixo é removido quando os pares de valores-chave de configuração são lidos.

A configuração padrão carrega variáveis de ambiente e argumentos de linha de


comando prefixados com DOTNET_ . O prefixo DOTNET_ é usado pelo .NET para
configuração de host e de aplicativo, mas não para configuração de usuário.

Para obter mais informações sobre configuração de host e aplicativo, confira Host
genérico do .NET.

Prefixos de cadeia de conexão


A API de configuração tem regras de processamento especiais para quatro variáveis de
ambiente de cadeia de conexão. Essas cadeias de conexão estão envolvidas na
configuração de cadeias de conexão do Azure para o ambiente do aplicativo. As
variáveis de ambiente com os prefixos mostrados na tabela são carregadas no aplicativo
com a configuração padrão ou quando nenhum prefixo é fornecido ao
AddEnvironmentVariables .
Prefixo da cadeia de conexão Provedor

CUSTOMCONNSTR_ Provedor personalizado

MYSQLCONNSTR_ MySQL

SQLAZURECONNSTR_ Banco de Dados SQL do Azure

SQLCONNSTR_ SQL Server

Quando uma variável de ambiente for descoberta e carregada na configuração com


qualquer um dos quatro prefixos mostrados na tabela:

A chave de configuração é criada removendo o prefixo da variável de ambiente e


adicionando uma seção de chave de configuração ( ConnectionStrings ).
Um novo par chave-valor de configuração é criado para representar o provedor de
conexão de banco de dados (exceto para CUSTOMCONNSTR_ , que não tem um
provedor indicado).

Chave de variável de Chave de configuração Entrada de configuração do


ambiente convertida provedor

CUSTOMCONNSTR_{KEY} ConnectionStrings:{KEY} Entrada de configuração não criada.

MYSQLCONNSTR_{KEY} ConnectionStrings:{KEY} Chave: ConnectionStrings:


{KEY}_ProviderName :
Valor: MySql.Data.MySqlClient

SQLAZURECONNSTR_{KEY} ConnectionStrings:{KEY} Chave: ConnectionStrings:


{KEY}_ProviderName :
Valor: System.Data.SqlClient

SQLCONNSTR_{KEY} ConnectionStrings:{KEY} Chave: ConnectionStrings:


{KEY}_ProviderName :
Valor: System.Data.SqlClient

Variáveis de ambiente definidas no launchSettings.json


As variáveis de ambiente definidas no launchSettings.json substituem as definidas no
ambiente do sistema.

Configurações do Serviço de Aplicativo do Azure


Em Serviço de Aplicativo do Azure , selecione Nova configuração de aplicativo na
página Definições>Configurações. As configurações do aplicativo do Serviço de
Aplicativo do Azure são:

Criptografadas em repouso e transmitidas por um canal criptografado.


Expostas como variáveis de ambiente.

Provedor de configuração de linha de comando


Usando a configuração padrão, o CommandLineConfigurationProvider carrega a
configuração dos pares chave-valor do argumento da linha de comando após as
seguintes fontes de configuração:

Arquivos appsettings.json e appsettings. Environment .json.


Segredos do aplicativo (Gerenciador de Segredos) no ambiente Development .
Variáveis de ambiente.

Por padrão, os valores de configuração definidos na linha de comando substituem os


valores de configuração definidos com todos os outros provedores de configuração.

Com o Visual Studio 2019 e posteriores, você pode especificar argumentos de linha de
comando usando o diálogo Perfis de Inicialização.


Argumentos de linha de comando
O comando a seguir define chaves e valores usando = :

CLI do .NET

dotnet run SecretKey="Secret key from command line"

O comando a seguir define chaves e valores usando / :

CLI do .NET

dotnet run /SecretKey "Secret key set from forward slash"

O comando a seguir define chaves e valores usando -- :

CLI do .NET

dotnet run --SecretKey "Secret key set from double hyphen"

O valor da chave:

Deve seguir = , ou a chave deve ter um prefixo de -- ou / quando o valor vier


após um espaço.
Não é necessário se = for usado. Por exemplo, SomeKey= .

No mesmo comando, não combine pares chave-valor do argumento de linha de


comando que usam um sinal de = l com pares chave-valor que usam um espaço.

Provedor de configuração de chave por arquivo


O KeyPerFileConfigurationProvider usa arquivos do diretório como pares chave-valor de
configuração. A chave é o nome do arquivo. O valor é o conteúdo do arquivo. O
provedor de configuração de chave por arquivo é usado em cenários de hospedagem
do Docker.

Para ativar a configuração de chave por arquivo, chame o método de extensão


AddKeyPerFile em uma instância do ConfigurationBuilder. O directoryPath para os
arquivos deve ser um caminho absoluto.

As sobrecargas permitem especificar:

Um delegado Action<KeyPerFileConfigurationSource> que configura a origem.


Se o diretório é opcional, e o caminho para o diretório.

O sublinhado duplo ( __ ) é usado como um delimitador de chave de configuração em


nomes de arquivo. Por exemplo, o nome do arquivo Logging__LogLevel__System produz
a chave de configuração Logging:LogLevel:System .

Chame ConfigureAppConfiguration ao criar o host para especificar a configuração do


aplicativo:

C#

.ConfigureAppConfiguration((_, configuration) =>


{
var path = Path.Combine(
Directory.GetCurrentDirectory(), "path/to/files");

configuration.AddKeyPerFile(directoryPath: path, optional: true);


})

Provedor de configuração de memória


O MemoryConfigurationProvider usa uma coleção na memória como pares chave-valor
de configuração.

O código a seguir adiciona uma coleção de memória ao sistema de configuração:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.AddInMemoryCollection(
new Dictionary<string, string?>
{
["SecretKey"] = "Dictionary MyKey Value",
["TransientFaultHandlingOptions:Enabled"] = bool.TrueString,
["TransientFaultHandlingOptions:AutoRetryDelay"] = "00:00:07",
["Logging:LogLevel:Default"] = "Warning"
});

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();
No código anterior,
MemoryConfigurationBuilderExtensions.AddInMemoryCollection(IConfigurationBuilder,
IEnumerable<KeyValuePair<String,String>>) adiciona o provedor de memória após os
provedores de configuração padrão. Para obter um exemplo de ordenação dos
provedores de configuração, confira Provedor de configuração XML.

Confira também
Configuração no .NET
Host Genérico .NET
Implementar um provedor de configuração personalizado
Implementar um provedor de
configuração personalizado em .NET
Artigo • 22/02/2024

Há muitos provedores de configuração disponíveis para fontes de configuração comuns,


como arquivos JSON, XML e INI. Talvez seja necessário implementar um provedor de
configuração personalizado quando um dos provedores disponíveis não atender às
necessidades do aplicativo. Neste artigo, você aprenderá a implementar um provedor
de configuração personalizado que depende de um banco de dados como sua fonte de
configuração.

Provedor de Configuração personalizado


O aplicativo de exemplo demonstra como criar um provedor de configuração básico
que lê os pares chave-valor da configuração de um banco de dados usando Entity
Framework (EF) Core.

O provedor tem as seguintes características:

O banco de dados EF na memória é usado para fins de demonstração.


Para usar um banco de dados que requer uma cadeia de conexão, obtenha uma
cadeia de conexão de uma configuração intermediária.
O provedor lê uma tabela de banco de dados na configuração na inicialização. O
provedor não consulta o banco de dados em uma base por chave.
O recarregamento na alteração não está implementado, portanto, a atualização do
banco de dados após a inicialização do aplicativo não terá efeito sobre a
configuração do aplicativo.

Defina uma entidade de tipo de registro Settings para armazenar valores de


configuração no banco de dados. Por exemplo, você pode adicionar um arquivo
Settings.cs na pasta Models:

C#

namespace CustomProvider.Example.Models;

public record Settings(string Id, string? Value);

Para obter informações sobre tipos de registro, confira Tipos de registro do C#.
Adicione um EntityConfigurationContext para armazenar e acessar os valores
configurados.

Providers/EntityConfigurationContext.cs:

C#

using CustomProvider.Example.Models;
using Microsoft.EntityFrameworkCore;

namespace CustomProvider.Example.Providers;

public sealed class EntityConfigurationContext(string? connectionString) :


DbContext
{
public DbSet<Settings> Settings => Set<Settings>();

protected override void OnConfiguring(DbContextOptionsBuilder


optionsBuilder)
{
_ = connectionString switch
{
{ Length: > 0 } =>
optionsBuilder.UseSqlServer(connectionString),
_ => optionsBuilder.UseInMemoryDatabase("InMemoryDatabase")
};
}
}

Ao substituir OnConfiguring(DbContextOptionsBuilder), você pode usar a conexão de


banco de dados apropriada. Por exemplo, se uma cadeia de conexão for fornecida, você
poderá se conectar ao SQL Server, caso contrário, poderá contar com um banco de
dados na memória.

Crie uma classe que implementa IConfigurationSource.

Providers/EntityConfigurationSource.cs:

C#

using Microsoft.Extensions.Configuration;

namespace CustomProvider.Example.Providers;

public sealed class EntityConfigurationSource(


string? connectionString) : IConfigurationSource
{
public IConfigurationProvider Build(IConfigurationBuilder builder) =>
new EntityConfigurationProvider(connectionString);
}

Crie o provedor de configuração personalizado através da herança de


ConfigurationProvider. O provedor de configuração inicializa o banco de dados quando
ele está vazio. Como as chaves de configuração não diferenciam maiúsculas de
minúsculas, o dicionário usado para inicializar o banco de dados é criado com o
comparador que não diferencia maiúsculas de minúsculas
(StringComparer.OrdinalIgnoreCase).

Providers/EntityConfigurationProvider.cs:

C#

using CustomProvider.Example.Models;
using Microsoft.Extensions.Configuration;

namespace CustomProvider.Example.Providers;

public sealed class EntityConfigurationProvider(


string? connectionString)
: ConfigurationProvider
{
public override void Load()
{
using var dbContext = new
EntityConfigurationContext(connectionString);

dbContext.Database.EnsureCreated();

Data = dbContext.Settings.Any()
? dbContext.Settings.ToDictionary(
static c => c.Id,
static c => c.Value, StringComparer.OrdinalIgnoreCase)
: CreateAndSaveDefaultValues(dbContext);
}

static Dictionary<string, string?> CreateAndSaveDefaultValues(


EntityConfigurationContext context)
{
var settings = new Dictionary<string, string?>(
StringComparer.OrdinalIgnoreCase)
{
["WidgetOptions:EndpointId"] = "b3da3c4c-9c4e-4411-bc4d-
609e2dcc5c67",
["WidgetOptions:DisplayLabel"] = "Widgets Incorporated, LLC.",
["WidgetOptions:WidgetRoute"] = "api/widgets"
};

context.Settings.AddRange(
[.. settings.Select(static kvp => new Settings(kvp.Key,
kvp.Value))]);

context.SaveChanges();

return settings;
}
}

Um método de extensão AddEntityConfiguration permite adicionar a fonte de


configuração à instância subjacente ConfigurationManager .

Extensions/ConfigurationManagerExtensions.cs:

C#

using CustomProvider.Example.Providers;

namespace Microsoft.Extensions.Configuration;

public static class ConfigurationManagerExtensions


{
public static ConfigurationManager AddEntityConfiguration(
this ConfigurationManager manager)
{
var connectionString =
manager.GetConnectionString("WidgetConnectionString");

IConfigurationBuilder configBuilder = manager;


configBuilder.Add(new EntityConfigurationSource(connectionString));

return manager;
}
}

Como o ConfigurationManager é uma implementação de IConfigurationBuilder e


IConfigurationRoot, o método de extensão pode acessar a configuração de cadeias de
conexão e adicionar o EntityConfigurationSource .

O código a seguir mostra como usar o EntityConfigurationProvider personalizado em


Program.cs:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using CustomProvider.Example;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.AddEntityConfiguration();

builder.Services.Configure<WidgetOptions>(
builder.Configuration.GetSection("WidgetOptions"));

using IHost host = builder.Build();

WidgetOptions options =
host.Services.GetRequiredService<IOptions<WidgetOptions>>().Value;
Console.WriteLine($"DisplayLabel={options.DisplayLabel}");
Console.WriteLine($"EndpointId={options.EndpointId}");
Console.WriteLine($"WidgetRoute={options.WidgetRoute}");

await host.RunAsync();
// Sample output:
// WidgetRoute=api/widgets
// EndpointId=b3da3c4c-9c4e-4411-bc4d-609e2dcc5c67
// DisplayLabel=Widgets Incorporated, LLC.

Consumir provedor
Para consumir o provedor de configuração personalizado, você pode usar o padrão de
opções. Com o aplicativo de exemplo em vigor, defina um objeto de opções para
representar as configurações do widget.

C#

namespace CustomProvider.Example;

public class WidgetOptions


{
public required Guid EndpointId { get; set; }

public required string DisplayLabel { get; set; } = null!;

public required string WidgetRoute { get; set; } = null!;


}

Uma chamada para Configure registra uma instância de configuração à qual TOptions
se associa.

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using CustomProvider.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.AddEntityConfiguration();

builder.Services.Configure<WidgetOptions>(
builder.Configuration.GetSection("WidgetOptions"));

using IHost host = builder.Build();

WidgetOptions options =
host.Services.GetRequiredService<IOptions<WidgetOptions>>().Value;
Console.WriteLine($"DisplayLabel={options.DisplayLabel}");
Console.WriteLine($"EndpointId={options.EndpointId}");
Console.WriteLine($"WidgetRoute={options.WidgetRoute}");

await host.RunAsync();
// Sample output:
// WidgetRoute=api/widgets
// EndpointId=b3da3c4c-9c4e-4411-bc4d-609e2dcc5c67
// DisplayLabel=Widgets Incorporated, LLC.

O código anterior configura o objeto WidgetOptions da seção "WidgetOptions" da


configuração. Isso habilita o padrão de opções, expondo uma representação
IOptions<WidgetOptions> pronta para injeção de dependência das configurações de EF.

As opções são fornecidas, em última análise, do provedor de configuração


personalizado.

Confira também
Configuração em .NET
Provedores de configuração em .NET
Padrão de opções no .NET
Injeção de dependência no .NET

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso  Fornecer comentários sobre o
guia para colaboradores.
produto
Padrão de opções no .NET
Artigo • 24/01/2024

O padrão de opções usa classes para fornecer acesso fortemente tipado a grupos de
configurações relacionadas. Quando as definições de configuração são isoladas por
cenário em classes separadas, o aplicativo segue dois princípios importantes de
engenharia de software:

O ISP (Princípio de Segregação da Interface) ou Encapsulamento: os cenários


(classes) que dependem das definições de configuração dependem apenas das
definições de configuração usadas por eles.
Separação de Interesses: as configurações para diferentes partes do aplicativo não
são dependentes nem acopladas entre si.

As opções também fornecem um mecanismo para validar os dados da configuração.


Para obter mais configurações, consulte a seção Validação de opções.

Associar configuração hierárquica


A maneira preferencial de ler os valores de configuração relacionados é usando o
padrão de opções. O padrão de opções é possível por meio da interface
IOptions<TOptions>, em que o parâmetro TOptions de tipo genérico é restrito a um
class . O IOptions<TOptions> pode ser fornecido posteriormente por meio de injeção

de dependência. Para obter mais informações, consulte Injeção de dependência no .NET.

Por exemplo, para ler os valores de configuração destacados de um arquivo


appsettings.json:

JSON

{
"SecretKey": "Secret key value",
"TransientFaultHandlingOptions": {
"Enabled": true,
"AutoRetryDelay": "00:00:07"
},
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
Crie a seguinte classe TransientFaultHandlingOptions :

C#

public sealed class TransientFaultHandlingOptions


{
public bool Enabled { get; set; }
public TimeSpan AutoRetryDelay { get; set; }
}

Ao usar o padrão de opções, uma classe de opções:

Precisa ser não abstrata e ter um construtor público sem parâmetros


Contém propriedades públicas de leitura e gravação a serem associadas (os
campos não estão associados)

O código a seguir faz parte do arquivo C# Program.cs e:

Chama ConfigurationBinder.Bind para associar a classe


TransientFaultHandlingOptions à seção "TransientFaultHandlingOptions" .

Exibe os dados de configuração.

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using ConsoleJson.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Configuration.Sources.Clear();

IHostEnvironment env = builder.Environment;

builder.Configuration
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", true, true);

TransientFaultHandlingOptions options = new();


builder.Configuration.GetSection(nameof(TransientFaultHandlingOptions))
.Bind(options);

Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{options.AutoRetryDelay}");

using IHost host = builder.Build();

// Application code should start here.


await host.RunAsync();

// <Output>
// Sample output:

No código anterior, o arquivo de configuração JSON tem sua seção


"TransientFaultHandlingOptions" associada à instância TransientFaultHandlingOptions .

Isso hidrata as propriedades de objetos C# com os valores correspondentes da


configuração.

ConfigurationBinder.Get<T> associa e retorna o tipo especificado.


ConfigurationBinder.Get<T> pode ser mais conveniente do que usar
ConfigurationBinder.Bind . O código a seguir mostra como usar

ConfigurationBinder.Get<T> com a classe TransientFaultHandlingOptions :

C#

var options =
builder.Configuration.GetSection(nameof(TransientFaultHandlingOptions))
.Get<TransientFaultHandlingOptions>();

Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{options.AutoRetryDelay}");

No código anterior, o ConfigurationBinder.Get<T> é usado para adquirir uma instância


do objeto TransientFaultHandlingOptions com seus valores de propriedade
preenchidos usando a configuração subjacente.

) Importante

A classe ConfigurationBinder expõe várias APIs, como .Bind(object instance) e


.Get<T>() que não são restritas a class . Ao usar qualquer uma das interfaces de

opções, você precisa cumprir as restrições de classe de opções mencionadas


anteriormente.

Uma abordagem alternativa ao usar o padrão de opções é associar a seção


"TransientFaultHandlingOptions" e adicioná-la ao contêiner do serviço de injeção de
dependência. No código a seguir, TransientFaultHandlingOptions é adicionada ao
contêiner de serviço com Configure e associada à configuração:
C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.Configure<TransientFaultHandlingOptions>(
builder.Configuration.GetSection(
key: nameof(TransientFaultHandlingOptions)));

No exemplo anterior, builder é uma instância de HostApplicationBuilder.

 Dica

O parâmetro key é o nome da seção de configuração a ser pesquisada. Ele não


precisa corresponder ao nome do tipo que o representa. Por exemplo, você pode
ter uma seção nomeada "FaultHandling" e ela pode ser representada pela classe
TransientFaultHandlingOptions . Nesse caso, você passará "FaultHandling" para a

GetSection função em vez disso. O operador nameof é usado como uma


conveniência quando a seção nomeada corresponde ao tipo ao qual corresponde.

Usando o código anterior, o código a seguir lê as opções de posição:

C#

using Microsoft.Extensions.Options;

namespace ConsoleJson.Example;

public sealed class ExampleService(IOptions<TransientFaultHandlingOptions>


options)
{
private readonly TransientFaultHandlingOptions _options = options.Value;

public void DisplayValues()


{
Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{_options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{_options.AutoRetryDelay}");
}
}

No código anterior, as alterações no arquivo de configuração JSON após a inicialização


do aplicativo não são lidas. Para ler as alterações após o início do aplicativo, use
IOptionsSnapshot ou IOptionsMonitor para monitorar as alterações conforme elas
ocorrem e reagir adequadamente.
Interfaces de opções
IOptions<TOptions>:

Não é compatível com:


Leitura de dados de configuração após o início do aplicativo.
Opções nomeadas
É registrado como singleton e pode ser injetado em qualquer tempo de vida do
serviço.

IOptionsSnapshot<TOptions>:

É útil em cenários em que as opções devem ser recalculadas em cada resolução de


injeção, em tempos de vida com escopo ou transitórios. Para obter mais
informações, consulte Usar IOptionsSnapshot para ler dados atualizados.
É registrado como Escopo e, portanto, não pode ser injetado em um serviço
Singleton.
Permite opções nomeadas

IOptionsMonitor<TOptions>:

O é usado para recuperar as opções e gerenciar notificações de opções para


instâncias de TOptions .
É registrado como singleton e pode ser injetado em qualquer tempo de vida do
serviço.
Suporte:
Notificações de alteração
Opções nomeadas
Configuração recarregável
Invalidação seletiva de opções (IOptionsMonitorCache<TOptions>)

O IOptionsFactory<TOptions> é responsável por criar novas instâncias de opções. Ele


tem um único método Create. A implementação padrão usa todos os
IConfigureOptions<TOptions> e IPostConfigureOptions<TOptions> registrados e
executa todas as configurações primeiro, seguidas da pós-configuração. Ela faz
distinção entre IConfigureNamedOptions<TOptions> e IConfigureOptions<TOptions> e
chama apenas a interface apropriada.

O IOptionsMonitorCache<TOptions> é usado pelo IOptionsMonitor<TOptions> para


armazenar em cache as instâncias do TOptions . O IOptionsMonitorCache<TOptions>
invalida as instâncias de opções no monitor, de modo que o valor seja recalculado
(TryRemove). Os valores podem ser manualmente inseridos com TryAdd. O método
Clear é usado quando todas as instâncias nomeadas devem ser recriadas sob demanda.
IOptionsChangeTokenSource<TOptions> é usado para efetuar fetch de IChangeToken
que rastreia as alterações na instância TOptions subjacente. Para obter mais
informações sobre primitivos de token de alteração, consulte Alterar notificações.

Benefícios das interfaces de opções


O uso de um tipo de wrapper genérico oferece a capacidade de desacoplar o tempo de
vida da opção do contêiner de DI. A interface IOptions<TOptions>.Value fornece uma
camada de abstração, incluindo restrições genéricas, em seu tipo de opções. Isso
oferece os seguintes benefícios:

A avaliação da instância de configuração de T é adiada para o acesso de , em vez


de IOptions<TOptions>.Value, quando ela é injetada. Isso é importante porque
você pode consumir a opção T de vários lugares e escolher a semântica de vida
sem alterar nada sobre T .
Ao registrar opções de tipo T , você não precisa registrar explicitamente o tipo T .
Essa é uma conveniência quando você está criando uma biblioteca com padrões
simples e não quer forçar o chamador a registrar opções no contêiner de DI com
um tempo de vida específico.
Do ponto de vista da API, ela permite restrições no tipo T (nesse caso, T é restrita
a um tipo de referência).

Usar IOptionsSnapshot para ler dados


atualizados
Ao usar IOptionsSnapshot<TOptions>, as opções são calculadas uma vez por
solicitação, quando acessadas e armazenadas em cache durante o tempo de vida da
solicitação. As alterações na configuração são lidas depois que o aplicativo é iniciado ao
usar provedores de configuração que permitem a leitura de valores de configuração
atualizados.

A diferença entre IOptionsMonitor e IOptionsSnapshot é que:

IOptionsMonitor é um serviço singleton que recupera valores de opção atuais a

qualquer momento, o que é especialmente útil em dependências singleton.


IOptionsSnapshot é um serviço com escopo e fornece um instantâneo das opções

no momento em que o IOptionsSnapshot<T> objeto é construído. Os instantâneos


de opções são projetados para uso com dependências transitórias e com escopo.

O código a seguir usa IOptionsSnapshot<TOptions>.


C#

using Microsoft.Extensions.Options;

namespace ConsoleJson.Example;

public sealed class


ScopedService(IOptionsSnapshot<TransientFaultHandlingOptions> options)
{
private readonly TransientFaultHandlingOptions _options = options.Value;

public void DisplayValues()


{
Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{_options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{_options.AutoRetryDelay}");
}
}

O código a seguir registra uma instância de configuração que


TransientFaultHandlingOptions associa a:

C#

builder.Services
.Configure<TransientFaultHandlingOptions>(
configurationRoot.GetSection(
nameof(TransientFaultHandlingOptions)));

No código anterior, o método Configure<TOptions> é usado para registrar uma instância


de configuração que TOptions associará, e atualiza as opções quando a configuração é
alterada.

IOptionsMonitor
Para usar o monitor de opções, os objetos de opções são configurados da mesma
maneira em uma seção de configuração.

C#

builder.Services
.Configure<TransientFaultHandlingOptions>(
configurationRoot.GetSection(
nameof(TransientFaultHandlingOptions)));

O exemplo a seguir usa IOptionsMonitor<TOptions>:


C#

using Microsoft.Extensions.Options;

namespace ConsoleJson.Example;

public sealed class


MonitorService(IOptionsMonitor<TransientFaultHandlingOptions> monitor)
{
public void DisplayValues()
{
TransientFaultHandlingOptions options = monitor.CurrentValue;

Console.WriteLine($"TransientFaultHandlingOptions.Enabled=
{options.Enabled}");
Console.WriteLine($"TransientFaultHandlingOptions.AutoRetryDelay=
{options.AutoRetryDelay}");
}
}

No código anterior, as alterações no arquivo de configuração JSON após o aplicativo


iniciar a leitura.

 Dica

Alguns sistemas de arquivos, como contêineres do Docker e compartilhamentos de


rede, podem não enviar notificações de alteração de forma confiável. Ao usar a
interface IOptionsMonitor<TOptions> nesses ambientes, defina a variável de
ambiente DOTNET_USE_POLLING_FILE_WATCHER como 1 ou true para sondar o
sistema de arquivos para obter alterações. O intervalo no qual as alterações são
sondadas é a cada quatro segundos e não é configurável.

Para obter mais informações sobre contêineres do Docker, consulte Colocar um


aplicativo .NET no contêiner.

Compatibilidade de opções nomeadas usando


IConfigureNamedOptions
Opções nomeadas:

São úteis quando várias seções de configuração se associam às mesmas


propriedades.
Diferencia maiúsculas de minúsculas.
Usando o seguinte arquivo appsettings.json:

JSON

{
"Features": {
"Personalize": {
"Enabled": true,
"ApiKey":
"aGEgaGEgeW91IHRob3VnaHQgdGhhdCB3YXMgcmVhbGx5IHNvbWV0aGluZw=="
},
"WeatherStation": {
"Enabled": true,
"ApiKey": "QXJlIHlvdSBhdHRlbXB0aW5nIHRvIGhhY2sgdXM/"
}
}
}

Em vez de criar duas classes para associar Features:Personalize e


Features:WeatherStation , a seguinte classe é usada para cada seção:

C#

public class Features


{
public const string Personalize = nameof(Personalize);
public const string WeatherStation = nameof(WeatherStation);

public bool Enabled { get; set; }


public string ApiKey { get; set; }
}

O código a seguir configura as opções nomeadas:

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

// Omitted for brevity...

builder.Services.Configure<Features>(
Features.Personalize,
builder.Configuration.GetSection("Features:Personalize"));

builder.Services.Configure<Features>(
Features.WeatherStation,
builder.Configuration.GetSection("Features:WeatherStation"));

O código a seguir mostra as opções nomeadas:


C#

public class sealed Service


{
private readonly Features _personalizeFeature;
private readonly Features _weatherStationFeature;

public Service(IOptionsSnapshot<Features> namedOptionsAccessor)


{
_personalizeFeature =
namedOptionsAccessor.Get(Features.Personalize);
_weatherStationFeature =
namedOptionsAccessor.Get(Features.WeatherStation);
}
}

Todas as opções são instâncias nomeadas. As instâncias IConfigureOptions<TOptions>


existentes são tratadas como sendo direcionadas à instância Options.DefaultName , que é
string.Empty . IConfigureNamedOptions<TOptions> também implementa

IConfigureOptions<TOptions>. A implementação padrão de


IOptionsFactory<TOptions> tem lógica para usar cada um de forma adequada. A opção
nomeada null é usada para direcionar todas as instâncias nomeadas, em vez de uma
instância nomeada específica. ConfigureAll e PostConfigureAll usam essa convenção.

API OptionsBuilder
OptionsBuilder<TOptions> é usada para configurar instâncias TOptions . OptionsBuilder
simplifica a criação de opções nomeadas, pois é apenas um único parâmetro para a
chamada AddOptions<TOptions>(string optionsName) inicial, em vez de aparecer em
todas as chamadas subsequentes. A validação de opções e as sobrecargas
ConfigureOptions que aceitam dependências de serviço só estão disponíveis por meio

de OptionsBuilder .

OptionsBuilder é usado na seção Validação de opções.

Usar os serviços de injeção de dependência


para configurar as opções
Os serviços podem ser acessados de injeção de dependência ao configurar as opções de
duas maneiras:
Passar um delegado de configuração para Configurar em
OptionsBuilder<TOptions>. OptionsBuilder<TOptions> oferece sobrecargas de
Configurar que permitem usar até cinco serviços para configurar opções:

C#

builder.Services
.AddOptions<MyOptions>("optionalName")
.Configure<ExampleService, ScopedService, MonitorService>(
(options, es, ss, ms) =>
options.Property = DoSomethingWith(es, ss, ms));

Criar um tipo que implementa IConfigureOptions<TOptions> ou


IConfigureNamedOptions<TOptions> e registra o tipo como um serviço.

É recomendável transmitir um delegado de configuração para Configurar, já que a


criação de um serviço é algo mais complexo. A criação de um tipo é equivalente ao que
a estrutura faz ao chamar Configurar. Chamar Configure registra um genérico transitório
IConfigureNamedOptions<TOptions>, que tem um construtor que aceita os tipos de
serviço genérico especificados.

Validação de opções
A validação de opções permite que valores de opção sejam validados.

Usando o seguinte arquivo appsettings.json:

JSON

{
"MyCustomSettingsSection": {
"SiteTitle": "Amazing docs from Awesome people!",
"Scale": 10,
"VerbosityLevel": 32
}
}

A classe a seguir associa-se à seção de configuração "MyCustomSettingsSection" e aplica


algumas regras de DataAnnotations :

C#

using System.ComponentModel.DataAnnotations;

namespace ConsoleJson.Example;
public sealed class SettingsOptions
{
public const string ConfigurationSectionName =
"MyCustomSettingsSection";

[Required]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
public required string SiteTitle { get; set; }

[Required]
[Range(0, 1_000,
ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public required int Scale { get; set; }

[Required]
public required int VerbosityLevel { get; set; }
}

Na classe anterior SettingsOptions , a propriedade ConfigurationSectionName contém o


nome da seção de configuração à qual associar. Nesse cenário, o objeto de opções
fornece o nome da sua seção de configuração.

 Dica

O nome da seção de configuração é independente do objeto de configuração ao


qual ele está associando. Em outras palavras, uma seção de configuração nomeada
"FooBarOptions" pode ser associada a um objeto de opções chamado ZedOptions .

Embora possa ser comum nomeá-los da mesma forma, isso não é necessário e
pode realmente causar conflitos de nome.

O seguinte código:

Chamadas AddOptions para obter um OptionsBuilder<TOptions> que se associa à


classe SettingsOptions .
Chamadas ValidateDataAnnotations para habilitar a validação usando
DataAnnotations .

C#

builder.Services
.AddOptions<SettingsOptions>()

.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations();
O método de extensão ValidateDataAnnotations é definido no pacote
Microsoft.Extensions.Options.DataAnnotations do NuGet.

O seguinte código exibe os valores de configuração ou relata erros de validação:

C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace ConsoleJson.Example;

public sealed class ValidationService


{
private readonly ILogger<ValidationService> _logger;
private readonly IOptions<SettingsOptions> _config;

public ValidationService(
ILogger<ValidationService> logger,
IOptions<SettingsOptions> config)
{
_config = config;
_logger = logger;

try
{
SettingsOptions options = _config.Value;
}
catch (OptionsValidationException ex)
{
foreach (string failure in ex.Failures)
{
_logger.LogError("Validation error: {FailureMessage}",
failure);
}
}
}
}

O código a seguir aplica uma regra de validação mais complexa usando um


representante:

C#

builder.Services
.AddOptions<SettingsOptions>()

.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Scale != 0)
{
return config.VerbosityLevel > config.Scale;
}

return true;
}, "VerbosityLevel must be > than Scale.");

A validação ocorre em tempo de execução, mas você pode configurá-la para ocorrer na
inicialização encadeando uma chamada para ValidateOnStart :

C#

builder.Services
.AddOptions<SettingsOptions>()

.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Scale != 0)
{
return config.VerbosityLevel > config.Scale;
}

return true;
}, "VerbosityLevel must be > than Scale.")
.ValidateOnStart();

A partir do .NET 8, você pode usar uma API alternativa,


AddOptionsWithValidateOnStart<TOptions>(IServiceCollection, String), que permite a
validação no início para um tipo de opções específico:

C#

builder.Services
.AddOptionsWithValidateOnStart<SettingsOptions>()

.Bind(Configuration.GetSection(SettingsOptions.ConfigurationSectionName))
.ValidateDataAnnotations()
.Validate(config =>
{
if (config.Scale != 0)
{
return config.VerbosityLevel > config.Scale;
}

return true;
}, "VerbosityLevel must be > than Scale.");
IValidateOptions para validação complexa

A classe a seguir implementa IValidateOptions<TOptions>:

C#

using System.Text;
using System.Text.RegularExpressions;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;

namespace ConsoleJson.Example;

sealed partial class ValidateSettingsOptions(


IConfiguration config)
: IValidateOptions<SettingsOptions>
{
public SettingsOptions? Settings { get; private set; } =
config.GetSection(SettingsOptions.ConfigurationSectionName)
.Get<SettingsOptions>();

public ValidateOptionsResult Validate(string? name, SettingsOptions


options)
{
StringBuilder? failure = null;

if (!ValidationRegex().IsMatch(options.SiteTitle))
{
(failure ??= new()).AppendLine($"{options.SiteTitle} doesn't
match RegEx");
}

if (options.Scale is < 0 or > 1_000)


{
(failure ??= new()).AppendLine($"{options.Scale} isn't within
Range 0 - 1000");
}

if (Settings is { Scale: 0 } && Settings.VerbosityLevel <=


Settings.Scale)
{
(failure ??= new()).AppendLine("VerbosityLevel must be > than
Scale.");
}

return failure is not null


? ValidateOptionsResult.Fail(failure.ToString())
: ValidateOptionsResult.Success;
}

[GeneratedRegex("^[a-zA-Z''-'\\s]{1,40}$")]
private static partial Regex ValidationRegex();
}

IValidateOptions permite mover o código de validação para uma classe.

7 Observação

Este exemplo de código depende do pacote


Microsoft.Extensions.Configuration.Json do NuGet.

Usando o código anterior, a validação está habilitada ao configurar os serviços com o


seguinte código:

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

// Omitted for brevity...

builder.Services.Configure<SettingsOptions>(
builder.Configuration.GetSection(
SettingsOptions.ConfigurationSectionName));

builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton
<IValidateOptions<SettingsOptions>, ValidateSettingsOptions>());

Pós-configuração de opções
Defina a pós-configuração com IPostConfigureOptions<TOptions>. A pós-configuração
é executada depois que todas as configurações IConfigureOptions<TOptions> ocorrem
e pode ser útil em cenários em que é necessário substituir a configuração:

C#

builder.Services.PostConfigure<CustomOptions>(customOptions =>
{
customOptions.Option1 = "post_configured_option1_value";
});

O PostConfigure está disponível para pós-configurar opções nomeadas:

C#
builder.Services.PostConfigure<CustomOptions>("named_options_1",
customOptions =>
{
customOptions.Option1 = "post_configured_option1_value";
});

Use PostConfigureAll para pós-configurar todas as instâncias de configuração:

C#

builder.Services.PostConfigureAll<CustomOptions>(customOptions =>
{
customOptions.Option1 = "post_configured_option1_value";
});

Confira também
Configuração em .NET
Diretrizes de padrão de opções para autores da biblioteca .NET

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Geração de origem de validação de
opções de tempo de compilação
Artigo • 22/12/2023

No padrão de opções, vários métodos para validar opções são apresentados. Esses
métodos incluem usar atributos de anotação de dados ou empregar um validador
personalizado. Os atributos de anotação de dados são validados em tempo de execução
e podem incorrer em custos de desempenho. Este artigo demonstra como utilizar o
gerador de origem de validação de opções para produzir código de validação otimizado
em tempo de compilação.

Geração automática de implementação de


IValidateOptions
O artigo de padrão de opções ilustra como implementar a interface
IValidateOptions<TOptions> para validar opções. O gerador de origem de validação de
opções pode criar automaticamente a implementação da interface IValidateOptions
aproveitando atributos de anotação de dados na classe de opções.

O conteúdo a seguir usa o exemplo de atributos de anotação mostrado no Padrão de


opções e o converte para usar o gerador de origem de validação de opções.

Usando o seguinte arquivo appsettings.json:

JSON

{
"MyCustomSettingsSection": {
"SiteTitle": "Amazing docs from awesome people!",
"Scale": 10,
"VerbosityLevel": 32
}
}

A classe a seguir associa-se à seção de configuração "MyCustomSettingsSection" e aplica


algumas regras de DataAnnotations :

C#

using System.ComponentModel.DataAnnotations;

namespace ConsoleJson.Example;
public sealed class SettingsOptions
{
public const string ConfigurationSectionName =
"MyCustomSettingsSection";

[Required]
[RegularExpression(@"^[a-zA-Z''-'\s]{1,40}$")]
public required string SiteTitle { get; set; }

[Required]
[Range(0, 1_000,
ErrorMessage = "Value for {0} must be between {1} and {2}.")]
public required int? Scale { get; set; }

[Required]
public required int? VerbosityLevel { get; set; }
}

Na classe anterior SettingsOptions , a propriedade ConfigurationSectionName contém o


nome da seção de configuração à qual associar. Nesse cenário, o objeto de opções
fornece o nome da sua seção de configuração. Os seguintes atributos de anotação de
dados são usados:

RequiredAttribute: especifica que a propriedade é necessária.


RegularExpressionAttribute: especifica que o valor da propriedade deve
corresponder ao padrão de expressão regular especificado.
RangeAttribute: especifica que o valor da propriedade deve estar dentro de um
intervalo especificado.

 Dica

Além do RequiredAttribute , as propriedades também usam o modificador


necessário. Isso ajuda a garantir que os consumidores do objeto de opções não se
esqueçam de definir o valor da propriedade, embora ele não esteja relacionado ao
recurso de geração de origem de validação.

O código a seguir exemplifica como associar a seção de configuração ao objeto de


opções e validar as anotações de dados:

C#

using ConsoleJson.Example;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services
.AddOptions<SettingsOptions>()

.Bind(builder.Configuration.GetSection(SettingsOptions.ConfigurationSectionN
ame));

builder.Services
.AddSingleton<IValidateOptions<SettingsOptions>,
ValidateSettingsOptions>();

using IHost app = builder.Build();

var settingsOptions =
app.Services.GetRequiredService<IOptions<SettingsOptions>>().Value;

await app.RunAsync();

Aproveitando a geração de fonte de tempo de compilação para validação de opções,


você pode gerar código de validação com otimização de desempenho e eliminar a
necessidade de reflexão, resultando em uma compilação de aplicativo compatível com
AOT mais suave. O código a seguir demonstra como usar o gerador de origem de
validação de opções:

C#

using Microsoft.Extensions.Options;

namespace ConsoleJson.Example;

[OptionsValidator]
public partial class ValidateSettingsOptions :
IValidateOptions<SettingsOptions>
{
}

A presença do OptionsValidatorAttribute em uma classe parcial vazia instrui o gerador


de origem de validação de opções a criar a implementação da interface
IValidateOptions que valida SettingsOptions . O código gerado pelo gerador de

origem de validação de opções será semelhante ao seguinte exemplo:

C#

// <auto-generated/>
#nullable enable
#pragma warning disable CS1591 // Compensate for
https://github.com/dotnet/roslyn/issues/54103
namespace ConsoleJson.Example
{
partial class ValidateSettingsOptions
{
/// <summary>
/// Validates a specific named options instance (or all when
<paramref name="name"/> is <see langword="null" />).
/// </summary>
/// <param name="name">The name of the options instance being
validated.</param>
/// <param name="options">The options instance.</param>
/// <returns>Validation result.</returns>

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extension
s.Options.SourceGeneration", "8.0.9.3103")]

[System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessage("Trimming",
"IL2026:RequiresUnreferencedCode",
Justification = "The created ValidationContext object is used
in a way that never call reflection")]
public global::Microsoft.Extensions.Options.ValidateOptionsResult
Validate(string? name, global::ConsoleJson.Example.SettingsOptions options)
{

global::Microsoft.Extensions.Options.ValidateOptionsResultBuilder? builder =
null;
var context = new
global::System.ComponentModel.DataAnnotations.ValidationContext(options);
var validationResults = new
global::System.Collections.Generic.List<global::System.ComponentModel.DataAn
notations.ValidationResult>();
var validationAttributes = new
global::System.Collections.Generic.List<global::System.ComponentModel.DataAn
notations.ValidationAttribute>(2);

context.MemberName = "SiteTitle";
context.DisplayName = string.IsNullOrEmpty(name) ?
"SettingsOptions.SiteTitle" : $"{name}.SiteTitle";

validationAttributes.Add(global::__OptionValidationStaticInstances.__Attribu
tes.A1);

validationAttributes.Add(global::__OptionValidationStaticInstances.__Attribu
tes.A2);
if
(!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(o
ptions.SiteTitle, context, validationResults, validationAttributes))
{
(builder ??= new()).AddResults(validationResults);
}

context.MemberName = "Scale";
context.DisplayName = string.IsNullOrEmpty(name) ?
"SettingsOptions.Scale" : $"{name}.Scale";
validationResults.Clear();
validationAttributes.Clear();

validationAttributes.Add(global::__OptionValidationStaticInstances.__Attribu
tes.A1);

validationAttributes.Add(global::__OptionValidationStaticInstances.__Attribu
tes.A3);
if
(!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(o
ptions.Scale, context, validationResults, validationAttributes))
{
(builder ??= new()).AddResults(validationResults);
}

context.MemberName = "VerbosityLevel";
context.DisplayName = string.IsNullOrEmpty(name) ?
"SettingsOptions.VerbosityLevel" : $"{name}.VerbosityLevel";
validationResults.Clear();
validationAttributes.Clear();

validationAttributes.Add(global::__OptionValidationStaticInstances.__Attribu
tes.A1);
if
(!global::System.ComponentModel.DataAnnotations.Validator.TryValidateValue(o
ptions.VerbosityLevel, context, validationResults, validationAttributes))
{
(builder ??= new()).AddResults(validationResults);
}

return builder is null ?


global::Microsoft.Extensions.Options.ValidateOptionsResult.Success :
builder.Build();
}
}
}
namespace __OptionValidationStaticInstances
{

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extension
s.Options.SourceGeneration", "8.0.9.3103")]
file static class __Attributes
{
internal static readonly
global::System.ComponentModel.DataAnnotations.RequiredAttribute A1 = new
global::System.ComponentModel.DataAnnotations.RequiredAttribute();

internal static readonly


global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute A2
= new
global::System.ComponentModel.DataAnnotations.RegularExpressionAttribute(
"^[a-zA-Z''-'\\s]{1,40}$");

internal static readonly


__OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute A3 = new
__OptionValidationGeneratedAttributes.__SourceGen__RangeAttribute(
(int)0,
(int)1000)
{
ErrorMessage = "Value for {0} must be between {1} and {2}."
};
}
}
namespace __OptionValidationStaticInstances
{

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extension
s.Options.SourceGeneration", "8.0.9.3103")]
file static class __Validators
{
}
}
namespace __OptionValidationGeneratedAttributes
{

[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Extension
s.Options.SourceGeneration", "8.0.9.3103")]
[global::System.AttributeUsage(global::System.AttributeTargets.Property
| global::System.AttributeTargets.Field |
global::System.AttributeTargets.Parameter, AllowMultiple = false)]
file class __SourceGen__RangeAttribute :
global::System.ComponentModel.DataAnnotations.ValidationAttribute
{
public __SourceGen__RangeAttribute(int minimum, int maximum) :
base()
{
Minimum = minimum;
Maximum = maximum;
OperandType = typeof(int);
}
public __SourceGen__RangeAttribute(double minimum, double maximum) :
base()
{
Minimum = minimum;
Maximum = maximum;
OperandType = typeof(double);
}
public __SourceGen__RangeAttribute(global::System.Type type, string
minimum, string maximum) : base()
{
OperandType = type;
NeedToConvertMinMax = true;
Minimum = minimum;
Maximum = maximum;
}
public object Minimum { get; private set; }
public object Maximum { get; private set; }
public bool MinimumIsExclusive { get; set; }
public bool MaximumIsExclusive { get; set; }
public global::System.Type OperandType { get; }
public bool ParseLimitsInInvariantCulture { get; set; }
public bool ConvertValueInInvariantCulture { get; set; }
public override string FormatErrorMessage(string name) =>

string.Format(global::System.Globalization.CultureInfo.CurrentCulture,
GetValidationErrorMessage(), name, Minimum, Maximum);
private bool NeedToConvertMinMax { get; }
private bool Initialized { get; set; }
public override bool IsValid(object? value)
{
if (!Initialized)
{
if (Minimum is null || Maximum is null)
{
throw new global::System.InvalidOperationException("The
minimum and maximum values must be set to valid values.");
}
if (NeedToConvertMinMax)
{
System.Globalization.CultureInfo culture =
ParseLimitsInInvariantCulture ?
global::System.Globalization.CultureInfo.InvariantCulture :
global::System.Globalization.CultureInfo.CurrentCulture;
Minimum = ConvertValue(Minimum, culture) ?? throw new
global::System.InvalidOperationException("The minimum and maximum values
must be set to valid values.");
Maximum = ConvertValue(Maximum, culture) ?? throw new
global::System.InvalidOperationException("The minimum and maximum values
must be set to valid values.");
}
int cmp =
((global::System.IComparable)Minimum).CompareTo((global::System.IComparable)
Maximum);
if (cmp > 0)
{
throw new global::System.InvalidOperationException("The
maximum value '{Maximum}' must be greater than or equal to the minimum value
'{Minimum}'.");
}
else if (cmp == 0 && (MinimumIsExclusive ||
MaximumIsExclusive))
{
throw new
global::System.InvalidOperationException("Cannot use exclusive bounds when
the maximum value is equal to the minimum value.");
}
Initialized = true;
}

if (value is null or string { Length: 0 })


{
return true;
}

System.Globalization.CultureInfo formatProvider =
ConvertValueInInvariantCulture ?
global::System.Globalization.CultureInfo.InvariantCulture :
global::System.Globalization.CultureInfo.CurrentCulture;
object? convertedValue;

try
{
convertedValue = ConvertValue(value, formatProvider);
}
catch (global::System.Exception e) when (e is
global::System.FormatException or global::System.InvalidCastException or
global::System.NotSupportedException)
{
return false;
}

var min = (global::System.IComparable)Minimum;


var max = (global::System.IComparable)Maximum;

return
(MinimumIsExclusive ? min.CompareTo(convertedValue) < 0 :
min.CompareTo(convertedValue) <= 0) &&
(MaximumIsExclusive ? max.CompareTo(convertedValue) > 0 :
max.CompareTo(convertedValue) >= 0);
}
private string GetValidationErrorMessage()
{
return (MinimumIsExclusive, MaximumIsExclusive) switch
{
(false, false) => "The field {0} must be between {1} and
{2}.",
(true, false) => "The field {0} must be between {1}
exclusive and {2}.",
(false, true) => "The field {0} must be between {1} and {2}
exclusive.",
(true, true) => "The field {0} must be between {1} exclusive
and {2} exclusive.",
};
}
private object? ConvertValue(object? value,
System.Globalization.CultureInfo formatProvider)
{
if (value is string stringValue)
{
value = global::System.Convert.ChangeType(stringValue,
OperandType, formatProvider);
}
else
{
value = global::System.Convert.ChangeType(value,
OperandType, formatProvider);
}
return value;
}
}
}
O código gerado é otimizado para desempenho e não depende de reflexão. Também é
compatível com AOT. O código gerado é colocado em um arquivo chamado
Validators.g.cs.

7 Observação

Você não precisa executar nenhuma etapa adicional para habilitar o gerador de
origem de validação de opções. Ele é habilitado automaticamente por padrão
quando seu projeto faz referência a Microsoft.Extensions.Options versão 8 ou
posterior ou ao criar um aplicativo ASP.NET.

A única etapa que você precisa executar é adicionar o seguinte ao código de


inicialização:

C#

builder.Services
.AddSingleton<IValidateOptions<SettingsOptions>,
ValidateSettingsOptions>();

7 Observação

A chamada
OptionsBuilderDataAnnotationsExtensions.ValidateDataAnnotations<TOptions>
(OptionsBuilder<TOptions>) não é necessária ao usar o gerador de origem de
validação de opções.

Quando o aplicativo tenta acessar o objeto de opções, o código gerado para validação
de opções é executado para validar o objeto de opções. O snippet de código a seguir
ilustra como acessar o objeto de opções:

C#

var settingsOptions =
app.Services.GetRequiredService<IOptions<SettingsOptions>>().Value;

Atributos de anotação de dados substituídos


Após um exame detalhado do código gerado, você observará que os atributos de
anotação de dados originais, como RangeAttribute, que foram inicialmente aplicados à
propriedade SettingsOptions.Scale , foram substituídos por atributos personalizados
como __SourceGen__RangeAttribute . Essa substituição é feita porque RangeAttribute
depende da reflexão para validação. Por outro lado, __SourceGen__RangeAttribute é um
atributo personalizado otimizado para desempenho e não depende da reflexão,
tornando o código compatível com AOT. O mesmo padrão de substituição de atributo
será aplicado em MaxLengthAttribute, MinLengthAttribute e LengthAttribute além de
RangeAttribute.

Para qualquer pessoa que desenvolva atributos de anotação de dados personalizados, é


aconselhável evitar o uso de reflexão para validação. Em vez disso, é recomendável criar
um código fortemente tipado que não dependa de reflexão. Essa abordagem garante
uma compatibilidade tranquila com builds AOT.

Confira também
Padrão de opções

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Diretrizes de padrão de opções para
autores da biblioteca .NET
Artigo • 23/06/2023

Com a ajuda da injeção de dependência, registrar seus serviços e suas configurações


correspondentes pode usar o padrão de opções. O padrão de opções permite que os
consumidores de sua biblioteca (e seus serviços) exijam instâncias de interfaces de
opções em que TOptions é sua classe de opções. O consumo de opções de
configuração por meio de objetos fortemente tipados ajuda a garantir uma
representação de valor consistente, permite a validação com anotações de dados e evita
a análise manual dos valores de cadeia de caracteres. Há muitos provedores de
configuração para os consumidores de sua biblioteca usarem. Com esses provedores, os
consumidores podem configurar sua biblioteca de várias maneiras.

Como um autor da biblioteca .NET, você aprenderá orientações gerais sobre como
expor corretamente o padrão de opções aos consumidores de sua biblioteca. Há várias
maneiras de alcançar a mesma coisa, e várias considerações a serem feitas.

Convenções de nomenclatura
Por convenção, os métodos de extensão responsáveis por registrar serviços são
nomeados Add{Service} , em que {Service} é um nome significativo e descritivo. Os
métodos de extensão Add{Service} são comuns no ASP.NET Core e no .NET.

✔️CONSIDERE os nomes que diferenciem seu serviço de outras ofertas.

❌ NÃO use nomes que já fazem parte do ecossistema .NET de pacotes oficiais da
Microsoft.

✔️CONSIDERE a nomenclatura de classes estáticas que expõem métodos de extensão


como {Type}Extensions , em que {Type} é o tipo que você está estendendo.

Diretrizes de namespace
Os pacotes da Microsoft usam o namespace Microsoft.Extensions.DependencyInjection
para unificar o registro de várias ofertas de serviço.

✔️CONSIDERE um namespace que identifique claramente sua oferta de pacote.


❌ NÃO use o namespace Microsoft.Extensions.DependencyInjection para pacotes não

oficiais da Microsoft.

Construtor
Se o serviço puder trabalhar com configuração mínima ou não explícita, considere um
método de extensão sem parâmetros.

C#

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions


{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services)
{
services.AddOptions<LibraryOptions>()
.Configure(options =>
{
// Specify default option values
});

// Register lib services here...


// services.AddScoped<ILibraryService, DefaultLibraryService>();

return services;
}
}

No código anterior, o AddMyLibraryService :

Estende uma instância de IServiceCollection


Chamadas OptionsServiceCollectionExtensions.AddOptions<TOptions>
(IServiceCollection) com o parâmetro de tipo de LibraryOptions
Encadeia uma chamada para Configure, que especifica os valores de opção padrão

Parâmetro IConfiguration
Ao criar uma biblioteca que exponha muitas opções aos consumidores, convém
considerar a necessidade de um método de extensão de parâmetro IConfiguration . A
instância esperada IConfiguration deve ser definida como escopo para uma seção
nomeada da configuração usando a função IConfiguration.GetSection.
C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions


{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
IConfiguration namedConfigurationSection)
{
// Default library options are overridden
// by bound configuration values.
services.Configure<LibraryOptions>(namedConfigurationSection);

// Register lib services here...


// services.AddScoped<ILibraryService, DefaultLibraryService>();

return services;
}
}

 Dica

O método Configure<TOptions>(IServiceCollection, IConfiguration) faz parte do


pacote NuGet Microsoft.Extensions.Options.ConfigurationExtensions .

No código anterior, o AddMyLibraryService :

Estende uma instância de IServiceCollection


Define um IConfiguration parâmetro namedConfigurationSection
Chamadas Configure<TOptions>(IServiceCollection, IConfiguration) passando o
parâmetro de tipo genérico de LibraryOptions e a instância
namedConfigurationSection para configurar

Os consumidores nesse padrão fornecem a instância IConfiguration em escopo da


seção nomeada:

C#

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);


builder.Services.AddMyLibraryService(
builder.Configuration.GetSection("LibraryOptions"));

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

A chamada para .AddMyLibraryService a ser feita no método IServiceCollection.

Como autor da biblioteca, a especificação de valores padrão cabe a você.

7 Observação

É possível associar a configuração a uma instância de opções. No entanto, há o


risco de colisões de nome, o que causará erros. Além disso, ao associar
manualmente dessa forma, você limita o consumo do padrão de opções para
leitura uma vez. As alterações nas configurações não serão vinculadas novamente,
pois esses consumidores não poderão usar a interface IOptionsMonitor .

C#

services.AddOptions<LibraryOptions>()
.Configure<IConfiguration>(
(options, configuration) =>
configuration.GetSection("LibraryOptions").Bind(options));

Como alternativa, use o método de extensão BindConfiguration. Esse método de


extensão associa a configuração à instância de opções e também registra uma
fonte de token de alteração para a seção de configuração. Isso permite que os
consumidores usem a interface IOptionsMonitor.

Parâmetro de caminho da seção de


configuração
Os consumidores da biblioteca podem querer especificar o caminho da seção de
configuração para vincular o tipo TOptions subjacente. Nesse cenário, você define um
parâmetro string no método de extensão.

C#
using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions


{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
string configSectionPath)
{
services.AddOptions<SupportOptions>()
.BindConfiguration(configSectionPath)
.ValidateDataAnnotations()
.ValidateOnStart();

// Register lib services here...


// services.AddScoped<ILibraryService, DefaultLibraryService>();

return services;
}
}

No código anterior, o AddMyLibraryService :

Estende uma instância de IServiceCollection


Define um parâmetro string configSectionPath
Chamadas:
AddOptions com o parâmetro de tipo genérico SupportOptions
BindConfiguration com o parâmetro configSectionPath fornecido
ValidateDataAnnotations para habilitar a validação de anotação de dados
ValidateOnStart para impor a validação no início, em vez de no runtime

No próximo exemplo, o pacote NuGet Microsoft.Extensions.Options.DataAnnotations


é usado para habilitar a validação de anotação de dados. A classe SupportOptions é
definida da seguinte forma:

C#

using System.ComponentModel.DataAnnotations;

public sealed class SupportOptions


{
[Url]
public string? Url { get; set; }

[Required, EmailAddress]
public required string Email { get; set; }

[Required, DataType(DataType.PhoneNumber)]
public required string PhoneNumber { get; set; }
}

Imagine que o seguinte arquivo JSON appsettings.json seja usado:

JavaScript

{
"Support": {
"Url": "https://support.example.com",
"Email": "help@support.example.com",
"PhoneNumber": "+1(888)-SUPPORT"
}
}

Parâmetro Action<TOptions>
Os consumidores de sua biblioteca podem estar interessados em fornecer uma
expressão lambda que produz uma instância de sua classe de opções. Nesse cenário,
você define um parâmetro Action<LibraryOptions> em seu método de extensão.

C#

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions


{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
Action<LibraryOptions> configureOptions)
{
services.Configure(configureOptions);

// Register lib services here...


// services.AddScoped<ILibraryService, DefaultLibraryService>();

return services;
}
}

No código anterior, o AddMyLibraryService :

Estende uma instância de IServiceCollection


Define um Action<T> parâmetro configureOptions em que T é LibraryOptions
Chama Configure devido à ação configureOptions
Os consumidores nesse padrão fornecem uma expressão lambda (ou um delegado que
satisfaz o parâmetro Action<LibraryOptions> ):

C#

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(options =>
{
// User defined option values
// options.SomePropertyValue = ...
});

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

Parâmetro de instância de opções


Os consumidores de sua biblioteca podem preferir fornecer uma instância de opções
embutidas. Nesse cenário, você expõe um método de extensão que usa uma instância
do objeto de opções, LibraryOptions .

C#

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions


{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
LibraryOptions userOptions)
{
services.AddOptions<LibraryOptions>()
.Configure(options =>
{
// Overwrite default option values
// with the user provided options.
// options.SomeValue = userOptions.SomeValue;
});

// Register lib services here...


// services.AddScoped<ILibraryService, DefaultLibraryService>();
return services;
}
}

No código anterior, o AddMyLibraryService :

Estende uma instância de IServiceCollection


Chamadas OptionsServiceCollectionExtensions.AddOptions<TOptions>
(IServiceCollection) com o parâmetro de tipo de LibraryOptions
Encadeia uma chamada para Configure, que especifica valores de opção padrão
que podem ser substituídos da instância userOptions determinada

Os consumidores nesse padrão fornecem uma instância da classe LibraryOptions ,


definindo os valores de propriedade desejados embutidos:

C#

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(new LibraryOptions
{
// Specify option values
// SomePropertyValue = ...
});

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

Pós-configuração
Depois que todos os valores de opção de configuração forem associados ou
especificados, a funcionalidade pós-configuração estará disponível. Expondo o mesmo
Action<TOptions> parâmetro detalhado anteriormente, você pode optar por chamar
PostConfigure. A configuração de postagem é executada após todas as chamadas
.Configure . Há alguns motivos pelos quais você deseja considerar o uso de

PostConfigure :
Ordem de execução: você pode substituir todos os valores de configuração que
foram definidos nas chamadas .Configure .
Validação: você pode validar se os valores padrão foram definidos depois que
todas as outras configurações tiverem sido aplicadas.

C#

using Microsoft.Extensions.DependencyInjection;

namespace ExampleLibrary.Extensions.DependencyInjection;

public static class ServiceCollectionExtensions


{
public static IServiceCollection AddMyLibraryService(
this IServiceCollection services,
Action<LibraryOptions> configureOptions)
{
services.PostConfigure(configureOptions);

// Register lib services here...


// services.AddScoped<ILibraryService, DefaultLibraryService>();

return services;
}
}

No código anterior, o AddMyLibraryService :

Estende uma instância de IServiceCollection


Define um Action<T> parâmetro configureOptions em que T é LibraryOptions
Chama PostConfigure devido à ação configureOptions

Os consumidores nesse padrão fornecem uma expressão lambda (ou um delegado que
satisfaz o parâmetro Action<LibraryOptions> ), da mesma forma que fariam com o
Action<TOptions> parâmetro em um cenário de configuração que seja pós-postagem:

C#

using ExampleLibrary.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddMyLibraryService(options =>
{
// Specify option values
// options.SomePropertyValue = ...
});
using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

Confira também
Padrão de opções no .NET
Injeção de dependência no .NET
Diretrizes de injeção de dependência
Registro em log em C# e .NET
Artigo • 14/03/2024

O .NET dá suporte ao registro em log estruturado e de alto desempenho por meio da


API ILogger para ajudar a monitorar o comportamento do aplicativo e diagnosticar
problemas. Os logs poderão ser gravados em destinos diferentes se você configurar
provedores de registro em log diferentes. Os provedores básicos de registro em log são
integrados e também existem muitos provedores de terceiros disponíveis.

Introdução
Este primeiro exemplo mostra as noções básicas, mas é adequado apenas para um
aplicativo de console trivial. Na próxima seção, você verá como aprimorar o código
levando em conta a escala, o desempenho, a configuração e os padrões de
programação típicos.

C#

using Microsoft.Extensions.Logging;

using ILoggerFactory factory = LoggerFactory.Create(builder =>


builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

No exemplo anterior:

Cria um ILoggerFactory. O ILoggerFactory armazena toda a configuração que


determina para onde as mensagens de log são enviadas. Nesse caso, você
configura o provedor de registro em log do console para que as mensagens de log
sejam gravadas no console.
Cria um ILogger com uma categoria chamada "Programa". A categoria é uma
string associada a cada mensagem registrada em log pelo objeto ILogger . É

usada para agrupar mensagens de log da mesma classe (ou categoria) ao


pesquisar ou filtrar logs.
Chama o LogInformation para registrar uma mensagem no nível Information . O
nível de log indica a gravidade do evento registrado em log e é usado para filtrar e
deixar de fora as mensagens de log menos importantes. A inserção no log também
inclui um modelo de mensagem "Hello World! Logging is {Description}." e um
par de chave-valor Description = fun . O nome da chave (ou espaço reservado)
vem da palavra entre chaves no modelo e o valor vem do argumento de método
restante.

Este arquivo de projeto para este exemplo inclui dois pacotes NuGet:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0"
/>
<PackageReference Include="Microsoft.Extensions.Logging.Console"
Version="8.0.0" />
</ItemGroup>

</Project>

 Dica

Todo o código-fonte do exemplo de log está disponível no Navegador de


Exemplos para download. Para obter mais informações, confira Procurar exemplos
de código: log no .NET.

Registro em log em um aplicativo não trivial


Há várias alterações que você deve pensar em fazer no exemplo anterior ao registrar em
log em um cenário menos trivial:

Se o seu aplicativo estiver usando Injeção de Dependência (DI) ou um host como o


WebApplication ou um Host Genérico do ASP.NET, você deve usar objetos
ILoggerFactory e ILogger de seus respectivos contêineres de DI em vez de criá-

los diretamente. Para obter mais informações, confira Integração com DI e Hosts.

O registro em log da geração da origem do tempo de compilação costuma ser


uma alternativa melhor do que os métodos de extensão do ILogger , como
LogInformation . A geração da origem do registro em log oferece um melhor

desempenho, uma digitação mais sólida e evita a propagação de constantes de


string em todos os seus métodos. A desvantagem é que usar essa técnica requer

um pouco mais de código.

C#

using Microsoft.Extensions.Logging;

internal partial class Program


{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder =>
builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
LogStartupMessage(logger, "fun");
}

[LoggerMessage(Level = LogLevel.Information, Message = "Hello World!


Logging is {Description}.")]
static partial void LogStartupMessage(ILogger logger, string
description);
}

A prática recomendada para nomes de categoria de logs é usar o nome


totalmente qualificado da classe que está criando a mensagem de log. Isso ajuda a
estabelecer uma relação entre as mensagens de log e o código que as produziu e
oferece um bom nível de controle na filtragem de logs. O CreateLogger aceita um
Type para tornar essa nomenclatura fácil de fazer.

C#

using Microsoft.Extensions.Logging;

internal class Program


{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder =>
builder.AddConsole());
ILogger logger = factory.CreateLogger<Program>();
logger.LogInformation("Hello World! Logging is {Description}.",
"fun");
}
}

Se não estiver usando logs de console como sua única solução de monitoramento
de produção, adicione os provedores de registro em log que você está planejando
usar. Por exemplo, você pode usar OpenTelemetry para enviar logs por meio do
Protocolo OpenTelemetry (OTLP) :

C#

using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;

using ILoggerFactory factory = LoggerFactory.Create(builder =>


{
builder.AddOpenTelemetry(logging =>
{
logging.AddOtlpExporter();
});
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");

Integração com hosts e injeção de dependência


Se o seu aplicativo estiver usando Injeção de Dependência (DI) ou um host como o
WebApplication ou um Host Genérico do ASP.NET, você deve usar objetos
ILoggerFactory e ILogger do contêiner de DI em vez de criá-los diretamente.

Obter um ILogger do DI
Esse exemplo obtém um objeto ILogger em um aplicativo hospedado usando APIs
Mínimas do ASP.NET:

C#

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSingleton<ExampleHandler>();

var app = builder.Build();

var handler = app.Services.GetRequiredService<ExampleHandler>();


app.MapGet("/", handler.HandleRequest);

app.Run();

partial class ExampleHandler(ILogger<ExampleHandler> logger)


{
public string HandleRequest()
{
LogHandleRequest(logger);
return "Hello World";
}

[LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was


called")]
public static partial void LogHandleRequest(ILogger logger);
}

No exemplo anterior:

Criou um serviço singleton chamado ExampleHandler e mapeou as solicitações da


web recebidas para executar a função ExampleHandler.HandleRequest .
A linha 8 define um construtor primário para o ExampleHandler, um recurso
adicionado no C# 12. Usar o construtor C# de estilo mais antigo funcionaria
igualmente bem, mas é um pouco mais detalhado.
O construtor define um parâmetro do tipo ILogger<ExampleHandler> .
ILogger<TCategoryName> deriva de ILogger e indica qual categoria o objeto
ILogger tem. O contêiner de DI localiza um ILogger com a categoria correta e o

fornece como o argumento do construtor. Se nenhum ILogger com essa categoria


já existir, o contêiner de DI o criará automaticamente a partir do ILoggerFactory
no provedor de serviços.
O parâmetro logger recebido no construtor foi usado para registrar em log na
função HandleRequest .

ILoggerFactory fornecido pelo host


Os construtores de host inicializam a configuração padrão e, em seguida, adicionam um
objeto ILoggerFactory configurado ao contêiner de DI do host quando o host é criado.
Antes de o host ser criado, você pode ajustar a configuração de registro em log por
meio de HostApplicationBuilder.Logging, WebApplicationBuilder.Logging ou APIs
semelhantes em outros hosts. Os hosts também aplicam a configuração do registro em
log das fontes de configuração padrão, como appsettings.json, e variáveis de ambiente.
Para obter mais informações, confira Configuração no .NET.

Esse exemplo se expande a partir do anterior para personalizar o ILoggerFactory


fornecido pelo WebApplicationBuilder . Adiciona o OpenTelemetry como um provedor
de registro em log que transmite os logs por meio do Protocolo OpenTelemetry
(OTLP) :

C#

var builder = WebApplication.CreateBuilder(args);


builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();

Criar um ILoggerFactory com DI


Se estiver usando um contêiner de DI sem um host, use AddLogging para configurar e
adicionar um ILoggerFactory ao contêiner.

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;

// Add services to the container including logging


var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();

// Get the ExampleService object from the container


ExampleService service = serviceProvider.GetRequiredService<ExampleService>
();

// Do some pretend work


service.DoSomeWork(10, 20);

class ExampleService(ILogger<ExampleService> logger)


{
public void DoSomeWork(int x, int y)
{
logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
}
}

No exemplo anterior:

Criou um contêiner do serviço DI que contém um ILoggerFactory configurado


para gravar no console
Adicionou um singleton ExampleService ao contêiner
Criou uma instância do ExampleService do contêiner de DI que também criou
automaticamente um ILogger<ExampleService> a ser usado como o argumento do
construtor.
Invocou o ExampleService.DoSomeWork que usou o ILogger<ExampleService> para
registrar uma mensagem no console.
Configurar o registro em log
A configuração do registro em log é definida no código ou por meio de fontes externas,
como, por exemplo, arquivos de configuração e variáveis de ambiente. O uso da
configuração externa é benéfico sempre que possível porque pode ser alterado sem ser
preciso recompilar o aplicativo. No entanto, algumas tarefas, como definir provedores
de registro em log, só podem ser configuradas a partir do código.

Configurar o registro em log sem código


No caso de arquivos que usam um host, a configuração do registro em log geralmente
é fornecida pela seção "Logging" dos arquivos appsettings .{Environment} .json. Para
aplicativos que não usam um host, as fontes de configuração externas são configuradas
explicitamente ou, alternativamente, configuradas no código.

O seguinte arquivo appsettings.Development.json é gerado pelos modelos do serviço


.NET Worker:

JSON

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

No JSON anterior:

As categorias de nível de log "Default" , "Microsoft" e


"Microsoft.Hosting.Lifetime" são especificadas.

O valor "Default" é aplicado a todas as categorias que não são especificadas de


outra forma, efetivamente tornando todos os valores padrão para todas as
categorias "Information" . Você pode substituir esse comportamento
especificando um valor para uma categoria.
A categoria "Microsoft" se aplica a todas as categorias que começam com
"Microsoft" .

A categoria "Microsoft" registra em log no nível de log Warning e superior.


A categoria "Microsoft.Hosting.Lifetime" é mais específica do que a "Microsoft" ,
ou seja, a categoria "Microsoft.Hosting.Lifetime" faz o registro em log no nível
"Information" e superiores.

Não é especificado um provedor de log específico, portanto LogLevel se aplica a


todos os provedores de log habilitados, exceto por EventLog do Windows.

A propriedade Logging pode ter LogLevel e as propriedades do provedor de logs. A


LogLevel especifica o nível mínimo de log nas categorias selecionadas. No JSON

anterior, os níveis de log Information e Warning são especificados. LogLevel indica a


severidade do log e varia de 0 a 6:

Trace = 0, Debug = 1, Information = 2, Warning = 3, Error = 4, Critical = 5 e None =

6.

Quando um LogLevel é especificado, o registro em log é habilitado para mensagens no


nível especificado e superior. No JSON anterior, a categoria Default é registrada para
Information e superior. Por exemplo, mensagens Information , Warning , Error e

Critical são registradas em log. Se LogLevel não for especificado, o registro em log

usará o nível Information como padrão. Para obter mais informações, confira Níveis de
log.

Uma propriedade de provedor pode especificar uma propriedade LogLevel . LogLevel


em um provedor especifica os níveis de log desse provedor e substitui as configurações
de log que não são do provedor. Considere o seguinte arquivo appsettings.json:

JSON

{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
As configurações em Logging.{ProviderName}.LogLevel substituem as configurações em
Logging.LogLevel . No JSON anterior, o nível de log padrão do provedor Debug é

definido como Information :

Logging:Debug:LogLevel:Default:Information

A configuração anterior especifica o nível de log Information para todas as categorias


de Logging:Debug: , exceto a Microsoft.Hosting . Quando uma categoria específica é
listada, ela substitui a categoria padrão. No JSON anterior, as categorias
"Microsoft.Hosting" e "Default" de Logging:Debug:LogLevel substituem as

configurações em Logging:LogLevel

O nível mínimo de log pode ser especificado para:

Provedores específicos: por exemplo,


Logging:EventSource:LogLevel:Default:Information

Categorias específicas: por exemplo, Logging:LogLevel:Microsoft:Warning


Todos os provedores e todas as categorias: Logging:LogLevel:Default:Warning

Todos os logs abaixo do nível mínimo não são:

Passados para o provedor.


Registrados nem exibidos.

Para suprimir todos os logs, especifique LogLevel.None. LogLevel.None tem o valor 6,


que é superior a LogLevel.Critical (5).

Se um provedor oferecer suporte a escopos de log, IncludeScopes indicará se eles estão


habilitados. Para obter mais informações, confira Escopos de log

O seguinte arquivo appsettings.json contém as configurações de todos os provedores


internos:

JSON

{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.Extensions.Hosting": "Warning",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}

Na amostra anterior:

As categorias e os níveis não são valores sugeridos. O exemplo é fornecido para


mostrar todos os provedores padrão.
As configurações em Logging.{ProviderName}.LogLevel substituem as
configurações em Logging.LogLevel . Por exemplo, o nível em
Debug.LogLevel.Default substitui o nível em LogLevel.Default .

O alias de cada provedor é usado. Cada provedor define um alias que pode ser
usado na configuração no lugar do nome de tipo totalmente qualificado. Os
aliases dos provedores internos são:
Console

Debug
EventSource
EventLog

AzureAppServicesFile
AzureAppServicesBlob

ApplicationInsights

Definir o nível de log por linha de comando, variáveis de


ambiente e outras configurações
O nível de log pode ser definido por qualquer um dos provedores de configuração. Por
exemplo, você pode criar uma variável de ambiente persistente chamada
Logging:LogLevel:Microsoft com o valor Information .

Linha de comando

Crie e atribua variáveis de ambiente persistentes, considerando o valor de nível de


log.

CMD

:: Assigns the env var to the value


setx "Logging__LogLevel__Microsoft" "Information" /M

Em uma nova instância do prompt de comando, leia a variável de ambiente.

CMD

:: Prints the env var value


echo %Logging__LogLevel__Microsoft%

A configuração de ambiente anterior é mantida no ambiente. Para testar as


configurações ao usar um aplicativo criado com os modelos do serviço .NET Worker, use
o comando dotnet run no diretório do projeto depois que a variável de ambiente for
atribuída.

CLI do .NET

dotnet run

 Dica
Depois de definir uma variável de ambiente, reinicie o IDE (ambiente de
desenvolvimento integrado) para garantir que as variáveis de ambiente adicionadas
recentemente estejam disponíveis.

Em Serviço de Aplicativo do Azure , selecione Nova configuração de aplicativo na


página Configuração > Configurações. As configurações do aplicativo do Serviço de
Aplicativo do Azure são:

Criptografadas em repouso e transmitidas por um canal criptografado.


Expostas como variáveis de ambiente.

Para obter mais informações de como definir valores de configuração do .NET usando
variáveis de ambiente, confira variáveis de ambiente.

Configurar o registro em log com código


Para configurar o registro em log no código, use a API ILoggingBuilder. O recurso pode
ser acessado a partir de diferentes locais:

Ao criar o ILoggerFactory diretamente, configure-o em LoggerFactory.Create.


Ao usar a DI sem um host, configure-a em
LoggingServiceCollectionExtensions.AddLogging.
Ao usar um host, configure-o com HostApplicationBuilder.Logging,
WebApplicationBuilder.Logging ou outras APIs específicas para cada host.

Esse exemplo mostra como configurar o provedor de registro em log do console e


diversos filtros.

C#

using Microsoft.Extensions.Logging;

using var loggerFactory = LoggerFactory.Create(static builder =>


{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole();
});

ILogger logger = loggerFactory.CreateLogger<Program>();


logger.LogDebug("Hello {Target}", "Everyone");
No exemplo anterior, AddFilter é usado para ajustar o nível de log que é habilitado para
várias categorias. O AddConsole é usado para adicionar o provedor de registro em log
do console. Por padrão, os logs com nível de gravidade Debug não são habilitados, mas
como a configuração ajustou os filtros, a mensagem de depuração "Olá, todo mundo" é
exibida no console.

Como as regras de filtragem são aplicadas


Quando um objeto ILogger<TCategoryName> é criado, o objeto ILoggerFactory
seleciona uma única regra por provedor para aplicar a esse agente. Todas as mensagens
gravadas pela instância ILogger são filtradas com base nas regras selecionadas. A regra
mais específica possível para cada par de categoria e provedor é selecionada entre as
regras disponíveis.

O algoritmo a seguir é usado para cada provedor quando um ILogger é criado para
uma determinada categoria:

Selecione todas as regras que correspondem ao provedor ou seu alias. Se


nenhuma correspondência for encontrada, selecione todas as regras com um
provedor vazio.
Do resultado da etapa anterior, selecione as regras com o prefixo de categoria de
maior correspondência. Se nenhuma correspondência for encontrada, selecione
todas as regras que não especificam uma categoria.
Se várias regras forem selecionadas, use a última.
Se não for selecionada nenhuma regra, use
LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) para
especificar o nível mínimo de registros em log.

Categoria do log
Quando um objeto ILogger é criado, uma categoria é especificada. Essa categoria é
incluída em cada mensagem de log criada por essa instância de ILogger . A cadeia de
caracteres da categoria é arbitrária, mas a convenção é usar o nome de classe
totalmente qualificado. Por exemplo, em um aplicativo com um serviço definido como o
seguinte objeto, a categoria pode ser "Example.DefaultService" :

C#

namespace Example
{
public class DefaultService : IService
{
private readonly ILogger<DefaultService> _logger;

public DefaultService(ILogger<DefaultService> logger) =>


_logger = logger;

// ...
}
}

Se mais categorização for desejada, a convenção é usar um nome hierárquico anexando


uma subcategoria ao nome de classe totalmente qualificado e especificar explicitamente
a categoria usando LoggerFactory.CreateLogger:

C#

namespace Example
{
public class DefaultService : IService
{
private readonly ILogger _logger;

public DefaultService(ILoggerFactory loggerFactory) =>


_logger =
loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");

// ...
}
}

A chamada de CreateLogger com um nome fixo pode ser útil quando usada em várias
classes/tipos para que os eventos possam ser organizados por categoria.

ILogger<T> é equivalente a chamar CreateLogger com o nome de tipo totalmente

qualificado de T .

Nível de log
A seguinte tabela lista os valores de LogLevel, o método de extensão de conveniência
Log{LogLevel} e o uso sugerido:

ノ Expandir a tabela

LogLevel Valor Método Descrição

Trace 0 LogTrace Contêm as mensagens mais detalhadas. Essas


mensagens podem conter dados confidenciais do
aplicativo. Essas mensagens são desabilitadas por
LogLevel Valor Método Descrição

padrão e não devem ser habilitadas em um ambiente de


produção.

Depurar 1 LogDebug Para depuração e desenvolvimento. Use com cuidado


em produção devido ao alto volume.

Informações 2 LogInformation Rastreia o fluxo geral do aplicativo. Pode ter um valor de


longo prazo.

Aviso 3 LogWarning Para eventos anormais ou inesperados. Geralmente,


inclui erros ou condições que não fazem com que o
aplicativo falhe.

Erro 4 LogError Para erros e exceções que não podem ser manipulados.
Essas mensagens indicam uma falha na operação ou na
solicitação atual, não uma falha em todo o aplicativo.

Crítico 5 LogCritical Para falhas que exigem atenção imediata. Exemplos:


cenários de perda de dados, espaço em disco
insuficiente.

Nenhum 6 Especifica que nenhuma mensagem deve ser gravada.

Na tabela anterior, o LogLevel é listado da severidade menor para a maior.

O primeiro parâmetro do método Log, LogLevel, indica a severidade do log. Em vez de


chamar Log(LogLevel, ...) , a maioria dos desenvolvedores chama os métodos de
extensão Log{LogLevel}. Os métodos de extensão Log{LogLevel} chamam o método Log
e especificam o LogLevel . Por exemplo, estas duas chamadas de log são equivalentes
em funcionalidades e produzem o mesmo log:

C#

public void LogDetails()


{
var logMessage = "Details for log.";

_logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);


_logger.LogInformation(AppLogEvents.Details, logMessage);
}

AppLogEvents.Details é a ID do evento e é representada implicitamente por um valor

Int32 constante. AppLogEvents é uma classe que expõe várias constantes de


identificador nomeadas e é exibida na seção ID do evento de log.

O código a seguir cria os logs Information e Warning :


C#

public async Task<T> GetAsync<T>(string id)


{
_logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);

var result = await _repository.GetAsync(id);


if (result is null)
{
_logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not
found", id);
}

return result;
}

No código anterior, o primeiro parâmetro de Log{LogLevel} , AppLogEvents.Read , é a ID


do evento de log. O segundo parâmetro é um modelo de mensagem com espaços
reservados para valores de argumento fornecidos pelos parâmetros de método
restantes. Os parâmetros de método serão explicados na seção modelos de mensagem
mais adiante neste artigo.

Configure o nível de log apropriado e chame os métodos Log{LogLevel} corretos para


controlar a quantidade de saída de log gravada em um determinado meio de
armazenamento. Por exemplo:

Em produção:
O log nos níveis Trace ou Debug produz um alto volume de mensagens de log
detalhadas. Para controlar os custos e não exceder os limites de
armazenamento de dados, registre mensagens nos níveis Trace e Debug em um
armazenamento de dados de alto volume e baixo custo. Considere limitar
Trace e Debug a categorias específicas.

O registro em log nos níveis Warning a Critical deve produzir poucas


mensagens de log.
Os custos e os limites de armazenamento geralmente não são preocupantes.
Poucos logs permitem mais flexibilidade nas opções de armazenamento de
dados.
Em desenvolvimento:
Defina como Warning .
Adicione mensagens de Trace ou Debug ao solucionar problemas. Para limitar a
saída, defina Trace ou Debug somente para as categorias em investigação.

O seguinte JSON define Logging:Console:LogLevel:Microsoft:Information :


JSON

{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}

ID de evento de log
Cada log pode especificar um identificador de evento. EventId é uma estrutura com a
propriedade Id e a propriedade somente leitura opcional Name . O código-fonte de
exemplo usa a classe AppLogEvents para definir IDs de evento:

C#

using Microsoft.Extensions.Logging;

internal static class AppLogEvents


{
internal static EventId Create = new(1000, "Created");
internal static EventId Read = new(1001, "Read");
internal static EventId Update = new(1002, "Updated");
internal static EventId Delete = new(1003, "Deleted");

// These are also valid EventId instances, as there's


// an implicit conversion from int to an EventId
internal const int Details = 3000;
internal const int Error = 3001;

internal static EventId ReadNotFound = 4000;


internal static EventId UpdateNotFound = 4001;

// ...
}

 Dica

Para obter mais informações de como converter um int em um EventId , confira


Operador EventId.Implicit(Int32 to EventId).
Uma ID de evento associa um conjunto de eventos. Por exemplo, todos os logs
relacionados à leitura de valores de um repositório podem ser 1001 .

O provedor de logs pode armazenar ou não a ID do evento em um campo de ID na


mensagem de log. O provedor de Depuração não mostra IDs de eventos. O provedor de
console mostra IDs de evento entre colchetes após a categoria:

Console

info: Example.DefaultService.GetAsync[1001]
Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
GetAsync(a1b2c3) not found

Alguns provedores de log armazenam a ID do evento em um campo, o que permite a


filtragem na ID.

Modelo de mensagem de log


Cada API de log usa um modelo de mensagem. O modelo de mensagem pode conter
espaços reservados para os quais são fornecidos argumentos. Use nomes para os
espaços reservados, não números. A ordem dos espaços reservados e não de seus
nomes, determina quais parâmetros serão usados para fornecer seus valores. No código
a seguir, os nomes de parâmetro estão fora de sequência no modelo de mensagem:

C#

string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);

Esse código cria uma mensagem de log com os valores de parâmetro em sequência:

text

Parameter values: param1, param2

7 Observação

Esteja atento ao usar vários espaços reservados em um só modelo de mensagem,


pois eles são baseados em ordinais. Os nomes não são usados para alinhar os
argumentos aos espaços reservados.
Essa abordagem permite que os provedores de log implementem o log semântico ou
estruturado . Os próprios argumentos são passados para o sistema de registro em log,
não apenas o modelo de mensagem formatado. Essas informações permitem que os
provedores de log armazenem os valores de parâmetro como campos. Considere o
seguinte método de agente:

C#

_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);

Por exemplo, ao registrar em log no Armazenamento de Tabelas do Azure:

Cada entidade da Tabela do Azure pode ter as propriedades ID e RunTime .


As tabelas com propriedades simplificam as consultas nos dados registrados. Por
exemplo, uma consulta pode encontrar todos os logs em um determinado
intervalo de RunTime sem precisar analisar o tempo limite da mensagem de texto.

Formatação do modelo de mensagem de log


Os modelos de mensagem de log dão suporte à formatação de espaço reservado. Os
modelos são livres para especificar qualquer formato válido para o argumento de tipo
fornecido. Por exemplo, considere o seguinte modelo de mensagem Information do
agente:

C#

_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}",


DateTimeOffset.UtcNow);
// Logged on January 06, 2022

No exemplo anterior, a instância de DateTimeOffset é o tipo que corresponde a


PlaceHolderName no modelo de mensagem do agente. Esse nome pode ser qualquer

coisa, pois os valores são baseados em ordinais. O formato MMMM dd, yyyy é válido para
o tipo DateTimeOffset .

Para obter mais informações sobre a formatação de DateTime e DateTimeOffset , confira


Cadeias de caracteres de formato de data e hora personalizadas.

Exemplos
Os exemplos a seguir mostram como formatar um modelo de mensagem usando a
sintaxe de espaço reservado {} . Além disso, um exemplo de escape da sintaxe de
espaço reservado {} é mostrado com a saída. Por fim, a interpolação de cadeia de
caracteres com espaços reservados de modelo também é mostrada:

C#

logger.LogInformation("Number: {Number}", 1); // Number: 1


logger.LogInformation("{{Number}}: {Number}", 3); // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5); // {Number}: 5

 Dica

Na maioria dos casos, você deve usar a formatação de modelo de mensagens


de log ao registrar em log. O uso da interpolação de cadeias de caracteres
pode causar problemas de desempenho.
A regra de análise de código CA2254: o modelo deve ser uma expressão
estática ajuda a alertar você para locais em que suas mensagens de log não
usam a formatação adequada.

Registrar exceções em log


Os métodos do agente têm sobrecargas que usam um parâmetro de exceção:

C#

public void Test(string id)


{
try
{
if (id is "none")
{
throw new Exception("Default Id detected.");
}
}
catch (Exception ex)
{
_logger.LogWarning(
AppLogEvents.Error, ex,
"Failed to process iteration: {Id}", id);
}
}

O log de exceções é específico do provedor.


Nível de log padrão
Se o nível de log padrão não for definido, o valor do nível de log padrão será
Information .

Por exemplo, considere o seguinte aplicativo de serviço de trabalho:

Criado com os modelos do .NET Worker.


appsettings.json e appsettings.Development.json excluídos ou renomeados.

Com a configuração anterior, o acesso à página de privacidade ou à home page gera


várias mensagens Trace , Debug e Information com Microsoft no nome da categoria.

O seguinte código define o nível de log padrão quando ele não está definido na
configuração:

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.SetMinimumLevel(LogLevel.Warning);

using IHost host = builder.Build();

await host.RunAsync();

Função Filter
Uma função de filtro é invocada para todos os provedores e as categorias que não têm
regras atribuídas por configuração ou no código:

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddFilter((provider, category, logLevel) =>


{
return provider.Contains("ConsoleLoggerProvider")
&& (category.Contains("Example") || category.Contains("Microsoft"))
&& logLevel >= LogLevel.Information;
});

using IHost host = builder.Build();

await host.RunAsync();
O código anterior exibe logs de console quando a categoria contém Example ou
Microsoft e o nível de log é Information ou superior.

Escopos de log
Um escopo agrupa um conjunto de operações lógicas. Esse agrupamento pode ser
usado para anexar os mesmos dados para cada log criado como parte de um conjunto.
Por exemplo, todo log criado como parte do processamento de uma transação pode
incluir a ID da transação.

Um escopo:

É um tipo IDisposable retornado pelo método BeginScope.


Dura até que seja descartado.

Os seguintes provedores dão suporte a escopos:

Console

AzureAppServicesFile e AzureAppServicesBlob

Use um escopo por meio do encapsulamento de chamadas de agente em um bloco


using :

C#

public async Task<T> GetAsync<T>(string id)


{
T result;
var transactionId = Guid.NewGuid().ToString();

using (_logger.BeginScope(new List<KeyValuePair<string, object>>


{
new KeyValuePair<string, object>("TransactionId",
transactionId),
}))
{
_logger.LogInformation(
AppLogEvents.Read, "Reading value for {Id}", id);

var result = await _repository.GetAsync(id);


if (result is null)
{
_logger.LogWarning(
AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
}
return result;
}

O seguinte JSON habilita escopos para o provedor de console:

JSON

{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}

O código a seguir habilita os escopos para o provedor de console:

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole(options => options.IncludeScopes = true);

using IHost host = builder.Build();

await host.RunAsync();

Criar logs em Main


O seguinte código registra logs em Main obtendo uma instância de ILogger da DI após
a criação do host:

C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using IHost host = Host.CreateApplicationBuilder(args).Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();


logger.LogInformation("Host created.");

await host.RunAsync();

O código anterior depende de dois pacotes NuGet:

Microsoft.Extensions.Hosting
Microsoft.Extensions.Logging

O arquivo de projeto seria semelhante ao seguinte:

XML

<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1"
/>
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0"
/>
</ItemGroup>

</Project>

Sem métodos de agente assíncronos


O registro em log deve ser tão rápido que não justifique o custo de desempenho de
código assíncrono. Se o armazenamento de dados de log estiver lento, não grave as
mensagens diretamente nele. Grave as mensagens de log em um armazenamento
rápido primeiro e depois passe-as para um armazenamento lento. Por exemplo, se você
estiver enviado logs ao SQL Server, não faça isso diretamente em um método Log , pois
os métodos Log são síncronos. Em vez disso, adicione mensagens de log de forma
síncrona a uma fila na memória e faça com que uma função de trabalho de plano de
fundo efetue pull das mensagens para fora da fila para fazer o trabalho assíncrono de
envio de dados por push para o SQL Server.

Alterar os níveis de log em um aplicativo em


execução
A API de Log não inclui um cenário para alterar os níveis de log enquanto um aplicativo
está em execução. No entanto, alguns provedores de configuração conseguem
recarregar a configuração, o que entra em vigor imediatamente na configuração de log.
Por exemplo, o Provedor de Configuração de Arquivos recarrega a configuração de log
por padrão. Se a configuração for alterada no código enquanto um aplicativo estiver em
execução, o aplicativo poderá chamar IConfigurationRoot.Reload para atualizar a
configuração de log do aplicativo.

Pacotes NuGet
As interfaces e implementações ILogger<TCategoryName> e ILoggerFactory estão
inclusas na maioria dos SDKs do .NET como referência implícita de pacote. Elas também
estão disponíveis explicitamente nos seguintes pacotes NuGet quando não são
referenciadas implicitamente:

As interfaces estão em Microsoft.Extensions.Logging.Abstractions .


As implementações padrão estão em Microsoft.Extensions.Logging .

Para obter mais informações sobre qual SDK do .NET inclui referências implícitas de
pacote, confira SDK do .NET: tabela para namespace implícito.

Confira também
Provedores de log no .NET
Implementar um provedor de log personalizado no .NET
Formatação de log de console
Log de alto desempenho no .NET
Diretrizes do registro em log para autores de bibliotecas .NET
Os bugs de log devem ser criados no repositório github.com/dotnet/runtime

6 Colaborar conosco no Comentários do .NET


GitHub
A fonte deste conteúdo pode O .NET é um projeto código aberto.
ser encontrada no GitHub, onde Selecione um link para fornecer
você também pode criar e comentários:
revisar problemas e solicitações
de pull. Para obter mais  Abrir um problema de
informações, confira o nosso documentação
guia para colaboradores.
 Fornecer comentários sobre o
produto
Provedores de log no .NET
Artigo • 23/06/2023

Os provedores de registro em log persistem logs, exceto o provedor Console , que


apenas exibe logs como saída padrão. Por exemplo, o provedor do Azure Application
Insights armazena logs no Azure Application Insights. Vários provedores podem ser
habilitados.

Os modelos de aplicativo padrão do Worker do .NET:

Usam o Host Genérico.


Chamam CreateApplicationBuilder, que adiciona os seguintes provedores de
registro em log:
Console
Depurar
EventSource
EventLog (somente Windows)

C#

using Microsoft.Extensions.Hosting;

using IHost host = Host.CreateApplicationBuilder(args).Build();

// Application code should start here.

await host.RunAsync();

O código anterior mostra a classe Program criada com os modelos de aplicativo do


Worker do .NET. As próximas seções fornecem exemplos baseados nos modelos de
aplicativo do Worker do .NET, que usam o Host Genérico.

Para substituir o conjunto padrão de provedores de registro em log adicionados por


Host.CreateApplicationBuilder , chame ClearProviders e adicione os provedores de

registro em log desejados. Por exemplo, o código a seguir:

Chama ClearProviders para remover todas as instâncias de ILoggerProvider do


construtor.
Adiciona o provedor de registro em log do Console.

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);


builder.Logging.ClearProviders();
builder.Logging.AddConsole();

Para provedores adicionais, consulte:

Provedores de registro em log internos.


Provedores de log de terceiros.

Configurar um serviço que depende do ILogger


Para configurar um serviço que depende de ILogger<T> , use a injeção de construtor ou
forneça um método de fábrica. A abordagem do método de fábrica é recomendada
somente se não há nenhuma outra opção. Por exemplo, considere um serviço que
precisa de uma instância de ILogger<T> fornecida pelo DI:

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddSingleton<IExampleService>(
container => new DefaultExampleService
{
Logger = container.GetRequiredService<ILogger<IExampleService>>()
});

O código anterior é um Func<IServiceProvider, IExampleService> que é executado na


primeira vez que o contêiner de DI precisa construir uma instância de IExampleService .
Você pode acessar qualquer um dos serviços registrados dessa maneira.

Provedores de log internos


As Extensões da Microsoft incluem os seguintes provedores de registro em log como
parte das bibliotecas de runtime:

Console
Depurar
EventSource
EventLog

Os provedores de registro em log a seguir são fornecidos pela Microsoft, mas não como
parte das bibliotecas de runtime. Eles devem ser instalados como pacotes NuGet
adicionais.
AzureAppServicesFile e AzureAppServicesBlob
ApplicationInsights

Console
O provedor Console registra a saída no console.

Depurar
O provedor Debug grava a saída de log usando a classe System.Diagnostics.Debug,
especificamente por meio do método Debug.WriteLine e somente quando o depurador
está anexado. DebugLoggerProvider cria DebugLogger instâncias, que são
implementações da interface ILogger .

Origem do Evento
O provedor EventSource grava em uma origem do evento multiplataforma com o nome
Microsoft-Extensions-Logging . No Windows, o provedor usa ETW.

Ferramentas de rastreamento dotnet


A ferramenta dotnet-trace é uma ferramenta global multiplataforma da CLI que permite
a coleta pelo .NET Core de rastreamentos de um processo em execução. A ferramenta
coleta dados do provedor Microsoft.Extensions.Logging.EventSource usando um
LoggingEventSource.

Consulte dotnet-trace para obter instruções de instalação. Para obter um tutorial de


diagnóstico usando dotnet-trace , confira Depurar o alto uso da CPU no .NET Core.

EventLog do Windows
O provedor EventLog envia a saída de log para o Log de Eventos do Windows. Diferente
dos outros provedores, o provedor EventLog não herda as configurações de não
provedor padrão. Se as configurações de log EventLog não forem especificadas, elas
serão LogLevel.Warning por padrão.

Para registrar eventos inferiores a LogLevel.Warning, defina explicitamente o nível de


log. O seguinte exemplo define o nível de log padrão do Log de Eventos como
LogLevel.Information:
JSON

"Logging": {
"EventLog": {
"LogLevel": {
"Default": "Information"
}
}
}

Sobrecargas de AddEventLog podem passar em EventLogSettings. Se for null ou não


for especificado, as seguintes configurações padrão serão usadas:

LogName : "Aplicativo"
SourceName : "Runtime do .NET"

MachineName : o nome do computador local é usado.

O seguinte código altera o SourceName do valor padrão de ".NET Runtime" para


CustomLogs :

C#

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddEventLog(
config => config.SourceName = "CustomLogs");

using IHost host = builder.Build();

host.Run();

Serviço de aplicativo do Azure


O pacote de provedor Microsoft.Extensions.Logging.AzureAppServices grava logs em
arquivos de texto no sistema de arquivos de um aplicativo do Serviço de Aplicativo do
Azure e no armazenamento de blobs em uma conta de Armazenamento do Azure.

O pacote do provedor não está incluído nas bibliotecas de runtime. Para usar o
provedor, adicione o pacote do provedor ao projeto.

Para definir as configurações do provedor, use AzureFileLoggerOptions e


AzureBlobLoggerOptions, conforme mostrado no exemplo a seguir:

C#
using Microsoft.Extensions.Logging.AzureAppServices;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args)

builder.Logging.AddAzureWebAppDiagnostics();
builder.Services.Configure<AzureFileLoggerOptions>(options =>
{
options.FileName = "azure-diagnostics-";
options.FileSizeLimit = 50 * 1024;
options.RetainedFileCountLimit = 5;
});
builder.ServicesConfigure<AzureBlobLoggerOptions>(options =>
{
options.BlobName = "log.txt";
});

using IHost host = builder.Build();

// Application code should start here.

await host.RunAsync();

Quando implantado no Serviço de Aplicativo do Azure, o aplicativo usa as configurações


na seção Logs do Serviço de Aplicativo da página Serviço de Aplicativo do portal do
Azure. Quando as configurações a seguir são atualizadas, as alterações entram em vigor
imediatamente sem a necessidade de uma reinicialização ou reimplantação do
aplicativo.

O local padrão para arquivos de log é na pasta D:\home\LogFiles\Application. Os


padrões adicionais variam de acordo com o provedor:

Registro em log de aplicativos (sistema de arquivos): o nome do arquivo padrão


do sistema de arquivos é diagnostics-yyyymmdd.txt. O limite padrão de tamanho
do arquivo é 10 MB e o número padrão máximo de arquivos mantidos é 2.
Registro em log do aplicativo (Blob): o nome de blob padrão é {app-
name}/yyyy/mm/dd/hh/{guid}_applicationLog.txt.

O provedor registra somente quando o projeto é executado no ambiente do Azure.

Fluxo de log do Azure

O streaming de log do Azure dá suporte à exibição de atividades de log em tempo


usando:

O servidor de aplicativos
Do servidor Web
De uma solicitação de rastreio com falha

Para configurar o fluxo de log do Azure:

Navegue até a página Logs do Serviço de Aplicativo no portal do aplicativo.


Defina Habilitar o log de aplicativo (sistema de arquivos) como Ativada.
Escolha o Nível de log. Essa configuração se aplica somente ao streaming de log
do Azure.

Navegue até a página Fluxo de Log para exibir os logs. As mensagens são registradas
com a interface ILogger .

Azure Application Insights


O pacote de provedor Microsoft.Extensions.Logging.ApplicationInsights grava os logs
no Azure Application Insights. O Application Insights é um serviço que monitora um
aplicativo web e fornece ferramentas para consultar e analisar os dados de telemetria.
Se você usar esse provedor, poderá consultar e analisar os logs usando as ferramentas
do Application Insights.

Para saber mais, consulte os recursos a seguir:

Visão geral do Application Insights


ApplicationInsightsLoggerProvider para logs do .NET Core ILogger – Comece aqui
se você quiser implementar o provedor de log sem o restante da telemetria do
Application Insights.
Application Insights logging adapters (Adaptadores de registro em log do
Application Insights).
Instalar, configurar e inicializar o SDK do Application Insights – Tutorial interativo
no site da Microsoft Learn.

Considerações de design do provedor de


registro em log
Se você planeja desenvolver sua própria implementação da interface ILoggerProvider e
da implementação personalizada correspondente de ILogger, considere os seguintes
pontos:

O método ILogger.Log é síncrono.


O tempo de vida do estado do log e dos objetos não deve ser assumido.
Uma implementação de ILoggerProvider criará um ILogger por meio do método
ILoggerProvider.CreateLogger dele. Se a sua implementação se esforça para enfileirar
mensagens de registro em log de maneira não bloqueada, as mensagens devem
primeiro ser materializadas ou o estado do objeto usado para materializar uma entrada
de log deve ser serializado. Isso evita possíveis exceções de objetos descartados.

Para obter mais informações, confira Implementar um provedor de registro em log


personalizado no .NET.

Provedores de log de terceiros


Aqui estão algumas estruturas de registro em log de terceiros que funcionam com
várias cargas de trabalho do .NET:

elmah.io (repositório GitHub )


Gelf (repositório do GitHub )
JSNLog (repositório GitHub )
KissLog.net (Repositório do GitHub )
Log4Net (repositório do GitHub )
NLog (repositório GitHub )
NReco.Logging (GitHub repo )
Sentry (repositório GitHub )
Serilog (repositório GitHub )
Stackdriver (repositório do GitHub )

Algumas estruturas de terceiros podem fazer o log semântico, também conhecido como
registro em log estruturado .

Usar uma estrutura de terceiros é semelhante ao uso de um dos provedores internos:

1. Adicione um pacote NuGet ao projeto.


2. Chame um método de extensão ILoggerFactory ou ILoggingBuilder fornecido
pela estrutura de registros.

Para saber mais, consulte a documentação de cada provedor. Não há suporte para
provedores de log de terceiros na Microsoft.

Confira também
Registro em log no .NET.
Implementar um provedor de registro em log personalizado no .NET.
Registro em log de alto desempenho no .NET.
Geração de origem de log em tempo de
compilação
Artigo • 11/10/2023

O .NET 6 apresenta o tipo LoggerMessageAttribute . Esse atributo faz parte do


namespace Microsoft.Extensions.Logging e, quando usado, gera a origem de APIs de
log de alto desempenho. O suporte ao log de geração de origem foi criado para
fornecer uma solução de log altamente utilizável e de alto desempenho para aplicativos
modernos do .NET. O código-fonte gerado automaticamente conta com a interface
ILogger em conjunto com a funcionalidade LoggerMessage.Define.

O gerador de origem é disparado quando LoggerMessageAttribute é usado em métodos


de log partial . Quando disparado, pode gerar automaticamente a implementação dos
métodos partial que está decorando ou produzir diagnósticos em tempo de
compilação com dicas sobre o uso adequado. Normalmente, a solução de log em
tempo de compilação é consideravelmente mais rápida em tempo de execução do que
as abordagens de log existentes. Ela faz isso eliminando a conversão boxing, alocações
temporárias e cópias até o limite.

Uso básico
Para usar o LoggerMessageAttribute , a classe de consumo e o método precisam ser
partial . O gerador de código é disparado em tempo de compilação e gera uma

implementação do método partial .

C#

public static partial class Log


{
[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public static partial void CouldNotOpenSocket(
ILogger logger, string hostName);
}

No exemplo anterior, o método de log é static e o nível de log é especificado na


definição de atributo. Ao usar o atributo em um contexto estático, a instância ILogger é
necessária como um parâmetro ou modifica a definição para usar a palavra-chave this
para definir o método como um método de extensão.

C#

public static partial class Log


{
[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public static partial void CouldNotOpenSocket(
this ILogger logger, string hostName);
}

Você também pode optar por usar o atributo em um contexto não estático. Considere o
exemplo a seguir, em que o método de log é declarado como método de instância.
Nesse contexto, o método de log obtém o agente acessando um campo ILogger na
classe de contenção.

C#

public partial class InstanceLoggingExample


{
private readonly ILogger _logger;

public InstanceLoggingExample(ILogger logger)


{
_logger = logger;
}

[LoggerMessage(
EventId = 0,
Level = LogLevel.Critical,
Message = "Could not open socket to `{HostName}`")]
public partial void CouldNotOpenSocket(string hostName);
}

Às vezes, o nível de log precisa ser dinâmico, em vez de estaticamente integrado ao


código. Você pode fazer isso omitindo o nível de log do atributo e, em vez disso,
exigindo que ele seja um parâmetro para o método de log.

C#

public static partial class Log


{
[LoggerMessage(
EventId = 0,
Message = "Could not open socket to `{HostName}`")]
public static partial void CouldNotOpenSocket(
ILogger logger,
LogLevel level, /* Dynamic log level as parameter, rather than
defined in attribute. */
string hostName);
}

Você pode omitir a mensagem de log e o String.Empty será fornecido para a mensagem.
O estado conterá os argumentos, formatados como pares chave-valor.

C#

using System.Text.Json;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory = LoggerFactory.Create(


builder =>
builder.AddJsonConsole(
options =>
options.JsonWriterOptions = new JsonWriterOptions()
{
Indented = true
}));

ILogger<SampleObject> logger = loggerFactory.CreateLogger<SampleObject>();


logger.PlaceOfResidence(logLevel: LogLevel.Information, name: "Liana", city:
"Seattle");

readonly file record struct SampleObject { }

public static partial class Log


{
[LoggerMessage(EventId = 23, Message = "{Name} lives in {City}.")]
public static partial void PlaceOfResidence(
this ILogger logger,
LogLevel logLevel,
string name,
string city);
}

Considere o exemplo de saída de log ao usar o formatador JsonConsole .

JSON

{
"EventId": 23,
"LogLevel": "Information",
"Category": "\u003CProgram\u003EF...9CB42__SampleObject",
"Message": "Liana lives in Seattle.",
"State": {
"Message": "Liana lives in Seattle.",
"name": "Liana",
"city": "Seattle",
"{OriginalFormat}": "{Name} lives in {City}."
}
}

Restrições do método de log


Ao usar o LoggerMessageAttribute nos métodos de log, algumas restrições devem ser
seguidas:

Os métodos de log devem ser partial e retornar void .


Os nomes do método de log não devem começar com um sublinhado.
Os nomes de parâmetro dos métodos de log não devem começar com um
sublinhado.
Os métodos de log podem não ser definidos em um tipo aninhado.
Os métodos de log não podem ser genéricos
Se um método de log for static , a instância ILogger será exigida como
parâmetro.

O modelo de geração de código depende de o código ser compilado com compilador


moderno do C# versão 9 ou posterior. O compilador do C# 9.0 ficou disponível com o
.NET 5. Para atualizar para um compilador moderno do C#, edite o arquivo de projeto
para ser direcionado ao C# 9.0.

XML

<PropertyGroup>
<LangVersion>9.0</LangVersion>
</PropertyGroup>

Para obter mais informações, confira Controle de versão da linguagem C#.

Anatomia do método de log


A assinatura ILogger.Log aceita o LogLevel e, opcionalmente, um Exception, conforme
mostrado abaixo.

C#

public interface ILogger


{
void Log<TState>(
Microsoft.Extensions.Logging.LogLevel logLevel,
Microsoft.Extensions.Logging.EventId eventId,
TState state,
System.Exception? exception,
Func<TState, System.Exception?, string> formatter);
}

Como regra geral, a primeira instância de ILogger , LogLevel e Exception é tratada de


maneira especial na assinatura do método de log do gerador de origem. As instâncias
subsequentes são tratadas como parâmetros normais para o modelo de mensagem:

C#

// This is a valid attribute usage


[LoggerMessage(
EventId = 110, Level = LogLevel.Debug, Message = "M1 {Ex3} {Ex2}")]
public static partial void ValidLogMethod(
ILogger logger,
Exception ex,
Exception ex2,
Exception ex3);

// This causes a warning


[LoggerMessage(
EventId = 0, Level = LogLevel.Debug, Message = "M1 {Ex} {Ex2}")]
public static partial void WarningLogMethod(
ILogger logger,
Exception ex,
Exception ex2);

) Importante

Os avisos emitidos fornecem detalhes sobre o uso correto do


LoggerMessageAttribute . No exemplo anterior, o WarningLogMethod relatará um

DiagnosticSeverity.Warning de SYSLIB0025 .

Console

Don't include a template for `ex` in the logging message since it is


implicitly taken care of.

Suporte ao nome do modelo que não diferencia


maiúsculas de minúsculas
O gerador faz uma comparação que não diferencia maiúsculas de minúsculas entre itens
no modelo de mensagem e nomes de argumento na mensagem de log. Isso significa
que quando ILogger enumera o estado, o argumento é selecionado pelo modelo de
mensagem, o que pode tornar os logs mais agradáveis de consumir:

C#

public partial class LoggingExample


{
private readonly ILogger _logger;

public LoggingExample(ILogger logger)


{
_logger = logger;
}

[LoggerMessage(
EventId = 10,
Level = LogLevel.Information,
Message = "Welcome to {City} {Province}!")]
public partial void LogMethodSupportsPascalCasingOfNames(
string city, string province);

public void TestLogging()


{
LogMethodSupportsPascalCasingOfNames("Vancouver", "BC");
}
}

Considere o exemplo de saída de log ao usar o formatador JsonConsole :

JSON

{
"EventId": 13,
"LogLevel": "Information",
"Category": "LoggingExample",
"Message": "Welcome to Vancouver BC!",
"State": {
"Message": "Welcome to Vancouver BC!",
"City": "Vancouver",
"Province": "BC",
"{OriginalFormat}": "Welcome to {City} {Province}!"
}
}

Ordem de parâmetro indeterminada


Não há restrições sobre a ordenação de parâmetros do método de log. Um
desenvolvedor pode definir o ILogger como o último parâmetro, embora possa parecer
um pouco estranho.

C#

[LoggerMessage(
EventId = 110,
Level = LogLevel.Debug,
Message = "M1 {Ex3} {Ex2}")]
static partial void LogMethod(
Exception ex,
Exception ex2,
Exception ex3,
ILogger logger);

 Dica

Não é necessário que a ordem dos parâmetros em um método de log corresponda


à ordem dos espaços reservados do modelo. Em vez disso, espera-se que os nomes
de espaço reservado no modelo correspondam aos parâmetros. Considere a saída
de JsonConsole a seguir e a ordem dos erros.

JSON

{
"EventId": 110,
"LogLevel": "Debug",
"Category": "ConsoleApp.Program",
"Message": "M1 System.Exception: Third time's the charm.
System.Exception: This is the second error.",
"State": {
"Message": "M1 System.Exception: Third time's the charm.
System.Exception: This is the second error.",
"ex2": "System.Exception: This is the second error.",
"ex3": "System.Exception: Third time's the charm.",
"{OriginalFormat}": "M1 {Ex3} {Ex2}"
}
}

Outros exemplos de log


Os exemplos a seguir demonstram como recuperar o nome do evento, definir o nível de
log dinamicamente e formatar os parâmetros de log. Os métodos de log são:
LogWithCustomEventName : recupere o nome do evento por meio do atributo
LoggerMessage .

LogWithDynamicLogLevel : defina o nível de log dinamicamente para permitir que

isso seja feito com base na entrada de configuração.


UsingFormatSpecifier : use especificadores de formato para formatar os

parâmetros de log.

C#

public partial class LoggingSample


{
private readonly ILogger _logger;

public LoggingSample(ILogger logger)


{
_logger = logger;
}

[LoggerMessage(
EventId = 20,
Level = LogLevel.Critical,
Message = "Value is {Value:E}")]
public static partial void UsingFormatSpecifier(
ILogger logger, double value);

[LoggerMessage(
EventId = 9,
Level = LogLevel.Trace,
Message = "Fixed message",
EventName = "CustomEventName")]
public partial void LogWithCustomEventName();

[LoggerMessage(
EventId = 10,
Message = "Welcome to {City} {Province}!")]
public partial void LogWithDynamicLogLevel(
string city, LogLevel level, string province);

public void TestLogging()


{
LogWithCustomEventName();

LogWithDynamicLogLevel("Vancouver", LogLevel.Warning, "BC");


LogWithDynamicLogLevel("Vancouver", LogLevel.Information, "BC");

UsingFormatSpecifier(logger, 12345.6789);
}
}

Considere o exemplo de saída de log ao usar o formatador SimpleConsole :


Console

trce: LoggingExample[9]
Fixed message
warn: LoggingExample[10]
Welcome to Vancouver BC!
info: LoggingExample[10]
Welcome to Vancouver BC!
crit: LoggingExample[20]
Value is 1.234568E+004

Considere o exemplo de saída de log ao usar o formatador JsonConsole :

JSON

{
"EventId": 9,
"LogLevel": "Trace",
"Category": "LoggingExample",
"Message": "Fixed message",
"State": {
"Message": "Fixed message",
"{OriginalFormat}": "Fixed message"
}
}
{
"EventId": 10,
"LogLevel": "Warning",
"Category": "LoggingExample",
"Message": "Welcome to Vancouver BC!",
"State": {
"Message": "Welcome to Vancouver BC!",
"city": "Vancouver",
"province": "BC",
"{OriginalFormat}": "Welcome to {City} {Province}!"
}
}
{
"EventId": 10,
"LogLevel": "Information",
"Category": "LoggingExample",
"Message": "Welcome to Vancouver BC!",
"State": {
"Message": "Welcome to Vancouver BC!",
"city": "Vancouver",
"province": "BC",
"{OriginalFormat}": "Welcome to {City} {Province}!"
}
}
{
"EventId": 20,
"LogLevel": "Critical",
"Category": "LoggingExample",
"Message": "Value is 1.234568E+004",
"State": {
"Message": "Value is 1.234568E+004",
"value": 12345.6789,
"{OriginalFormat}": "Value is {Value:E}"
}
}

Resumo
Com o advento dos geradores de origem do C#, gravar APIs de log de alto desempenho
ficou muito mais fácil. O uso da abordagem do gerador de origem tem vários benefícios
importantes:

Permite que a estrutura de log seja preservada e habilita a sintaxe de formato


exato exigida pelos Modelos de Mensagem .
Permite fornecer nomes alternativos para os espaços reservados do modelo e usar
especificadores de formato.
Permite a passagem de todos os dados originais conforme apresentados, sem
complicações em relação à maneira como são armazenados antes do
processamento (além de criar um string ).
Fornece diagnósticos específicos do log, emite avisos para IDs do evento
duplicadas.

Além disso, existem benefícios no uso manual de LoggerMessage.Define:

Sintaxe mais curta e mais simples: uso de atributo declarativo, em vez da


codificação padrão.
Experiência guiada do desenvolvedor: o gerador fornece avisos para ajudar os
desenvolvedores a fazer a coisa certa.
Suporte para um número arbitrário de parâmetros de log. LoggerMessage.Define
permite no máximo seis.
Suporte para nível de log dinâmico. Não é possível com o LoggerMessage.Define
isolado.

Confira também
Registro em log no .NET
Log de alto desempenho no .NET
Formatação de log de console
NuGet: Microsoft.Extensions.Logging.Abstractions
6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Diretrizes do registro em log para
autores de bibliotecas .NET
Artigo • 21/03/2024

Como autor de biblioteca, a exposição do registro em log é uma ótima maneira de


fornecer aos consumidores uma visão do funcionamento interno da sua biblioteca. Estas
diretrizes ajudam você a expor o registro em log de forma consistente com outras
bibliotecas e estruturas .NET. Ela também ajuda você a evitar gargalos comuns de
desempenho que podem não ser óbvios.

Quando utilizar a interface ILoggerFactory


Ao escrever uma biblioteca que emite logs, você precisa de um objeto ILogger para
registrar os logs. Para obter esse objeto, sua API pode aceitar um parâmetro
ILogger<TCategoryName> ou pode aceitar um ILoggerFactory após o qual você chama
ILoggerFactory.CreateLogger. Qual abordagem deve ser a preferida?

Quando precisar de um objeto de registro em log que possa ser transmitido a


várias classes para que todas elas possam emitir logs, utilize ILoggerFactory .
Recomenda-se que cada classe crie registros em logs com uma categoria
separada, com o mesmo nome da classe. Para fazer isso, é necessário que a fábrica
crie objetos ILogger<TCategoryName> exclusivos para cada classe que emite logs.
Exemplos comuns incluem APIs de ponto de entrada público para uma biblioteca
ou construtores públicos de tipos que podem criar classes auxiliares internamente.

Quando precisar de um objeto de registro em log que seja usado somente dentro
de uma classe e nunca compartilhado, utilize ILogger<TCategoryName> , na qual
TCategoryName é o tipo que produz os logs. Um exemplo comum disso é um

construtor para uma classe criada por injeção de dependência.

Se estiver projetando uma API pública que deve permanecer estável ao longo do
tempo, lembre-se de que talvez deseje refatorar sua implementação interna no futuro.
Mesmo que uma classe não crie nenhum tipo auxiliar interno inicialmente, isso pode ser
alterado à medida que o código evolui. Utilizar ILoggerFactory acomoda a criação de
novos objetos ILogger<TCategoryName> para todas as novas classes sem alterar a API
pública.

Para obter mais informações, confira Como as regras de filtragem são aplicadas.
Prefira o registro em log gerado pela origem
A ILogger suporta duas abordagens para o uso da API. Você pode chamar métodos
como LoggerExtensions.LogError e LoggerExtensions.LogInformation, ou pode utilizar o
gerador de origens de registro em log para definir métodos de registro fortemente
tipados. Na maioria das situações, o gerador de origem é recomendado porque oferece
desempenho superior e digitação mais forte. Ele também isola preocupações específicas
de registro em log, como modelos de mensagens, IDs e níveis de log do código de
chamada. A abordagem não gerada pela origem é útil principalmente em cenários nos
quais você está disposto a abrir mão dessas vantagens para tornar o código mais
conciso.

C#

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages


{
[LoggerMessage(
Message = "Sold {Quantity} of {Description}",
Level = LogLevel.Information)]
internal static partial void LogProductSaleDetails(
this ILogger logger,
int quantity,
string description);
}

O código anterior:

Define um partial class chamado LogMessages , que é static , de modo que


possa ser utilizado para definir métodos de extensão no tipo ILogger .
Decora um método de extensão LogProductSaleDetails com o atributo
LoggerMessage e o modelo Message .

Declara LogProductSaleDetails , que estende ILogger e aceita quantity e


description .

 Dica

Você pode entrar no código gerado pela origem durante a depuração, pois ele faz
parte do mesmo assembly que o código que o chama.
Utilize IsEnabled para evitar a avaliação dispendiosa de
parâmetros
Podem existir situações nas quais a avaliação de parâmetros é dispendiosa. Expandindo
o exemplo anterior, imagine que o parâmetro description é um string cuja
computação é dispendiosa. Talvez o produto que está sendo vendido tenha uma
descrição amigável e dependa de uma consulta ao banco de dados ou da leitura de um
arquivo. Nessas situações, você pode instruir o gerador de origens a ignorar a proteção
IsEnabled e adicionar manualmente a proteção IsEnabled no local da chamada. Isso

permite que o usuário determine o local em que a proteção é chamada e garante que
os parâmetros que podem ser dispendiosos para computação sejam avaliados somente
quando realmente necessários. Considere o código a seguir:

C#

using Microsoft.Extensions.Logging;

namespace Logging.LibraryAuthors;

internal static partial class LogMessages


{
[LoggerMessage(
Message = "Sold {Quantity} of {Description}",
Level = LogLevel.Information,
SkipEnabledCheck = true)]
internal static partial void LogProductSaleDetails(
this ILogger logger,
int quantity,
string description);
}

Quando o método de extensão LogProductSaleDetails é chamado, a proteção


IsEnabled é invocada manualmente e a avaliação do parâmetro dispendioso é limitada

ao momento em que é necessário. Considere o código a seguir:

C#

if (_logger.IsEnabled(LogLevel.Information))
{
// Expensive parameter evaluation
var description = product.GetFriendlyProductDescription();

_logger.LogProductSaleDetails(
quantity,
description);
}
Para obter mais informações, confira Geração de origens de registro em log em tempo
de compilação e Registro em log de alto desempenho no .NET.

Evite a interpolação de cadeia de caracteres no


registro em log
Um erro comum é utilizar interpolação de cadeia de caracteres para criar mensagens de
log. A interpolação da cadeia de caracteres no registro em log é problemática para o
desempenho, pois a cadeia é avaliada mesmo que o LogLevel correspondente não
esteja habilitado. Em vez da interpolação da cadeia de caracteres, utilize o modelo de
mensagens de log, a formatação e a lista de argumentos. Para obter mais informações,
confira o Registro em log no .NET: Modelo de mensagens de log.

Utilize os padrões de registro em log sem


operações
Pode haver ocasiões em que você pode consumir uma biblioteca que expõe APIs de
registro em log que esperam um ILogger ou ILoggerFactory , para as quais você não
deseja fornecer um agente. Nesses casos, o pacote NuGet
Microsoft.Extensions.Logging.Abstractions fornece padrões de log sem operação.

Os consumidores de biblioteca podem usar o padrão de registro em log nulo se nenhum


ILoggerFactory for fornecido. O uso de registro em log nulo difere da definição de tipos

como anuláveis ( ILoggerFactory? ), pois os tipos são não nulos. Esses tipos baseados em
conveniência não registram nada em log e são essencialmente sem operações.
Considere o uso de qualquer um dos tipos de abstração disponíveis no local em que for
aplicável:

NullLogger.Instance
Microsoft.Extensions.Logging.Abstractions.NullLogger<T>
NullLoggerFactory.Instance
NullLoggerProvider.Instance

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Implementar um provedor de log
personalizado no .NET
Artigo • 21/03/2023

Há muitos provedores de log disponíveis para necessidades comuns de log. Talvez seja
necessário implementar um ILoggerProvider personalizado quando um dos provedores
disponíveis não atender às necessidades do aplicativo. Neste artigo, você aprenderá a
implementar um provedor de log personalizado que pode ser usado para colorir logs
no console.

 Dica

O código-fonte de exemplo do provedor de log personalizado está disponível no


repositório Docs Github. Saiba mais em GitHub: .NET Docs - Log personalizado
do console .

Configuração de agente personalizado de amostra


O exemplo cria diferentes entradas de console de cores por nível de log e ID de evento
usando o seguinte tipo de configuração:

C#

using Microsoft.Extensions.Logging;

public sealed class ColorConsoleLoggerConfiguration


{
public int EventId { get; set; }

public Dictionary<LogLevel, ConsoleColor> LogLevelToColorMap { get; set;


} = new()
{
[LogLevel.Information] = ConsoleColor.Green
};
}

O código anterior define o nível padrão como Information , a cor para Green , e o
EventId é implicitamente 0 .

Criar o agente personalizado


O nome da categoria de implementação ILogger normalmente é a origem do log. Por
exemplo, o tipo em que o agente é criado:

C#

using Microsoft.Extensions.Logging;

public sealed class ColorConsoleLogger : ILogger


{
private readonly string _name;
private readonly Func<ColorConsoleLoggerConfiguration>
_getCurrentConfig;

public ColorConsoleLogger(
string name,
Func<ColorConsoleLoggerConfiguration> getCurrentConfig) =>
(_name, _getCurrentConfig) = (name, getCurrentConfig);

public IDisposable? BeginScope<TState>(TState state) where TState :


notnull => default!;

public bool IsEnabled(LogLevel logLevel) =>


_getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel);

public void Log<TState>(


LogLevel logLevel,
EventId eventId,
TState state,
Exception? exception,
Func<TState, Exception?, string> formatter)
{
if (!IsEnabled(logLevel))
{
return;
}

ColorConsoleLoggerConfiguration config = _getCurrentConfig();


if (config.EventId == 0 || config.EventId == eventId.Id)
{
ConsoleColor originalColor = Console.ForegroundColor;

Console.ForegroundColor = config.LogLevelToColorMap[logLevel];
Console.WriteLine($"[{eventId.Id,2}: {logLevel,-12}]");

Console.ForegroundColor = originalColor;
Console.Write($" {_name} - ");

Console.ForegroundColor = config.LogLevelToColorMap[logLevel];
Console.Write($"{formatter(state, exception)}");

Console.ForegroundColor = originalColor;
Console.WriteLine();
}
}
}

O código anterior:

Cria uma instância do agente por nome de categoria.


Verifica _getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel) em
IsEnabled , para que cada logLevel tenha um agente exclusivo. Nesta
implementação, cada nível de log requer uma entrada de configuração explícita
para o log.

É uma boa prática chamar ILogger.IsEnabled em implementações ILogger.Log, pois Log


pode ser chamado por qualquer consumidor e não há garantias de que ele foi verificado
anteriormente. O método IsEnabled deve ser muito rápido na maioria das
implementações.

C#

public bool IsEnabled(LogLevel logLevel) =>


_getCurrentConfig().LogLevelToColorMap.ContainsKey(logLevel);

O agente é instanciado com o name e um Func<ColorConsoleLoggerConfiguration> , que


retorna a configuração atual – trata das atualizações para os valores de configuração
conforme monitorado por meio do retorno de chamada
IOptionsMonitor<TOptions>.OnChange.

) Importante

A implementação ILogger.Log verifica se o valor config.EventId está definido.


Quando config.EventId não está definido ou quando corresponde ao
logEntry.EventId exato, o agente registra na cor.

Provedor de agente personalizado


O objeto ILoggerProvider é responsável pela criação de instâncias do agente. Não é
necessário criar uma instância de agente por categoria, mas faz sentido para alguns
agentes, como NLog ou log4net. Essa estratégia permite que você escolha diferentes
destinos de saída de log por categoria, como no exemplo a seguir:

C#
using System.Collections.Concurrent;
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

[UnsupportedOSPlatform("browser")]
[ProviderAlias("ColorConsole")]
public sealed class ColorConsoleLoggerProvider : ILoggerProvider
{
private readonly IDisposable? _onChangeToken;
private ColorConsoleLoggerConfiguration _currentConfig;
private readonly ConcurrentDictionary<string, ColorConsoleLogger>
_loggers =
new(StringComparer.OrdinalIgnoreCase);

public ColorConsoleLoggerProvider(
IOptionsMonitor<ColorConsoleLoggerConfiguration> config)
{
_currentConfig = config.CurrentValue;
_onChangeToken = config.OnChange(updatedConfig => _currentConfig =
updatedConfig);
}

public ILogger CreateLogger(string categoryName) =>


_loggers.GetOrAdd(categoryName, name => new ColorConsoleLogger(name,
GetCurrentConfig));

private ColorConsoleLoggerConfiguration GetCurrentConfig() =>


_currentConfig;

public void Dispose()


{
_loggers.Clear();
_onChangeToken?.Dispose();
}
}

No código anterior, CreateLogger cria uma única instância do ColorConsoleLogger por


nome de categoria e a armazena no ConcurrentDictionary<TKey,TValue>. Além disso, a
interface IOptionsMonitor<TOptions> é necessária para atualizar as alterações no
objeto ColorConsoleLoggerConfiguration subjacente.

Para controlar a configuração do ColorConsoleLogger , defina um alias em seu provedor:

C#

[UnsupportedOSPlatform("browser")]
[ProviderAlias("ColorConsole")]
public sealed class ColorConsoleLoggerProvider : ILoggerProvider
A classe ColorConsoleLoggerProvider define dois atributos com escopo de classe:

UnsupportedOSPlatformAttribute: O tipo ColorConsoleLogger não é suportado no


"browser" .

ProviderAliasAttribute: as seções de configuração podem definir opções usando a


chave "ColorConsole" .

A configuração pode ser especificada com qualquer provedor de configuração válido.


Considere o seguinte arquivo appsettings.json:

JSON

{
"Logging": {
"ColorConsole": {
"LogLevelToColorMap": {
"Information": "DarkGreen",
"Warning": "Cyan",
"Error": "Red"
}
}
}
}

Ele configura os níveis de log para os seguintes valores:

LogLevel.Information: ConsoleColor.DarkGreen
LogLevel.Warning: ConsoleColor.Cyan
LogLevel.Error: ConsoleColor.Red

O nível de log Information é definido como DarkGreen, o que substitui o valor padrão
definido no objeto ColorConsoleLoggerConfiguration .

Uso e registro do agente personalizado


Por convenção, o registro de serviços para injeção de dependência acontece como parte
da rotina de inicialização de um aplicativo. O registro ocorre na classe Program ou pode
ser delegado a uma classe Startup . Nesse exemplo, você se registrará diretamente no
Program.cs.

Para adicionar o provedor de log personalizado e o agente correspondente, adicione


um ILoggerProvider com ILoggingBuilder do
HostingHostBuilderExtensions.ConfigureLogging(IHostBuilder,
Action<ILoggingBuilder>):
C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

using IHost host = Host.CreateDefaultBuilder(args)


.ConfigureLogging(builder =>
builder.ClearProviders()
.AddColorConsoleLogger(configuration =>
{
// Replace warning value from appsettings.json of "Cyan"
configuration.LogLevelToColorMap[LogLevel.Warning] =
ConsoleColor.DarkCyan;
// Replace warning value from appsettings.json of "Red"
configuration.LogLevelToColorMap[LogLevel.Error] =
ConsoleColor.DarkRed;
}))
.Build();

var logger = host.Services.GetRequiredService<ILogger<Program>>();

logger.LogDebug(1, "Does this line get hit?"); // Not logged


logger.LogInformation(3, "Nothing to see here."); // Logs in
ConsoleColor.DarkGreen
logger.LogWarning(5, "Warning... that was odd."); // Logs in
ConsoleColor.DarkCyan
logger.LogError(7, "Oops, there was an error."); // Logs in
ConsoleColor.DarkRed
logger.LogTrace(5, "== 120."); // Not logged

await host.RunAsync();

O ILoggingBuilder cria uma ou mais instâncias ILogger . As instâncias ILogger são


usadas pela estrutura para registrar as informações em log.

A configuração do arquivo appsettings.json substitui os seguintes valores:

LogLevel.Warning: ConsoleColor.DarkCyan
LogLevel.Error: ConsoleColor.DarkRed

Por convenção, os métodos de extensão no ILoggingBuilder são usados para registrar o


provedor personalizado:

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;
public static class ColorConsoleLoggerExtensions
{
public static ILoggingBuilder AddColorConsoleLogger(
this ILoggingBuilder builder)
{
builder.AddConfiguration();

builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton<ILoggerProvider,
ColorConsoleLoggerProvider>());

LoggerProviderOptions.RegisterProviderOptions
<ColorConsoleLoggerConfiguration, ColorConsoleLoggerProvider>
(builder.Services);

return builder;
}

public static ILoggingBuilder AddColorConsoleLogger(


this ILoggingBuilder builder,
Action<ColorConsoleLoggerConfiguration> configure)
{
builder.AddColorConsoleLogger();
builder.Services.Configure(configure);

return builder;
}
}

A execução deste aplicativo simples renderizará a saída de cores para a janela do


console semelhante à seguinte imagem:

Confira também
Registro em log no .NET
Provedores de log no .NET
Injeção de dependência no .NET
Log de alto desempenho no .NET
Registro em log de alto desempenho no
.NET
Artigo • 14/03/2023

A classe LoggerMessage expõe a funcionalidade para criar delegados que podem ser
armazenados em cache e exigem menos alocações de objeto e sobrecarga
computacional reduzida em comparação com os métodos de extensão do agente, tais
como LogInformation e LogDebug. Para cenários de registro em log de alto
desempenho, use o padrão LoggerMessage.

LoggerMessage fornece as seguintes vantagens de desempenho em relação aos


métodos de extensão do Agente:

Métodos de extensão do agente exigem tipos de valor de conversão boxing, como


int , em object . O padrão LoggerMessage evita a conversão boxing usando
campos Action estáticos e métodos de extensão com parâmetros fortemente
tipados.
Os métodos de extensão do agente precisam analisar o modelo de mensagem
(cadeia de caracteres de formato nomeada) sempre que uma mensagem de log é
gravada. LoggerMessage exige apenas a análise de um modelo uma vez quando a
mensagem é definida.

) Importante

Em vez de usar a classe LoggerMessage para criar logs de alto desempenho, você
pode usar o atributo LoggerMessage no .NET 6.0. O LoggerMessageAttribute dá
suporte ao log de geração de origem criado para fornecer uma solução de log
altamente utilizável e de alto desempenho para aplicativos modernos do .NET. Para
saber mais, confira Geração de origem de registro em log em tempo de
compilação (Conceitos básicos do .NET).

O aplicativo de exemplo demonstra recursos LoggerMessage com um serviço de


trabalho de processamento de fila de prioridade. O aplicativo processa itens de trabalho
na ordem de prioridade. Conforme ocorrem essas operações, são geradas mensagens
de log usando o padrão LoggerMessage.

 Dica
Todo o código-fonte do exemplo de log está disponível no Navegador de
Exemplos para download. Para obter mais informações, confira Procurar exemplos
de código: log no .NET.

Definir uma mensagem do agente


Use Define(LogLevel, EventId, String) para criar um delegado Action a fim de registrar
uma mensagem em log. Sobrecargas de Define permitem passar até seis parâmetros de
tipo para uma cadeia de caracteres de formato nomeada (modelo).

A cadeia de caracteres fornecida para o método Define é um modelo e não uma cadeia
de caracteres interpolada. Os espaços reservados são preenchidos na ordem em que os
tipos são especificados. Os nomes do espaço reservado no modelo devem ser
descritivos e consistentes em todos os modelos. Eles servem como nomes de
propriedade em dados de log estruturado. Recomendamos o uso da formatação Pascal
Case para nomes de espaço reservado. Por exemplo, {Item} , {DateTime} .

Cada mensagem de log é uma Action mantida em um campo estático criado por
LoggerMessage.Define. Por exemplo, o aplicativo de exemplo cria um campo a fim de
descrever uma mensagem de log para o processamento de itens de trabalho:

C#

private static readonly Action<ILogger, Exception>


s_failedToProcessWorkItem;

Para a Action, especifique:

O nível de log.
Um identificador de evento exclusivo (EventId) com o nome do método de
extensão estático.
O modelo de mensagem (cadeia de caracteres de formato nomeada).

À medida que os itens de trabalho são removidos da fila para processamento, o


aplicativo de serviço de trabalho define:

O nível de log como LogLevel.Critical.


A ID do evento como 13 com o nome do método FailedToProcessWorkItem .
Modelo de mensagem (cadeia de caracteres de formato nomeada) como uma
cadeia de caracteres.

C#
s_failedToProcessWorkItem = LoggerMessage.Define(
LogLevel.Critical,
new EventId(13, nameof(FailedToProcessWorkItem)),
"Epic failure processing item!");

O método LoggerMessage.Define é usado para configurar e definir um delegado Action,


que representa uma mensagem de log.

Repositórios de log estruturado podem usar o nome do evento quando recebem a ID


do evento para enriquecer o log. Por exemplo, Serilog usa o nome do evento.

A Action é invocada por meio de um método de extensão fortemente tipado. O método


PriorityItemProcessed registra uma mensagem sempre que um item de trabalho está
sendo processado. FailedToProcessWorkItem é chamado se e quando ocorre uma
exceção:

C#

protected override async Task ExecuteAsync(


CancellationToken stoppingToken)
{
using IDisposable? scope = _logger.ProcessingWorkScope(DateTime.Now);
while (!stoppingToken.IsCancellationRequested)
{
WorkItem? nextItem = _priorityQueue.ProcessNextHighestPriority();
try
{
if (nextItem is not null)
{
_logger.PriorityItemProcessed(nextItem);
}
}
catch (Exception ex)
{
_logger.FailedToProcessWorkItem(ex);
}

await Task.Delay(1_000, stoppingToken);


}
}

Inspecione a saída do console do aplicativo:

Console

crit: WorkerServiceOptions.Example.Worker[13]
Epic failure processing item!
System.Exception: Failed to verify communications.
at
WorkerServiceOptions.Example.Worker.ExecuteAsync(CancellationToken
stoppingToken) in
..\Worker.cs:line 27

Para passar parâmetros para uma mensagem de log, defina até seis tipos ao criar o
campo estático. O aplicativo de exemplo registra os detalhes do item de trabalho ao
processar itens definindo um tipo WorkItem para o campo Action:

C#

private static readonly Action<ILogger, WorkItem, Exception>


s_processingPriorityItem;

O modelo de mensagem de log do delegado recebe seus valores de espaço reservado


dos tipos fornecidos. O aplicativo de exemplo define um delegado para adicionar um
item de trabalho em que o parâmetro do item é um WorkItem :

C#

s_processingPriorityItem = LoggerMessage.Define<WorkItem>(
LogLevel.Information,
new EventId(1, nameof(PriorityItemProcessed)),
"Processing priority item: {Item}");

O método de extensão estático para registrar em log que um item de trabalho está
sendo processado, PriorityItemProcessed , recebe o valor do argumento do item de
trabalho e o passa ao delegado Action:

C#

public static void PriorityItemProcessed(


this ILogger logger, WorkItem workItem) =>
s_processingPriorityItem(logger, workItem, default!);

No método ExecuteAsync do serviço de trabalho, PriorityItemProcessed é chamado


para registrar a mensagem:

C#

protected override async Task ExecuteAsync(


CancellationToken stoppingToken)
{
using IDisposable? scope = _logger.ProcessingWorkScope(DateTime.Now);
while (!stoppingToken.IsCancellationRequested)
{
WorkItem? nextItem = _priorityQueue.ProcessNextHighestPriority();
try
{
if (nextItem is not null)
{
_logger.PriorityItemProcessed(nextItem);
}
}
catch (Exception ex)
{
_logger.FailedToProcessWorkItem(ex);
}

await Task.Delay(1_000, stoppingToken);


}
}

Inspecione a saída do console do aplicativo:

Console

info: WorkerServiceOptions.Example.Worker[1]
Processing priority item: Priority-Extreme (50db062a-9732-4418-936d-
110549ad79e4): 'Verify communications'

Definir o escopo da mensagem do agente


O método DefineScope(string) cria um delegado Func<TResult> para definir um escopo
de log. Sobrecargas de DefineScope permitem passar até três parâmetros de tipo para
uma cadeia de caracteres de formato nomeada (modelo).

Como é o caso com o método Define, a cadeia de caracteres fornecida ao método


DefineScope é um modelo e não uma cadeia de caracteres interpolada. Os espaços
reservados são preenchidos na ordem em que os tipos são especificados. Os nomes do
espaço reservado no modelo devem ser descritivos e consistentes em todos os
modelos. Eles servem como nomes de propriedade em dados de log estruturado.
Recomendamos o uso da formatação Pascal Case para nomes de espaço reservado. Por
exemplo, {Item} , {DateTime} .

Defina um escopo de log a ser aplicado a uma série de mensagens de log usando o
método DefineScope. Habilite IncludeScopes na seção de agente do console de
appSettings.json:

JSON

{
"Logging": {
"Console": {
"IncludeScopes": true
},
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}

Para criar um escopo de log, adicione um campo para conter um delegado


Func<TResult> para o escopo. O aplicativo de exemplo cria um campo chamado
_processingWorkScope (Internal/LoggerExtensions.cs):

C#

private static Func<ILogger, DateTime, IDisposable?> s_processingWorkScope;

Use DefineScope para criar o delegado. Até três tipos podem ser especificados para uso
como argumentos de modelo quando o delegado é invocado. O aplicativo de exemplo
usa um modelo de mensagem que inclui a hora e a data em que o processamento foi
iniciado:

C#

s_processingWorkScope =
LoggerMessage.DefineScope<DateTime>(
"Processing work, started at: {DateTime}");

Forneça um método de extensão estático para a mensagem de log. Inclua os


parâmetros de tipo para propriedades nomeadas exibidos no modelo de mensagem. O
aplicativo de exemplo usa um DateTime como carimbo de data/hora personalizado para
registrar e retorna _processingWorkScope :

C#

public static IDisposable? ProcessingWorkScope(


this ILogger logger, DateTime time) =>
s_processingWorkScope(logger, time);

O escopo encapsula as chamadas de extensão de log em um bloco using:

C#
protected override async Task ExecuteAsync(
CancellationToken stoppingToken)
{
using IDisposable? scope =
_logger.ProcessingWorkScope(DateTime.Now);
while (!stoppingToken.IsCancellationRequested)
{
WorkItem? nextItem =
_priorityQueue.ProcessNextHighestPriority();
try
{
if (nextItem is not null)
{
_logger.PriorityItemProcessed(nextItem);
}
}
catch (Exception ex)
{
_logger.FailedToProcessWorkItem(ex);
}

await Task.Delay(1_000, stoppingToken);


}
}
}

Inspecione as mensagens de log na saída do console do aplicativo. O seguinte resultado


mostra a ordenação prioritária de mensagens de log com a mensagem de escopo de
log incluída:

Console

info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-Extreme (f5090ede-a337-4041-b914-
f6bc0db5ae64): 'Verify communications'
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: ..\worker-service-options
info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-High (496d440f-2007-4391-b179-
09d75ab52373): 'Validate collection'
info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-Medium (dea9e3f4-d7df-46d2-b7cd-
5e0232eb98a5): 'Propagate selections'
info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-Medium (089d7f0d-da72-4b55-92fe-
57b147838056): 'Enter pooling [contention]'
info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-Low (6e68c4be-089f-4450-9080-
1ea63fcbb686): 'Health check network'
info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-Deferred (6f324134-6bb6-455f-81d4-
553ab307c421): 'Ping weather service'
info: WorkerServiceOptions.Example.Worker[1]
=> Processing work, started at: 09/25/2020 14:30:45
Processing priority item: Priority-Deferred (37bf736c-7a26-4a2a-9e56-
e89bcf3b8f35): 'Set process state'

Otimizações protegidas no nível de log


Outra otimização de desempenho adicional pode ser feita verificando LogLevel, com
ILogger.IsEnabled(LogLevel) antes de uma invocação para o método Log*
correspondente. Quando o log não está configurado para o LogLevel determinado, as
seguintes instruções são verdadeiras:

ILogger.Log não é chamado.


Uma alocação de object[] representando os parâmetros é evitada.
A conversão boxing de tipo de valor é evitada.

Para mais informações:

Micro parâmetro de comparação no runtime do .NET


Plano de fundo e motivação para verificações no nível do log

Confira também
Registro em log no .NET
Formatação de log de console
Artigo • 23/06/2023

No .NET 5, o suporte para formatação personalizada foi adicionado aos logs de console
no namespace Microsoft.Extensions.Logging.Console . Há três opções de formatação
predefinidas disponíveis: Simple, Systemd e Json.

) Importante

Antes, a enumeração ConsoleLoggerFormat era permitida para selecionar o


formato de log desejado, seja o legível por pessoas, que era o Default ou o de
linha única, também conhecido como Systemd . No entanto, eles não eram
personalizáveis e agora foram preteridos.

Neste artigo, você conhecerá os formatadores de log do console. O código-fonte de


exemplo demonstra como:

Registrar um novo formatador


Selecionar um formatador registrado a ser usado
Por meio do código ou da configuração
Implementar um formatador personalizado
Atualizar a configuração por meio de IOptionsMonitor<TOptions>
Habilitar a formatação de cores personalizadas

 Dica

Todo o código-fonte do exemplo de log está disponível no Navegador de


Exemplos para download. Para obter mais informações, confira Procurar exemplos
de código: log no .NET.

Registrar o formatador
O provedor de log Console tem vários formatadores predefinidos e expõe a capacidade
de criar o próprio formatador personalizado. Para registrar um dos formatadores
disponíveis, use o método de extensão correspondente Add{Type}Console :

Tipos disponíveis Método para registrar o tipo

ConsoleFormatterNames.Json ConsoleLoggerExtensions.AddJsonConsole
Tipos disponíveis Método para registrar o tipo

ConsoleFormatterNames.Simple ConsoleLoggerExtensions.AddSimpleConsole

ConsoleFormatterNames.Systemd ConsoleLoggerExtensions.AddSystemdConsole

Simples
Para usar o formatador de console Simple , registre-o com AddSimpleConsole :

C#

using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory =


LoggerFactory.Create(builder =>
builder.AddSimpleConsole(options =>
{
options.IncludeScopes = true;
options.SingleLine = true;
options.TimestampFormat = "HH:mm:ss ";
}));

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();


using (logger.BeginScope("[scope is enabled]"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("Logs contain timestamp and log level.");
logger.LogInformation("Each log message is fit in a single line.");
}

No código-fonte de exemplo anterior, o formatador ConsoleFormatterNames.Simple foi


registrado. Ele fornece logs com a capacidade de não apenas encapsular informações,
como tempo e nível de log em cada mensagem de log, mas também permite a inserção
de cores ANSI e recuo de mensagens.

Systemd
O agente de console ConsoleFormatterNames.Systemd:

Usa o formato e as severidades do nível de log "Syslog"


Não formata mensagens com cores
Sempre registra mensagens em uma só linha

Isso geralmente é útil para contêineres, que geralmente usam o log de console Systemd .
Com o .NET 5, o agente de console Simple também habilita uma versão compacta que
faz logon em uma só linha e também permite desabilitar cores, conforme mostrado em
um exemplo anterior.

C#

using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory =


LoggerFactory.Create(builder =>
builder.AddSystemdConsole(options =>
{
options.IncludeScopes = true;
options.TimestampFormat = "HH:mm:ss ";
}));

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();


using (logger.BeginScope("[scope is enabled]"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("Logs contain timestamp and log level.");
logger.LogInformation("Systemd console logs never provide color
options.");
logger.LogInformation("Systemd console logs always appear in a single
line.");
}

Json
Para gravar logs em um formato JSON, o formatador de console Json é usado. O
código-fonte de exemplo mostra como um aplicativo ASP.NET Core pode registrá-lo.
Usando o modelo webapp , crie um aplicativo ASP.NET Core com o comando dotnet new:

CLI do .NET

dotnet new webapp -o Console.ExampleFormatters.Json

Ao executar o aplicativo, usando o código do modelo, você obtém o formato de log


padrão abaixo:

Console

info: Console.ExampleFormatters.Json.Startup[0]
Hello .NET friends!
info: Microsoft.Hosting.Lifetime[14]
Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[14]
Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
Content root path: .\snippets\logging\console-formatter-json

Por padrão, o formatador de log de console Simple é selecionado com a configuração


padrão. Altere isso chamando AddJsonConsole no Program.cs:

C#

using System.Text.Json;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddJsonConsole(options =>
{
options.IncludeScopes = false;
options.TimestampFormat = "HH:mm:ss ";
options.JsonWriterOptions = new JsonWriterOptions
{
Indented = true
};
});

using IHost host = builder.Build();

var logger =
host.Services
.GetRequiredService<ILoggerFactory>()
.CreateLogger<Program>();

logger.LogInformation("Hello .NET friends!");

await host.RunAsync();

Como alternativa, você também pode configurar isso usando a configuração de log,
como a encontrada no arquivo appsettings.json:

JSON

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "json",
"FormatterOptions": {
"SingleLine": true,
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss ",
"UseUtcTimestamp": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
"AllowedHosts": "*"
}

Execute o aplicativo novamente, com a alteração acima, a mensagem de log agora está
formatada como JSON:

JSON

{
"Timestamp": "02:28:19 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Console.ExampleFormatters.Json.Startup",
"Message": "Hello .NET friends!",
"State": {
"Message": "Hello .NET friends!",
"{OriginalFormat}": "Hello .NET friends!"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 14,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Now listening on: https://localhost:5001",
"State": {
"Message": "Now listening on: https://localhost:5001",
"address": "https://localhost:5001",
"{OriginalFormat}": "Now listening on: {address}"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 14,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Now listening on: http://localhost:5000",
"State": {
"Message": "Now listening on: http://localhost:5000",
"address": "http://localhost:5000",
"{OriginalFormat}": "Now listening on: {address}"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Application started. Press Ctrl\u002BC to shut down.",
"State": {
"Message": "Application started. Press Ctrl\u002BC to shut down.",
"{OriginalFormat}": "Application started. Press Ctrl\u002BC to shut
down."
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Hosting environment: Development",
"State": {
"Message": "Hosting environment: Development",
"envName": "Development",
"{OriginalFormat}": "Hosting environment: {envName}"
}
}
{
"Timestamp": "02:28:21 ",
"EventId": 0,
"LogLevel": "Information",
"Category": "Microsoft.Hosting.Lifetime",
"Message": "Content root path: .\\snippets\\logging\\console-formatter-
json",
"State": {
"Message": "Content root path: .\\snippets\\logging\\console-formatter-
json",
"contentRoot": ".\\snippets\\logging\\console-formatter-json",
"{OriginalFormat}": "Content root path: {contentRoot}"
}
}

 Dica

O formatador de console Json , por padrão, registra cada mensagem em uma só


linha. Para torná-lo mais legível ao configurar o formatador, defina
JsonWriterOptions.Indented como true .
U Cuidado

Ao usar o formatador de console JSON, não passe mensagens de log que já foram
serializadas como JSON. A própria infraestrutura de log já gerencia a serialização
de mensagens de log, portanto, se você quiser passar uma mensagem de log que
já esteja serializada, ela será serializada duas vezes, causando uma saída
malformada.

Definir o formatador com configuração


Os exemplos anteriores mostraram como registrar um formatador programaticamente.
Como alternativa, isso pode ser feito com uma configuração. Considere o código-fonte
de exemplo do aplicativo Web anterior. Se você atualizar o arquivo appsettings.json em
vez de chamar ConfigureLogging no arquivo Program.cs , poderá obter o mesmo
resultado. O arquivo appsettings.json atualizado configuraria o formatador da seguinte
maneira:

JSON

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "json",
"FormatterOptions": {
"SingleLine": true,
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss ",
"UseUtcTimestamp": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
"AllowedHosts": "*"
}
Os dois valores de chave que precisam ser definidos são "FormatterName" e
"FormatterOptions" . Se um formatador com o valor definido para "FormatterName" já
estiver registrado, esse formatador será selecionado e suas propriedades poderão ser
configuradas desde que sejam fornecidas como uma chave dentro do nó
"FormatterOptions" . Os nomes de formatador predefinidos são reservados em

ConsoleFormatterNames:

ConsoleFormatterNames.Json
ConsoleFormatterNames.Simple
ConsoleFormatterNames.Systemd

Implementar um formatador personalizado


Para implementar um formatador personalizado, você precisa:

Criar uma subclasse de ConsoleFormatter, que representa o formatador


personalizado
Registrar o formatador personalizado com
ConsoleLoggerExtensions.AddConsole
ConsoleLoggerExtensions.AddConsoleFormatter<TFormatter,TOptions>
(ILoggingBuilder, Action<TOptions>)

Crie um método de extensão para lidar com isso:

C#

using Microsoft.Extensions.Logging;

namespace Console.ExampleFormatters.Custom;

public static class ConsoleLoggerExtensions


{
public static ILoggingBuilder AddCustomFormatter(
this ILoggingBuilder builder,
Action<CustomOptions> configure) =>
builder.AddConsole(options => options.FormatterName = "customName")
.AddConsoleFormatter<CustomFormatter, CustomOptions>(configure);
}

As CustomOptions são definidas desta forma:

C#

using Microsoft.Extensions.Logging.Console;
namespace Console.ExampleFormatters.Custom;

public sealed class CustomOptions : ConsoleFormatterOptions


{
public string? CustomPrefix { get; set; }
}

No código anterior, as opções são uma subclasse de ConsoleFormatterOptions.

A API AddConsoleFormatter :

Registra uma subclasse de ConsoleFormatter


Manipula a configuração:
Usa um token de alteração para sincronizar atualizações, com base no padrão
de opções e na interface IOptionsMonitor

C#

using Console.ExampleFormatters.Custom;
using Microsoft.Extensions.Logging;

using ILoggerFactory loggerFactory =


LoggerFactory.Create(builder =>
builder.AddCustomFormatter(options =>
options.CustomPrefix = " ~~~~~ "));

ILogger<Program> logger = loggerFactory.CreateLogger<Program>();


using (logger.BeginScope("TODO: Add logic to enable scopes"))
{
logger.LogInformation("Hello World!");
logger.LogInformation("TODO: Add logic to enable timestamp and log level
info.");
}

Definir uma subclasse de CustomerFormatter de ConsoleFormatter :

C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;

namespace Console.ExampleFormatters.Custom;

public sealed class CustomFormatter : ConsoleFormatter, IDisposable


{
private readonly IDisposable? _optionsReloadToken;
private CustomOptions _formatterOptions;
public CustomFormatter(IOptionsMonitor<CustomOptions> options)
// Case insensitive
: base("customName") =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);

private void ReloadLoggerOptions(CustomOptions options) =>


_formatterOptions = options;

public override void Write<TState>(


in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter)
{
string? message =
logEntry.Formatter?.Invoke(
logEntry.State, logEntry.Exception);

if (message is null)
{
return;
}

CustomLogicGoesHere(textWriter);
textWriter.WriteLine(message);
}

private void CustomLogicGoesHere(TextWriter textWriter)


{
textWriter.Write(_formatterOptions.CustomPrefix);
}

public void Dispose() => _optionsReloadToken?.Dispose();


}

A API CustomFormatter.Write<TState> anterior determina qual texto é encapsulado em


cada mensagem de log. Um ConsoleFormatter padrão deve ter a capacidade mínima de
encapsular escopos, carimbos de data/hora e o nível de severidade dos logs. Além
disso, você também pode codificar cores ANSI nas mensagens de log e fornecer os
recuos desejados. A implementação do CustomFormatter.Write<TState> não tem essas
funcionalidades.

Para se inspirar em personalizar ainda mais a formatação, confira as implementações


existentes no namespace Microsoft.Extensions.Logging.Console :

SimpleConsoleFormatter .
SystemdConsoleFormatter
JsonConsoleFormatter
Opções de configuração personalizada
Para personalizar ainda mais a extensibilidade de log, a classe derivada
ConsoleFormatterOptions pode ser configurada por meio de qualquer provedor de
configuração. Por exemplo, você pode usar o provedor de configuração JSON para
definir as opções personalizadas. Primeiro defina a subclasse ConsoleFormatterOptions.

C#

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.CustomWithConfig;

public sealed class CustomWrappingConsoleFormatterOptions :


ConsoleFormatterOptions
{
public string? CustomPrefix { get; set; }

public string? CustomSuffix { get; set; }


}

A classe de opções de formatador de console anterior define duas propriedades


personalizadas, representando um prefixo e um sufixo. Depois, defina o arquivo
appsettings.json que vai configurar as opções de formatador do console.

JSON

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"Console": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
},
"FormatterName": "CustomTimePrefixingFormatter",
"FormatterOptions": {
"CustomPrefix": "|-<[",
"CustomSuffix": "]>-|",
"SingleLine": true,
"IncludeScopes": true,
"TimestampFormat": "HH:mm:ss.ffff ",
"UseUtcTimestamp": true,
"JsonWriterOptions": {
"Indented": true
}
}
}
},
"AllowedHosts": "*"
}

No arquivo de configuração JSON anterior:

O nó "Logging" define um "Console" .


O nó "Console" especifica um "FormatterName" de
"CustomTimePrefixingFormatter" , que é mapeado para um formatador
personalizado.
O nó "FormatterOptions" define um "CustomPrefix" e "CustomSuffix" , bem como
algumas outras opções derivadas.

 Dica

O caminho JSON $.Logging.Console.FormatterOptions é reservado e é mapeado


para um ConsoleFormatterOptions personalizado quando adicionado usando o
método de extensão AddConsoleFormatter. Isso fornece a capacidade de definir
propriedades personalizadas, além das disponíveis.

Considere o seguinte CustomDatePrefixingFormatter :

C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;

namespace Console.ExampleFormatters.CustomWithConfig;

public sealed class CustomTimePrefixingFormatter : ConsoleFormatter,


IDisposable
{
private readonly IDisposable? _optionsReloadToken;
private CustomWrappingConsoleFormatterOptions _formatterOptions;

public
CustomTimePrefixingFormatter(IOptionsMonitor<CustomWrappingConsoleFormatterO
ptions> options)
// Case insensitive
: base(nameof(CustomTimePrefixingFormatter)) =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);
private void ReloadLoggerOptions(CustomWrappingConsoleFormatterOptions
options) =>
_formatterOptions = options;

public override void Write<TState>(


in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter)
{
string message =
logEntry.Formatter(
logEntry.State, logEntry.Exception);

if (message == null)
{
return;
}

WritePrefix(textWriter);
textWriter.Write(message);
WriteSuffix(textWriter);
}

private void WritePrefix(TextWriter textWriter)


{
DateTime now = _formatterOptions.UseUtcTimestamp
? DateTime.UtcNow
: DateTime.Now;

textWriter.Write($"""
{_formatterOptions.CustomPrefix}
{now.ToString(_formatterOptions.TimestampFormat)}
""");
}

private void WriteSuffix(TextWriter textWriter) =>


textWriter.WriteLine($" {_formatterOptions.CustomSuffix}");

public void Dispose() => _optionsReloadToken?.Dispose();


}

Na implementação do formatador anterior:

As CustomWrappingConsoleFormatterOptions são monitoradas quanto à alteração e


atualizadas corretamente.
As mensagens gravadas são encapsuladas com o prefixo e o sufixo configurados.
Um carimbo de data/hora é adicionado após o prefixo, mas antes da mensagem
usando os valores configurados ConsoleFormatterOptions.UseUtcTimestamp e
ConsoleFormatterOptions.TimestampFormat.
Para usar opções de configuração personalizadas, com implementações de formatador
personalizado, adicione ao chamar ConfigureLogging(IHostBuilder,
Action<HostBuilderContext,ILoggingBuilder>).

C#

using Console.ExampleFormatters.CustomWithConfig;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Logging.AddConsole()
.AddConsoleFormatter<
CustomTimePrefixingFormatter, CustomWrappingConsoleFormatterOptions>
();
using IHost host = builder.Build();

ILoggerFactory loggerFactory =
host.Services.GetRequiredService<ILoggerFactory>();
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();

using (logger.BeginScope("Logging scope"))


{
logger.LogInformation("Hello World!");
logger.LogInformation("The .NET developer community happily welcomes
you.");
}

A saída do console a seguir é semelhante à esperada quando esse


CustomTimePrefixingFormatter é usado.

Console

|-<[ 15:03:15.6179 Hello World! ]>-|


|-<[ 15:03:15.6347 The .NET developer community happily welcomes you. ]>-|

Implementar a formatação de cores


personalizadas
Para habilitar corretamente as funcionalidades de cor no formatador de log
personalizado, estenda o SimpleConsoleFormatterOptions como ele tivesse uma
propriedade SimpleConsoleFormatterOptions.ColorBehavior que pode ser útil para
habilitar cores em logs.
Crie uma classe SimpleConsoleFormatterOptions derivada de CustomColorOptions :

C#

using Microsoft.Extensions.Logging.Console;

namespace Console.ExampleFormatters.Custom;

public class CustomColorOptions : SimpleConsoleFormatterOptions


{
public string? CustomPrefix { get; set; }
}

Depois, escreva alguns métodos de extensão em uma classe TextWriterExtensions que


permitam inserir facilmente as cores codificadas como ANSI em mensagens de log
formatadas:

C#

namespace Console.ExampleFormatters.Custom;

public static class TextWriterExtensions


{
const string DefaultForegroundColor = "\x1B[39m\x1B[22m";
const string DefaultBackgroundColor = "\x1B[49m";

public static void WriteWithColor(


this TextWriter textWriter,
string message,
ConsoleColor? background,
ConsoleColor? foreground)
{
// Order:
// 1. background color
// 2. foreground color
// 3. message
// 4. reset foreground color
// 5. reset background color

var backgroundColor = background.HasValue ?


GetBackgroundColorEscapeCode(background.Value) : null;
var foregroundColor = foreground.HasValue ?
GetForegroundColorEscapeCode(foreground.Value) : null;

if (backgroundColor != null)
{
textWriter.Write(backgroundColor);
}
if (foregroundColor != null)
{
textWriter.Write(foregroundColor);
}
textWriter.WriteLine(message);

if (foregroundColor != null)
{
textWriter.Write(DefaultForegroundColor);
}
if (backgroundColor != null)
{
textWriter.Write(DefaultBackgroundColor);
}
}

static string GetForegroundColorEscapeCode(ConsoleColor color) =>


color switch
{
ConsoleColor.Black => "\x1B[30m",
ConsoleColor.DarkRed => "\x1B[31m",
ConsoleColor.DarkGreen => "\x1B[32m",
ConsoleColor.DarkYellow => "\x1B[33m",
ConsoleColor.DarkBlue => "\x1B[34m",
ConsoleColor.DarkMagenta => "\x1B[35m",
ConsoleColor.DarkCyan => "\x1B[36m",
ConsoleColor.Gray => "\x1B[37m",
ConsoleColor.Red => "\x1B[1m\x1B[31m",
ConsoleColor.Green => "\x1B[1m\x1B[32m",
ConsoleColor.Yellow => "\x1B[1m\x1B[33m",
ConsoleColor.Blue => "\x1B[1m\x1B[34m",
ConsoleColor.Magenta => "\x1B[1m\x1B[35m",
ConsoleColor.Cyan => "\x1B[1m\x1B[36m",
ConsoleColor.White => "\x1B[1m\x1B[37m",

_ => DefaultForegroundColor
};

static string GetBackgroundColorEscapeCode(ConsoleColor color) =>


color switch
{
ConsoleColor.Black => "\x1B[40m",
ConsoleColor.DarkRed => "\x1B[41m",
ConsoleColor.DarkGreen => "\x1B[42m",
ConsoleColor.DarkYellow => "\x1B[43m",
ConsoleColor.DarkBlue => "\x1B[44m",
ConsoleColor.DarkMagenta => "\x1B[45m",
ConsoleColor.DarkCyan => "\x1B[46m",
ConsoleColor.Gray => "\x1B[47m",

_ => DefaultBackgroundColor
};
}

Um formatador de cores personalizadas que aplica cores personalizadas pode ser


definido da seguinte maneira:
C#

using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;

namespace Console.ExampleFormatters.Custom;

public sealed class CustomColorFormatter : ConsoleFormatter, IDisposable


{
private readonly IDisposable? _optionsReloadToken;
private CustomColorOptions _formatterOptions;

private bool ConsoleColorFormattingEnabled =>


_formatterOptions.ColorBehavior == LoggerColorBehavior.Enabled ||
_formatterOptions.ColorBehavior == LoggerColorBehavior.Default &&
System.Console.IsOutputRedirected == false;

public CustomColorFormatter(IOptionsMonitor<CustomColorOptions> options)


// Case insensitive
: base("customName") =>
(_optionsReloadToken, _formatterOptions) =
(options.OnChange(ReloadLoggerOptions), options.CurrentValue);

private void ReloadLoggerOptions(CustomColorOptions options) =>


_formatterOptions = options;

public override void Write<TState>(


in LogEntry<TState> logEntry,
IExternalScopeProvider? scopeProvider,
TextWriter textWriter)
{
if (logEntry.Exception is null)
{
return;
}

string? message =
logEntry.Formatter?.Invoke(
logEntry.State, logEntry.Exception);

if (message is null)
{
return;
}

CustomLogicGoesHere(textWriter);
textWriter.WriteLine(message);
}
private void CustomLogicGoesHere(TextWriter textWriter)
{
if (ConsoleColorFormattingEnabled)
{
textWriter.WriteWithColor(
_formatterOptions.CustomPrefix ?? string.Empty,
ConsoleColor.Black,
ConsoleColor.Green);
}
else
{
textWriter.Write(_formatterOptions.CustomPrefix);
}
}

public void Dispose() => _optionsReloadToken?.Dispose();


}

Quando você executar o aplicativo, os logs mostrarão a mensagem CustomPrefix na cor


verde quando FormatterOptions.ColorBehavior for Enabled .

7 Observação

Quando LoggerColorBehavior é Disabled , as mensagens de log não interpretam


códigos de cor ANSI inseridos em mensagens de log. Nesse caso, a mensagem
bruta é gerada. Considere o seguinte exemplo:

C#

logger.LogInformation("Random log \x1B[42mwith green background\x1B[49m


message");

Isso geraria a cadeia de caracteres verbatim não colorida.

Saída

Random log \x1B[42mwith green background\x1B[49m message

Confira também
Registro em log no .NET
Implementar um provedor de log personalizado no .NET
Log de alto desempenho no .NET
Host Genérico .NET
Artigo • 03/04/2024

Neste artigo, você aprenderá sobre os vários padrões para configuração e criação de um
host genérico .NET disponível no pacote NuGet Microsoft.Extensions.Hosting . O Host
Genérico do .NET é responsável pela inicialização e gerenciamento do tempo de vida do
aplicativo. Os modelos do Serviço de Trabalho criam um host genérico .NET.
HostApplicationBuilder O Host Genérico pode ser usado com outros tipos de aplicativos
.NET, como aplicativos de console.

Um host é um objeto que encapsula os recursos e a funcionalidade de tempo de vida de


um aplicativo, tais como:

DI (injeção de dependência)
Log
Configuração
Desligamento do aplicativo
Implementações de IHostedService

Quando um host é iniciado, ele chama IHostedService.StartAsync em cada


implementação de IHostedService registrada na coleção de serviços hospedados do
contêiner de serviço. Em um aplicativo de serviço de trabalho, todas as implementações
IHostedService que contêm instâncias BackgroundService têm seus métodos

BackgroundService.ExecuteAsync chamados.

O principal motivo para incluir todos os recursos interdependentes do aplicativo em um


objeto é o gerenciamento de tempo de vida: controle sobre a inicialização do aplicativo
e desligamento normal.

Configurar um host
O host normalmente é configurado, compilado e executado pelo código na classe
Program . O método Main :

IHostApplicationBuilder

Chama um método CreateApplicationBuilder para criar e configurar um objeto


construtor.
Chama Build() para criar uma instância IHost.
Chama Run ou o método RunAsync no objeto host.
Os modelos do Serviço de Trabalho do .NET geram o seguinte código para criar um
Host Genérico:

C#

using Example.WorkerService;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);


builder.Services.AddHostedService<Worker>();

IHost host = builder.Build();


host.Run();

Para obter mais informações sobre as Funções de Trabalho, confira Serviços de Trabalho
no .NET.

Configurações do construtor de host


IHostApplicationBuilder

O método CreateApplicationBuilder:

Define a raiz do conteúdo como o caminho retornado por


GetCurrentDirectory().
Carrega a configuração do host de:
Variáveis de ambiente prefixadas com DOTNET_ .
Argumentos de linha de comando.
Carrega a configuração do aplicativo de:
appsettings.json.
appsettings.{Environment}.json.
Gerenciador de Segredo quando o aplicativo é executado no ambiente
Development .

Variáveis de ambiente.
Argumentos de linha de comando.
Adiciona os seguintes provedores de registro em log:
Console
Depurar
EventSource
EventLog (somente quando em execução no Windows)
Habilita a validação de escopo e a validação de dependência quando o
ambiente é Development .
HostApplicationBuilder.Services é uma instância de
Microsoft.Extensions.DependencyInjection.IServiceCollection. Esses serviços são
usados para criar um IServiceProvider usado com a injeção de dependência para
resolver os serviços registrados.

Serviços fornecidos pela estrutura


Ao chamar IHostBuilder.Build() ou HostApplicationBuilder.Build(), os seguintes serviços
são registrados automaticamente:

IHostApplicationLifetime
IHostLifetime
IHostEnvironment

IHostApplicationLifetime
Injete o serviço IHostApplicationLifetime em qualquer classe para lidar com tarefas de
pós-inicialização e de desligamento normal. Três propriedades na interface são tokens
de cancelamento usados para registrar métodos de manipulador de eventos de
inicialização e desligamento do aplicativo. A interface também inclui um método
StopApplication().

O exemplo a seguir é uma implementação IHostedService e IHostedLifecycleService que


registra eventos IHostApplicationLifetime :

C#

using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

namespace AppLifetime.Example;

public sealed class ExampleHostedService : IHostedService,


IHostedLifecycleService
{
private readonly ILogger _logger;

public ExampleHostedService(
ILogger<ExampleHostedService> logger,
IHostApplicationLifetime appLifetime)
{
_logger = logger;
appLifetime.ApplicationStarted.Register(OnStarted);
appLifetime.ApplicationStopping.Register(OnStopping);
appLifetime.ApplicationStopped.Register(OnStopped);
}

Task IHostedLifecycleService.StartingAsync(CancellationToken
cancellationToken)
{
_logger.LogInformation("1. StartingAsync has been called.");

return Task.CompletedTask;
}

Task IHostedService.StartAsync(CancellationToken cancellationToken)


{
_logger.LogInformation("2. StartAsync has been called.");

return Task.CompletedTask;
}

Task IHostedLifecycleService.StartedAsync(CancellationToken
cancellationToken)
{
_logger.LogInformation("3. StartedAsync has been called.");

return Task.CompletedTask;
}

private void OnStarted()


{
_logger.LogInformation("4. OnStarted has been called.");
}

private void OnStopping()


{
_logger.LogInformation("5. OnStopping has been called.");
}

Task IHostedLifecycleService.StoppingAsync(CancellationToken
cancellationToken)
{
_logger.LogInformation("6. StoppingAsync has been called.");

return Task.CompletedTask;
}

Task IHostedService.StopAsync(CancellationToken cancellationToken)


{
_logger.LogInformation("7. StopAsync has been called.");

return Task.CompletedTask;
}

Task IHostedLifecycleService.StoppedAsync(CancellationToken
cancellationToken)
{
_logger.LogInformation("8. StoppedAsync has been called.");

return Task.CompletedTask;
}

private void OnStopped()


{
_logger.LogInformation("9. OnStopped has been called.");
}
}

O modelo do Serviço de Trabalho pode ser modificado para adicionar a implementação


ExampleHostedService :

C#

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using AppLifetime.Example;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddHostedService<ExampleHostedService>();
using IHost host = builder.Build();

await host.RunAsync();

O aplicativo gravaria a seguinte saída de exemplo:

C#

// Sample output:
// info: AppLifetime.Example.ExampleHostedService[0]
// 1.StartingAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 2.StartAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 3.StartedAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 4.OnStarted has been called.
// info: Microsoft.Hosting.Lifetime[0]
// Application started. Press Ctrl+C to shut down.
// info: Microsoft.Hosting.Lifetime[0]
// Hosting environment: Production
// info: Microsoft.Hosting.Lifetime[0]
// Content root path: ..\app-lifetime\bin\Debug\net8.0
// info: AppLifetime.Example.ExampleHostedService[0]
// 5.OnStopping has been called.
// info: Microsoft.Hosting.Lifetime[0]
// Application is shutting down...
// info: AppLifetime.Example.ExampleHostedService[0]
// 6.StoppingAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 7.StopAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 8.StoppedAsync has been called.
// info: AppLifetime.Example.ExampleHostedService[0]
// 9.OnStopped has been called.

A saída mostra a ordem de todos os vários eventos de ciclo de vida:

1. IHostedLifecycleService.StartingAsync
2. IHostedService.StartAsync
3. IHostedLifecycleService.StartedAsync
4. IHostApplicationLifetime.ApplicationStarted

Quando o aplicativo é interrompido, por exemplo, com Ctrl + C , os seguintes eventos


são gerados:

1. IHostApplicationLifetime.ApplicationStopping
2. IHostedLifecycleService.StoppingAsync
3. IHostedService.StopAsync
4. IHostedLifecycleService.StoppedAsync
5. IHostApplicationLifetime.ApplicationStopped

IHostLifetime
A implementação IHostLifetime controla quando o host é iniciado e quando ele é
interrompido. A última implementação registrada é usada.
Microsoft.Extensions.Hosting.Internal.ConsoleLifetime é a implementação
IHostLifetime padrão. Para obter mais informações sobre a mecânica de tempo de vida

do desligamento, consulte Desligamento do host.

A interface IHostLifetime expõe um método IHostLifetime.WaitForStartAsync, que é


chamado no início de IHost.StartAsync que aguardará até que ele seja concluído antes
de continuar. Isso pode ser usado para atrasar a inicialização até que seja sinalizado por
um evento externo.

Além disso, a interface IHostLifetime expõe um método IHostLifetime.StopAsync, que é


chamado de IHost.StopAsync para indicar que o host está parando e é hora de desligar.

IHostEnvironment
Injete o serviço IHostEnvironment em uma classe para obter informações sobre as
seguintes configurações:

IHostEnvironment.ApplicationName
IHostEnvironment.ContentRootFileProvider
IHostEnvironment.ContentRootPath
IHostEnvironment.EnvironmentName

Além disso, o serviço IHostEnvironment expõe a capacidade de avaliar o ambiente com


a ajuda desses métodos de extensão:

HostingEnvironmentExtensions.IsDevelopment
HostingEnvironmentExtensions.IsEnvironment
HostingEnvironmentExtensions.IsProduction
HostingEnvironmentExtensions.IsStaging

Configuração do host
A configuração do host é usada para configurar propriedades da implementação
IHostEnvironment.

IHostApplicationBuilder

A configuração do host está disponível na propriedade


IHostApplicationBuilder.Configuration e a implementação do ambiente está
disponível na propriedade IHostApplicationBuilder.Environment. Para configurar o
host, acesse a propriedade Configuration e chame qualquer um dos métodos de
extensão disponíveis.

Para adicionar a configuração do host, considere o seguinte exemplo:

C#

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Environment.ContentRootPath = Directory.GetCurrentDirectory();
builder.Configuration.AddJsonFile("hostsettings.json", optional: true);
builder.Configuration.AddEnvironmentVariables(prefix: "PREFIX_");
builder.Configuration.AddCommandLine(args);

using IHost host = builder.Build();


// Application code should start here.

await host.RunAsync();

O código anterior:

Define a raiz do conteúdo como o caminho retornado por


GetCurrentDirectory().
Carrega a configuração do host de:
hostsettings.json.
Variáveis de ambiente prefixadas com PREFIX_ .
Argumentos de linha de comando.

Configuração do aplicativo
IHostApplicationBuilder

A configuração do aplicativo é criada chamando ConfigureAppConfiguration em


um IHostApplicationBuilder. A propriedade
públicaIHostApplicationBuilder.Configuration permite que os consumidores leiam
ou façam alterações na configuração existente usando métodos de extensão
disponíveis.

Para obter mais informações, confira Configuração no .NET.

Desligamento do host
Há várias maneiras como um processo hospedado é interrompido. O mais comum é que
um processo de serviço hospedado possa ser interrompido das seguintes maneiras:

Se alguém não chamar Run ou


HostingAbstractionsHostExtensions.WaitForShutdown o aplicativo sair
normalmente com a conclusão de Main .
Se o aplicativo falhar.
Se o aplicativo for desligado à força usando SIGKILL (ou CTRL + Z ).

O código de hospedagem não é responsável por lidar com esses cenários. O


proprietário do processo precisa lidar com eles da mesma forma que qualquer outro
aplicativo. Há várias outras maneiras como um processo de serviço hospedado pode ser
interrompido:

Se ConsoleLifetime for usado (UseConsoleLifetime), ele escutará os sinais a seguir


e tentará interromper o host normalmente.
SIGINT (ou CTRL + C ).
SIGQUIT (ou CTRL + BREAK no Windows, CTRL + \ no Unix).
SIGTERM (enviado por outros aplicativos, como docker stop ).
Se o aplicativo chamar Environment.Exit.

A lógica de hospedagem interna lida com esses cenários, especificamente a classe


ConsoleLifetime . ConsoleLifetime tenta lidar com os sinais de "desligamento" SIGINT,

SIGQUIT e SIGTERM para permitir uma saída normal para o aplicativo.

Antes do .NET 6, não havia uma maneira de o código .NET manipular normalmente o
SIGTERM. Para contornar essa limitação, ConsoleLifetime assinaria para
System.AppDomain.ProcessExit. Quando ProcessExit era acionado, ConsoleLifetime
sinalizaria que o host parasse e bloqueasse o thread ProcessExit , aguardando o host
parar.

O manipulador de saída do processo permitiria que o código de limpeza no aplicativo


fosse executado , por exemplo, IHost.StopAsync e código depois de
HostingAbstractionsHostExtensions.Run no método Main .

No entanto, havia outros problemas com essa abordagem porque SIGTERM não era a
única maneira de gerar ProcessExit . SIGTERM também é gerado quando o código do
aplicativo chama Environment.Exit . Environment.Exit não é uma maneira normal de
desligar um processo no modelo de aplicativo Microsoft.Extensions.Hosting . Ele aciona
o evento ProcessExit e, em seguida, sai do processo. O final do método Main não é
executado. Os threads em segundo e primeiro plano são encerrados e os
blocos finally não são executados.

Como ConsoleLifetime bloqueava ProcessExit enquanto esperava o host ser desligado,


esse comportamento fazia com que deadlocks de Environment.Exit também
bloqueassem a espera pela chamada ProcessExit . Além disso, como a manipulação do
SIGTERM estava tentando desligar o processo normalmente, ConsoleLifetime definia
ExitCode para 0 , o que sobrecarregava o código de saída do usuário passado para
Environment.Exit .

No .NET 6, os sinais POSIX têm suporte e são manipulados. O ConsoleLifetime


manipula SIGTERM normalmente e não se envolve mais quando Environment.Exit é
invocado.
 Dica

No .NET 6+, ConsoleLifetime não tem mais lógica para lidar com o cenário
Environment.Exit . Os aplicativos que chamam Environment.Exit e precisam

executar a lógica de limpeza podem se inscrever em ProcessExit sozinhos. A


hospedagem não tentará mais interromper normalmente o host nesses cenários.

Se o aplicativo usa hospedagem e você deseja interromper normalmente o host, você


pode chamar IHostApplicationLifetime.StopApplication em vez de Environment.Exit .

Processo de desligamento de hospedagem


O diagrama de sequência a seguir mostra como os sinais são tratados internamente no
código de hospedagem. A maioria dos usuários não precisa entender esse processo.
Mas para desenvolvedores que precisam de uma compreensão profunda, um bom
visual pode ajudá-los a começar.

Depois que o host for iniciado, quando um usuário chamar Run ou WaitForShutdown , um
manipulador se registra em IApplicationLifetime.ApplicationStopping. A execução está
pausada em WaitForShutdown , aguardando o evento ApplicationStopping ser acionado.
O método Main não retorna imediatamente e o aplicativo permanece em execução até
Run ou WaitForShutdown retorna.

Quando um sinal é enviado para o processo, ele inicia a seguinte sequência:

User ConsoleLife me Applica onLife me WaitForShutdownAsync Host IHostedService

CTRL+C / SIGTERM
StopApplica on()
Raise "Applica onStopping"

Resume a wai ng "await" asynchronously


Cancel = true
StopAsync()

foreach IHostedService

StopAsync()

No fyStopped()
StopAsync()

Resume wai ng "Main" method



1. O controle flui de ConsoleLifetime para ApplicationLifetime para acionar o
evento ApplicationStopping . Isso sinaliza WaitForShutdownAsync para desbloquear
o código de execução Main . Enquanto isso, o manipulador de sinal POSIX retorna
Cancel = true desde que esse sinal POSIX tenha sido tratado.

2. O código de execução Main começa a ser executado novamente e informa ao host


para StopAsync() , o qual, por sua vez, interrompe todos os serviços hospedados e
gera quaisquer outros eventos parados.
3. Por fim, WaitForShutdown sai, permitindo que qualquer código de limpeza de
aplicativo seja executado e para que o método Main saia normalmente.

Desligamento de host em cenários de servidor Web


Há vários outros cenários comuns em que o desligamento normal funciona no Kestrel
para os protocolos HTTP/1.1 e HTTP/2, e como você pode configurá-lo em ambientes
diferentes com um balanceador de carga para esvaziar o tráfego sem problemas.
Embora a configuração do servidor Web esteja além do escopo deste artigo, você pode
encontrar mais informações na documentação Configurar opções para o servidor Web
ASP.NET Core Kestrel.

Quando o Host recebe um sinal de desligamento (por exemplo, CTL + C ou StopAsync ),


ele notifica o aplicativo sinalizando ApplicationStopping. Você deverá assinar esse
evento se tiver operações de execução longa que precisem ser concluídas normalmente.

Em seguida, o Host chama IServer.StopAsync com um tempo limite de desligamento


que você pode configurar (30s por padrão). Kestrel (e Http.Sys) fecham suas associações
de porta e param de aceitar novas conexões. Eles também informam as conexões atuais
para interromper o processamento de novas solicitações. Para HTTP/2 e HTTP/3, uma
mensagem GOAWAY preliminar é enviada ao cliente. Para HTTP/1.1, eles interrompem o
loop de conexão porque as solicitações são processadas em ordem. O IIS se comporta
de modo diferente, rejeitando novas solicitações com um código de status 503.

As solicitações ativas têm até o tempo limite de desligamento para serem concluídas. Se
todas foram concluídas antes do tempo limite, o servidor retornará o controle para o
host mais cedo. Se o tempo limite expirar, as conexões e solicitações pendentes serão
anuladas forçadamente, o que pode causar erros nos logs e nos clientes.

Considerações do Balanceador de carga

Para garantir uma transição suave de clientes para um novo destino ao trabalhar com
um balanceador de carga, você pode seguir estas etapas:
Abra a nova instância e comece a balancear o tráfego para ela (talvez você já tenha
várias instâncias para fins de dimensionamento).
Desabilite ou remova a instância antiga na configuração do balanceador de carga
para que ela pare de receber novo tráfego.
Sinalize a instância antiga para desligar.
Aguarde até ela esvaziar ou atingir o tempo limite.

Confira também
Injeção de dependência no .NET
Registro em log no .NET
Configuração no .NET
Serviços de Trabalho no .NET
Host da Web do ASP.NET Core
Configuração do servidor Web ASP.NET Core Kestrel
Os bugs de host genéricos devem ser criados no repositório
github.com/dotnet/runtime

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Introduction to resilient app
development
Article • 11/29/2023

Resiliency is the ability of an app to recover from transient failures and continue to
function. In the context of .NET programming, resilience is achieved by designing apps
that can handle failures gracefully and recover quickly. To help build resilient apps in
.NET, the following two packages are available on NuGet:

ノ Expand table

NuGet package Description

📦 Microsoft.Extensions.Resilience This NuGet package provides mechanisms to harden


apps against transient failures.

📦 Microsoft.Extensions.Http.Resilience This NuGet package provides resilience mechanisms


specifically for the HttpClient class.

These two NuGet packages are built on top of Polly , which is a popular open-source
project. Polly is a .NET resilience and transient fault-handling library that allows
developers to express strategies such as retry, circuit breaker, timeout, bulkhead
isolation, rate-limiting, fallback, and hedging in a fluent and thread-safe manner.

) Important

The Microsoft.Extensions.Http.Polly NuGet package is deprecated. Use either of


the aforementioned packages instead.

Get started
To get started with resilience in .NET, install the Microsoft.Extensions.Resilience NuGet
package.

.NET CLI

.NET CLI

dotnet add package Microsoft.Extensions.Resilience --version 8.0.0


For more information, see dotnet add package or Manage package dependencies in
.NET applications.

Build a resilience pipeline


To use resilience, you must first build a pipeline of resilience-based strategies. Each
configured strategy executes in order of configuration. In other words, order is
important. The entry point is an extension method on the IServiceCollection type,
named AddResiliencePipeline . This method takes an identifier of the pipeline and a
delegate that configures the pipeline. The delegate is passed an instance of
ResiliencePipelineBuilder , which is used to add resilience strategies to the pipeline.

Consider the following string-based key example:

C#

using Microsoft.Extensions.DependencyInjection;
using Polly;
using Polly.CircuitBreaker;
using Polly.Registry;
using Polly.Retry;
using Polly.Timeout;

var services = new ServiceCollection();

const string key = "Retry-Timeout";

services.AddResiliencePipeline(key, static builder =>


{
// See: https://www.pollydocs.org/strategies/retry.html
builder.AddRetry(new RetryStrategyOptions
{
ShouldHandle = new
PredicateBuilder().Handle<TimeoutRejectedException>()
});

// See: https://www.pollydocs.org/strategies/timeout.html
builder.AddTimeout(TimeSpan.FromSeconds(1.5));
});

The preceding code:

Creates a new ServiceCollection instance.


Defines a key to identify the pipeline.
Adds a resilience pipeline to the ServiceCollection instance.
Configures the pipeline with a retry and timeout strategies.
Each pipeline is configured for a given key , and each key is used to identify its
corresponding ResiliencePipeline when getting the pipeline from the provider. The
key is a generic type parameter of the AddResiliencePipeline method.

Resilience pipeline builder extensions


To add a strategy to the pipeline, call any of the available Add* extension methods on
the ResiliencePipelineBuilder instance.

AddRetry : Try again if something fails, which is useful when the problem is
temporary and might go away.
AddCircuitBreaker : Stop trying if something is broken or busy, which benefits you

by avoiding wasted time and making things worse.


AddTimeout : Give up if something takes too long, which can improve performance

by freeing up resources.
AddRateLimiter : Limit how many requests you accept, which enables you to

control inbound load.


AddConcurrencyLimiter : Limit how many requests you make, which enables you to

control outbound load.


AddFallback : Do something else when experiencing failures, which improves user

experience.
AddHedging : Issue multiple requests in case of high latency or failure, which can

improve responsiveness.

For more information, see Resilience strategies . For examples, see Build resilient HTTP
apps: Key development patterns.

Metrics enrichment
Enrichment is the automatic augmentation of telemetry with well-known state, in the
form of name/value pairs. For example, an app might emit a log that includes the
operation and result code as columns to represent the outcome of some operation. In
this situation and depending on peripheral context, enrichment adds Cluster name,
Process name, Region, Tenant ID, and more to the log as it's sent to the telemetry
backend. When enrichment is added, the app code doesn't need to do anything extra to
benefit from enriched metrics.

How enrichment works


Imagine 1,000 globally distributed service instances generating logs and metrics. When
you encounter an issue on your service dashboard, it's crucial to quickly identify the
problematic region or data center. Enrichment ensures that metric records contain the
necessary information to pinpoint failures in distributed systems. Without enrichment,
the burden falls on the app code to internally manage this state, integrate it into the
logging process, and manually transmit it. Enrichment simplifies this process, seamlessly
handling it without affecting the app's logic.

In the case of resiliency, when you add enrichment the following dimensions are added
to the outgoing telemetry:

error.type : Low-cardinality version of an exception's information.

request.name : The name of the request.


request.dependency.name : The name of the dependency.

Under the covers, resilience enrichment is built on top of Polly's Telemetry


MeteringEnricher . For more information, see Polly: Metering enrichment .

Add resilience enrichment


In addition to registering a resilience pipeline, you can also register resilience
enrichment. To add enrichment, call the AddResilienceEnricher(IServiceCollection)
extensions method on the IServiceCollection instance.

C#

services.AddResilienceEnricher();

By calling the AddResilienceEnricher extension method, you're adding dimensions on


top of the default ones that are built into the underlying Polly library. The following
enrichment dimensions are added:

Exception enrichment based on the IExceptionSummarizer, which provides a


mechanism to summarize exceptions for use in telemetry. For more information,
see Exception summarization.
Request metadata enrichment based on RequestMetadata, which holds the request
metadata for telemetry. For more information, see Polly: Telemetry metrics .

Use resilience pipeline


To use a configured resilience pipeline, you must get the pipeline from a
ResiliencePipelineProvider<TKey> . When you added the pipeline earlier, the key was of

type string , so you must get the pipeline from the


ResiliencePipelineProvider<string> .

C#

using ServiceProvider provider = services.BuildServiceProvider();

ResiliencePipelineProvider<string> pipelineProvider =
provider.GetRequiredService<ResiliencePipelineProvider<string>>();

ResiliencePipeline pipeline = pipelineProvider.GetPipeline(key);

The preceding code:

Builds a ServiceProvider from the ServiceCollection instance.


Gets the ResiliencePipelineProvider<string> from the service provider.
Retrieves the ResiliencePipeline from the ResiliencePipelineProvider<string> .

Execute resilience pipeline


To use the resilience pipeline, call any of the available Execute* methods on the
ResiliencePipeline instance. For example, consider an example call to ExecuteAsync

method:

C#

await pipeline.ExecuteAsync(static cancellationToken =>


{
// Code that could potentially fail.

return ValueTask.CompletedTask;
});

The preceding code executes the delegate within the ExecuteAsync method. When there
are failures, the configured strategies are executed. For example, if the RetryStrategy is
configured to retry three times, the delegate is executed four times (one initial attempt
plus three retry attempts) before the failure is propagated.

Next steps
Build resilient HTTP apps: Key development patterns Consider the

challenges of idempotent handling of retried calls

6 Collaborate with us on
GitHub .NET feedback
.NET is an open source project.
The source for this content can
Select a link to provide feedback:
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Programação de rede no .NET
Artigo • 10/05/2023

O .NET fornece uma implementação dos serviços de Internet em camadas, extensível e


gerenciada que pode ser rápida e facilmente integrada aos aplicativos. Seus aplicativos
de rede podem compilar em protocolos conectáveis para usufruir automaticamente de
novos protocolos da Internet ou podem usar uma implementação gerenciada da
interface de soquete da plataforma cruzada para trabalhar com a rede a nível de
soquete.

Aplicativos da Internet
Os aplicativos da Internet podem ser classificados amplamente em dois tipos:
aplicativos cliente que solicitam informações e aplicativos para servidores que
respondem às solicitações de informações feitas por clientes. O aplicativo cliente-
servidor clássico de Internet é a World Wide Web, em que as pessoas usam navegadores
para acessar documentos e outros dados armazenados em servidores Web em todo o
mundo.

Os aplicativos não se limitam a apenas uma dessas funções. Por exemplo, o servidor de
aplicativos familiar de camada intermediária responde a solicitações de clientes,
solicitando dados em outro servidor, caso em que ele está atuando como um servidor e
também como um cliente.

O aplicativo do cliente faz uma solicitação identificando o recurso da Internet solicitado


e o protocolo de comunicação a usar para a solicitação e resposta. Se necessário, o
cliente também fornece quaisquer dados adicionais necessários para concluir a
solicitação, assim como autenticação ou local de informações de proxy (nome de
usuário, senha e assim por diante). Depois que a solicitação é formada, ela pode ser
enviada ao servidor.

Identificando recursos
O .NET usa URI (Uniform Resource Identifier) para identificar o protocolo de
comunicação e recursos de Internet solicitado. O URI consiste em pelo menos três,
provavelmente quatro fragmentos: o identificador do esquema, que identifica o
protocolo de comunicação de solicitação e resposta; o identificador do servidor, que
consiste em um nome do host do sistema de nome de domínio (DNS) ou um endereço
TCP que identifica exclusivamente o servidor na Internet; o identificador do caminho,
que localiza as informações solicitadas no servidor e, por fim, uma cadeia de caracteres
de consulta opcional, que passa informações do cliente para o servidor.

O tipo System.Uri é usado como uma representação de um URI (Uniform Resource


Identifier) e fácil acesso às partes do URI. Para criar uma instância Uri , você pode passar
uma cadeia de caracteres:

C#

const string uriString =


"https://learn.microsoft.com/en-us/dotnet/path?key=value#bookmark";

Uri canonicalUri = new(uriString);


Console.WriteLine(canonicalUri.Host);
Console.WriteLine(canonicalUri.PathAndQuery);
Console.WriteLine(canonicalUri.Fragment);
// Sample output:
// learn.microsoft.com
// /en-us/dotnet/path?key=value
// #bookmark

A classe Uri executa automaticamente a validação e a canonicalização por RCF 3986 .


Essas regras de validação e canonicalização são usadas para garantir que um URI seja
bem formado e que o URI esteja em uma forma canônica.

Confira também
Opções de configuração de tempo de execução para sistema de rede
Suporte a HTTP no .NET
Soquetes no .NET
TCP no .NET
Tutorial: Fazer solicitações HTTP em um aplicativo de console .NET usando C #
Telemetria de rede no .NET
Melhorias na rede do .NET

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
 Provide product feedback
more information, see our
contributor guide.
Disponibilidade da rede
Artigo • 10/05/2023

O namespace System.Net.NetworkInformation permite que você colete informações


sobre eventos, alterações, estatísticas e propriedades de rede. Neste artigo, você
aprenderá a usar a classe System.Net.NetworkInformation.NetworkChange para
determinar se o endereço de rede ou a disponibilidade foram alterados. Além disso,
você verá informações sobre as estatísticas de rede e as propriedades com base na
interface ou no protocolo. Por fim, você usará a classe
System.Net.NetworkInformation.Ping para determinar se um host remoto pode ser
acessado.

Eventos de alteração de rede


A classe System.Net.NetworkInformation.NetworkChange permite que você determine
se o endereço de rede ou a disponibilidade foi alterada. Para usar essa classe, crie um
manipulador de eventos para processar a alteração e associe-o a um
NetworkAddressChangedEventHandler ou NetworkAvailabilityChangedEventHandler.

C#

NetworkChange.NetworkAvailabilityChanged += OnNetworkAvailabilityChanged;

static void OnNetworkAvailabilityChanged(


object? sender, NetworkAvailabilityEventArgs networkAvailability) =>
Console.WriteLine($"Network is available:
{networkAvailability.IsAvailable}");

Console.WriteLine(
"Listening changes in network availability. Press any key to
continue.");
Console.ReadLine();

NetworkChange.NetworkAvailabilityChanged -= OnNetworkAvailabilityChanged;

O código anterior do C#:

Registra um manipulador de eventos para o evento


NetworkChange.NetworkAvailabilityChanged.
O manipulador de eventos apenas grava o status de disponibilidade no console.
Uma mensagem é gravada no console informando ao usuário que o código está
escutando alterações na disponibilidade da rede e aguarda um pressionamento de
tecla para sair.
Cancela o registro do manipulador de eventos.

C#

NetworkChange.NetworkAddressChanged += OnNetworkAddressChanged;

static void OnNetworkAddressChanged(


object? sender, EventArgs args)
{
foreach ((string name, OperationalStatus status) in
NetworkInterface.GetAllNetworkInterfaces()
.Select(networkInterface =>
(networkInterface.Name,
networkInterface.OperationalStatus)))
{
Console.WriteLine(
$"{name} is {status}");
}
}

Console.WriteLine(
"Listening for address changes. Press any key to continue.");
Console.ReadLine();

NetworkChange.NetworkAddressChanged -= OnNetworkAddressChanged;

O código anterior do C#:

Registra um manipulador de eventos para o evento


NetworkChange.NetworkAddressChanged.
O manipulador de eventos itera em NetworkInterface.GetAllNetworkInterfaces(),
gravando seu nome e o status operacional no console.
Uma mensagem é gravada no console informando ao usuário que o código está
escutando alterações na disponibilidade da rede e aguarda um pressionamento de
tecla para sair.
Cancela o registro do manipulador de eventos.

Propriedades e estatísticas de rede


Você pode coletar estatísticas de rede e propriedades por protocolo ou por interface. As
classes NetworkInterface, NetworkInterfaceType e PhysicalAddress fornecem
informações sobre um adaptador de rede específico, enquanto as classes
IPInterfaceProperties, IPGlobalProperties, IPGlobalStatistics, TcpStatistics e UdpStatistics
fornecem informações sobre os pacotes de camada 3 e de camada 4.

C#
ShowStatistics(NetworkInterfaceComponent.IPv4);
ShowStatistics(NetworkInterfaceComponent.IPv6);

static void ShowStatistics(NetworkInterfaceComponent version)


{
var properties = IPGlobalProperties.GetIPGlobalProperties();
var stats = version switch
{
NetworkInterfaceComponent.IPv4 => properties.GetTcpIPv4Statistics(),
_ => properties.GetTcpIPv6Statistics()
};

Console.WriteLine($"TCP/{version} Statistics");
Console.WriteLine($" Minimum Transmission Timeout :
{stats.MinimumTransmissionTimeout:#,#}");
Console.WriteLine($" Maximum Transmission Timeout :
{stats.MaximumTransmissionTimeout:#,#}");
Console.WriteLine(" Connection Data");
Console.WriteLine($" Current :
{stats.CurrentConnections:#,#}");
Console.WriteLine($" Cumulative :
{stats.CumulativeConnections:#,#}");
Console.WriteLine($" Initiated :
{stats.ConnectionsInitiated:#,#}");
Console.WriteLine($" Accepted :
{stats.ConnectionsAccepted:#,#}");
Console.WriteLine($" Failed Attempts :
{stats.FailedConnectionAttempts:#,#}");
Console.WriteLine($" Reset :
{stats.ResetConnections:#,#}");
Console.WriteLine(" Segment Data");
Console.WriteLine($" Received :
{stats.SegmentsReceived:#,#}");
Console.WriteLine($" Sent :
{stats.SegmentsSent:#,#}");
Console.WriteLine($" Retransmitted :
{stats.SegmentsResent:#,#}");
Console.WriteLine();
}

O código anterior do C#:

Chama um método ShowStatistics personalizado para exibir as estatísticas de


cada protocolo.
O método ShowStatistics chama IPGlobalProperties.GetIPGlobalProperties() e,
dependendo do NetworkInterfaceComponent determinado, chama
IPGlobalProperties.GetIPv4GlobalStatistics() ou
IPGlobalProperties.GetIPv6GlobalStatistics().
Os TcpStatistics são gravados no console.
Determinar se um host remoto é alcançável
Você pode usar a classe Ping para determinar se um host remoto está funcionando, se
está na rede e se pode ser acessado.

C#

using Ping ping = new();

string hostName = "stackoverflow.com";


PingReply reply = await ping.SendPingAsync(hostName);
Console.WriteLine($"Ping status for ({hostName}): {reply.Status}");
if (reply is { Status: IPStatus.Success })
{
Console.WriteLine($"Address: {reply.Address}");
Console.WriteLine($"Roundtrip time: {reply.RoundtripTime}");
Console.WriteLine($"Time to live: {reply.Options?.Ttl}");
Console.WriteLine();
}

O código anterior do C#:

Instancie um objeto Ping.


Chama Ping.SendPingAsync(String) com o parâmetro de hostname
"stackoverflow.com" .

O status do ping é gravado no console.

Confira também
Programação de rede no .NET
NetworkInterface

6 Collaborate with us on
GitHub .NET feedback
The source for this content can The .NET documentation is open
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Visão geral do Protocolo de Internet
versão 6(IPv6)
Artigo • 10/05/2023

O Protocolo de Internet versão 6 (IPv6) é um novo pacote de protocolos padrão para a


camada de rede da Internet. O IPv6 foi projetado para resolver muitos dos problemas
da versão atual do pacote de Protocolos de Internet (conhecido como IPv4)
relacionados ao o esgotamento de endereços, a segurança, a configuração automática,
a extensibilidade e outros. O IPv6 expande os recursos da Internet para habilitar novos
tipos de aplicativos, inclusive aplicativos móveis e de ponto a ponto. Estes são os
principais problemas do protocolo IPv4 atual:

Rápido esgotamento do espaço de endereço.

Isso levou ao uso de conversores de endereço de rede (NATs) que mapeiam vários
endereços particulares para um único endereço IP público. Os principais
problemas criados por esse mecanismo são a sobrecarga de processamento e a
falta de conectividade de ponta a ponta.

Falta de suporte a hierarquia.

Por causa de sua organização de classe predefinida inerente, IPv4 não tem um
verdadeiro suporte hierárquico. É impossível estruturar os endereços IP de uma
forma que realmente mapeie a topologia de rede. Essa falha de design crucial cria
a necessidade de grandes tabelas de roteamento para entregar pacotes IPv4 em
qualquer local na Internet.

Configuração de rede complexa.

Com o IPv4, os endereços devem ser atribuídos estaticamente ou usando um


protocolo de configuração, como o DHCP. Em uma situação ideal, hosts não
precisariam depender da administração de uma infraestrutura DHCP. Em vez disso,
eles poderiam configurar a si mesmos com base no segmento de rede no qual
estivessem localizados.

Falta de autenticação interna e de confidencialidade.

O IPv4 não requer suporte para nenhum outro mecanismo que fornece
autenticação ou criptografia dos dados transmitidos. Isso muda com o IPv6. O
protocolo IPsec é um requisito de suporte a IPv6.

Um novo pacote de protocolos deve atender aos seguintes requisitos básicos:


Roteamento em larga escala e endereçamento com pouca sobrecarga.
Configuração automática para situações de conexão diversas.
Autenticação interna e confidencialidade.

Endereçamento de IPv6
Com IPv6, os endereços têm 128 bits de comprimento. Um motivo para um espaço de
endereço tão grande é subdividir os endereços disponíveis em uma hierarquia de
domínios de roteamento que reflitam a topologia da Internet. Outro motivo é mapear
os endereços de adaptadores de rede (ou interfaces) que conectam dispositivos à rede.
O IPv6 tem uma capacidade inerente de resolver endereços no nível mais baixo deles,
que é o nível de adaptador de rede e também tem capacidades de configuração
automática.

Representação de texto
A seguir estão as três formas convencionais usadas para representar os endereços IPv6
como cadeias de caracteres de texto:

Forma hexadecimal com dois pontos:

Essa é o opção preferencial n:n:n:n:n:n:n:n . Cada n representa o valor


hexadecimal de um dos oito elementos de 16 bits do endereço. Por exemplo:
3FFE:FFFF:7654:FEDA:1245:BA98:3210:4562 .

Forma compactada:

Devido ao tamanho de endereço, é comum ter endereços que contêm uma cadeia
de caracteres longa de zeros. Para simplificar a gravar esses endereços, use a
forma compactada, em que uma única sequência contígua de 0 blocos é
representada por um símbolo de dois pontos duplos ( :: ). Este símbolo pode
aparecer apenas uma vez em um endereço. Por exemplo, o endereço multicast
FFED:0:0:0:0:BA98:3210:4562 torna-se FFED::BA98:3210:4562 no formato

compactado. O endereço unicast 3FFE:FFFF:0:0:8:800:20C4:0 em formato


compactado é 3FFE:FFFF::8:800:20C4:0 . O endereço de loopback
0:0:0:0:0:0:0:1 na forma compactada é ::1 . O endereço não especificado
0:0:0:0:0:0:0:0 em formato compactado é :: .

Forma mista:

Esse formato combina os endereços IPv4 e IPv6. Nesse caso, o formato de


endereço é n:n:n:n:n:n:d.d.d.d , em que cada n representa os valores
hexadecimais dos seis elementos do endereço de 16 bits superiores de IPv6 e cada
d representa o valor decimal de um endereço IPv4.

Tipos de endereço
Os bits à esquerda do endereço definem o tipo específico de endereço IPv6. O campo
de comprimento variável que contém esses bits à esquerda é chamado de um FP
(prefixo de formato).

Um endereço unicast IPv6 é dividido em duas partes. A primeira parte contém o prefixo
de endereço e a segunda parte contém o identificador de interface. Uma maneira
concisa de expressar uma combinação de endereço IPv6/prefixo é a seguinte: endereço
ipv6/comprimento do prefixo.

A seguir, um exemplo de um endereço com um prefixo de 64 bits.

3FFE:FFFF:0:CD30:0:0:0:0/64 .

O prefixo neste exemplo é 3FFE:FFFF:0:CD30 . O endereço também pode ser gravado em


um formato compactado, como 3FFE:FFFF:0:CD30::/64 .

O IPv6 define os seguintes tipos de endereço:

Endereço Unicast:

Um identificador para uma única interface. Um pacote enviado para esse endereço
é entregue para a interface identificada. Os endereços unicast são diferenciados
dos endereços multicast pelo valor do octeto superior. O octeto superior dos
endereços multicast tem o valor hexadecimal FF. Qualquer outro valor para esse
octeto identifica um endereço unicast. Estes são os diferentes tipos de endereço
unicast:

Endereços de link local:

Esses endereços são usados em um único link e têm o seguinte formato:


FE80::*InterfaceID* . Endereços de conexões locais são usados entre os nós em

um link para a configuração automática de endereços, descoberta de vizinhos


ou ainda quando nenhum dos roteadores está presente. Um endereço de link
local é usado principalmente na inicialização e quando o sistema ainda não
adquiriu endereços de escopo mais amplo.

Endereços de site-local:
Esses endereços são usados em um único site e têm o seguinte formato:
FEC0::*SubnetID*:*InterfaceID* . Os endereços de sites locais são usados para

endereçamento dentro de um site sem a necessidade de um prefixo global.


Endereços unicast IPv6 globais:

Esses endereços podem ser usados na Internet e têm o seguinte formato:


*GlobalRoutingPrefix*::*SubnetID*:*InterfaceID* .

Endereço Multicast:

Um identificador para um conjunto de interfaces (normalmente pertencentes a nós


diferentes). Um pacote enviado para esse endereço será enviado para todas as
interfaces identificadas pelo endereço. Os tipos de endereço multicast substituem
os endereços difundidos por IPv4.

Endereço Anycast:

Um identificador para um conjunto de interfaces (normalmente pertencentes a nós


diferentes). Um pacote enviado para esse endereço será enviado para apenas uma
interface identificada pelo endereço. Essa é a interface mais próxima conforme
identificado pela métrica de roteamento. Endereços anycast são obtidos do espaço
de endereço unicast e não é possível diferenciá-los sintaticamente. A interface
endereçada faz a distinção entre endereços unicast e anycast como uma função de
sua configuração.

Em geral, um nó sempre tem um endereço de link local. Ele pode ter um endereço de
site local e um ou mais endereços globais.

Roteamento IPv6
Um mecanismo de roteamento flexível é uma vantagem do IPv6. Devido à maneira que
as IDs de rede IPv4 foram e são alocadas, grandes tabelas de roteamento precisam ser
mantidas pelos roteadores que estão na estrutura básica da Internet. Esses roteadores
devem saber todas as rotas para encaminhar pacotes que são potencialmente
direcionados para qualquer nó na Internet. Com sua capacidade de agregar endereços,
o IPv6 permite endereçamento flexível e reduz consideravelmente o tamanho das
tabelas de roteamento. Nessa nova arquitetura de endereçamento, os roteadores
intermediários devem rastrear apenas a parte local de sua rede para encaminhar as
mensagens corretamente.

Descoberta de vizinhos
Estes são alguns dos recursos fornecidos pela descoberta de vizinhos:

Descoberta de roteador: Isso permite que os hosts identifiquem os roteadores


locais.
Resolução de endereço: Isso permite que os nós resolvam um endereço de
camada de link para um endereço de próximo salto correspondente (uma
substituição para o Protocolo de Resolução de Endereço [ARP]).
Configuração automática de endereço: Isso permite que os hosts configurem
automaticamente os endereços de site-local e globais.

A descoberta de vizinhos usa mensagens de Protocolo de Mensagem de Controle de


Internet para IPv6 (ICMPv6) que incluem:

Anúncio do roteador: Enviado por um roteador pseudoperiodicamente ou em


resposta a uma solicitação de roteador. Os roteadores IPv6 usam anúncios de
roteador para anunciar a própria disponibilidade, prefixos de endereço e outros
parâmetros.
Solicitação de roteador: Enviada por um host para solicitar que os roteadores no
link enviem um anúncio de roteador imediatamente.
Solicitação de vizinho: Enviada por nós para resolução de endereço, para detecção
de endereço duplicado ou para verificar se um vizinho ainda pode ser alcançado.
Anúncio de vizinho: Enviado por nós para responder a uma solicitação de vizinho
ou para notificar vizinhos de uma alteração no endereço de camada de link.
Redirecionamento: Enviado por roteadores a fim de indicar um melhor endereço
de próximo salto para um destino específico para um nó de envio.

Configuração automática de IPv6


Uma meta importante para IPv6 é dar suporte a Plug and Play de nó. Ou seja, deve ser
possível conectar um nó a uma rede IPv6 de modo que ele seja configurado
automaticamente sem intervenção humana.

Tipos de configuração automática


O IPv6 dá suporte aos seguintes tipos de configuração automática:

Configuração automática com estado:

Esse tipo de configuração requer um certo nível de intervenção humana, porque é


necessário um protocolo DHCP para servidor IPv6 (DHCPv6) para a instalação e
administração dos nós. O servidor DHCPv6 mantém uma lista de nós para os quais
ele fornece informações de configuração. Ele também mantém informações de
estado para que o servidor saiba quanto tempo cada endereço está em uso e
quando ele pode estar disponível para reatribuição.

Configuração automática sem estado:

Esse tipo de configuração é adequado para indivíduos e pequenas empresas.


Nesse caso, cada host determina seus endereços com base no conteúdo de
anúncios de roteador recebidos. Usando o padrão IEEE EUI-64 para definir a parte
de ID de rede do endereço, é razoável pressupor a exclusividade do endereço de
host no link.

Independentemente de como o endereço é determinado, o nó deve verificar se seu


endereço potencial é exclusivo para o link local. Isso é feito enviando uma mensagem
de solicitação de vizinho para o endereço potencial. Se o nó receber alguma resposta,
ele saberá que o endereço já está em uso e deverá determinar outro endereço.

Mobilidade IPv6
A proliferação de dispositivos móveis introduziu um novo requisito: um dispositivo deve
ser capaz de alterar os locais na Internet IPv6 arbitrariamente e ainda manter as
conexões existentes. Para fornecer essa funcionalidade, um endereço residencial é
atribuído a um nó móvel, endereço no qual ele sempre pode ser alcançado. Quando o
nó móvel está no endereço residencial, ele se conecta ao link residencial e usa seu
endereço residencial. Quando o nó móvel está longe do endereço residencial, um
agente residencial, que geralmente é um roteador, retransmite mensagens entre o nó
móvel e os nós com os quais ele está se comunicando.

Desabilitar ou habilitar IPv6


Para usar o protocolo IPv6, verifique se você está executando uma versão do sistema
operacional que dá suporte ao IPv6 e se o sistema operacional e as classes de rede
estão configuradas corretamente.

Etapas de configuração
A tabela a seguir lista várias configurações

IPv6 do sistema IPv6 do código Descrição


operacional habilitado?
habilitado?

❌ Não ❌ Não Pode analisar endereços IPv6.


IPv6 do sistema IPv6 do código Descrição
operacional habilitado?
habilitado?

❌ Não ✔️Sim Pode analisar endereços IPv6.

✔️Sim ❌ Não Pode analisar endereços IPv6 e resolver endereços


IPv6 usando métodos de resolução de nomes não
marcados como obsoletos.

✔️Sim ✔️Sim Pode analisar e resolver endereços IPv6 usando


todos os métodos, incluindo aqueles marcados
como obsoletos.

O IPv6 está habilitado como padrão. Para configurar essa opção em uma variável de
ambiente, use a variável de ambiente DOTNET_SYSTEM_NET_DISABLEIPV6 . Para obter mais
informações, consulte Variáveis de ambiente do .NET:
DOTNET_SYSTEM_NET_DISABLEIPV6.

Confira também
Rede no .NET
Soquetes no .NET
System.AppContext

6 Collaborate with us on
GitHub .NET feedback
The .NET documentation is open
The source for this content can
source. Provide feedback here.
be found on GitHub, where you
can also create and review
 Open a documentation issue
issues and pull requests. For
more information, see our
 Provide product feedback
contributor guide.
Descoberta de serviço no .NET
Artigo • 11/12/2023

Neste artigo, você aprenderá a usar a biblioteca


Microsoft.Extensions.ServiceDiscovery . A descoberta de serviço é uma forma de os

desenvolvedores usarem nomes lógicos em vez de endereços físicos (endereço IP e


porta) para se referirem a serviços externos.

Começar agora
Para começar a usar a descoberta de serviço no .NET, instale o pacote NuGet
Microsoft.Extensions.ServiceDiscovery .

CLI do .NET

CLI do .NET

dotnet add package Microsoft.Extensions.ServiceDiscovery --prerelease

Para obter mais informações, consulte dotnet add package ou Gerenciar dependências
de pacotes em aplicativos .NET.

Exemplo de uso
No arquivo Program.cs do projeto, chame o método de extensão AddServiceDiscovery
para adicionar a descoberta de serviço ao host, configurando resolvedores de ponto de
extremidade de serviço padrão:

C#

builder.Services.AddServiceDiscovery();

Adicione a descoberta de serviço a um IHttpClientBuilder individual chamando o


método de extensão UseServiceDiscovery :

C#

builder.Services.AddHttpClient<CatalogServiceClient>(static client =>


{
client.BaseAddress = new("http://catalog");
})
.UseServiceDiscovery();

Como alternativa, você pode adicionar a descoberta de serviço a todas as instâncias de


HttpClient por padrão:

C#

builder.Services.ConfigureHttpClientDefaults(static http =>


{
// Turn on service discovery by default
http.UseServiceDiscovery();
});

Resolver pontos de extremidade de serviço por


meio da configuração
O método de extensão AddServiceDiscovery adiciona um resolvedor de ponto de
extremidade baseado em configuração por padrão. Esse resolvedor lê os pontos de
extremidade do sistema de configuração do .NET. A biblioteca dá suporte à
configuração por meio do appsettings.json, de variáveis de ambiente ou de qualquer
outra fonte de IConfiguration.

Veja um exemplo que demonstra como configurar pontos de extremidade para o


serviço chamado catalog por meio de appsettings.json :

JSON

{
"Services": {
"catalog": [
"localhost:8080",
"10.46.24.90:80",
]
}
}

O exemplo anterior adiciona dois pontos de extremidade ao serviço chamado catalog:


localhost:8080 e "10.46.24.90:80" . Cada vez que o catalog é resolvido, um desses

pontos de extremidade é selecionado.

Se a descoberta de serviço foi adicionada ao host por meio do método de extensão


AddServiceDiscoveryCore em IServiceCollection, o resolvedor de ponto de extremidade
baseado em configuração pode ser adicionado chamando o método de extensão
AddConfigurationServiceEndPointResolver em IServiceCollection .

Configuração
O resolvedor de configuração é configurado por meio da classe
ConfigurationServiceEndPointResolverOptions, que oferece estas opções de
configuração:

SectionName: o nome da seção de configuração que contém os pontos de


extremidade de serviço. O padrão é "Services" .

ApplyHostNameMetadata: um representante usado para determinar se os


metadados do nome do host devem ser aplicados aos pontos de extremidade
resolvidos. Usa como padrão uma função que retorna false .

Para configurar essas opções, use o método de extensão Configure em


IServiceCollection na classe Startup ou no arquivo Program do aplicativo:

C#

var builder = WebApplication.CreateBuilder(args);

builder.Services.Configure<ConfigurationServiceEndPointResolverOptions>(
static options =>
{
options.SectionName = "MyServiceEndpoints";

// Configure the logic for applying host name metadata


options.ApplyHostNameMetadata = static endpoint =>
{
// Your custom logic here. For example:
return endpoint.EndPoint is DnsEndPoint dnsEp
&& dnsEp.Host.StartsWith("internal");
};
});

O exemplo anterior demonstra a definição de um nome de seção personalizado para


seus pontos de extremidade de serviço e o fornecimento de uma lógica condicional
personalizada para aplicar os metadados do nome do host.

Resolver os pontos de extremidade de serviço


usando a descoberta de serviço fornecida pela
plataforma
Algumas plataformas, como os Aplicativos de Contêiner do Azure e o Kubernetes
(quando configuradas de acordo), oferecem funcionalidades de descoberta de serviço
sem a necessidade de uma biblioteca de clientes de descoberta de serviço. Nos casos
em que um aplicativo é implantado nesses ambientes, o uso da funcionalidade interna
da plataforma pode ser vantajoso. O resolvedor de passagem foi projetado para facilitar
esse cenário. Ele permite a utilização de resolvedores alternativos, como configuração,
em diferentes ambientes, como um computador do desenvolvedor. É importante
ressaltar que essa flexibilidade é alcançada sem a necessidade de nenhuma modificação
de código ou da implementação de proteções condicionais.

O resolvedor de passagem não executa nenhuma resolução externa e, em vez disso,


resolve os pontos de extremidade retornando o nome do serviço de entrada
representado como um DnsEndPoint.

O provedor de passagem é configurado por padrão ao adicionar a descoberta de


serviço por meio do método de extensão AddServiceDiscovery .

Se a descoberta de serviço foi adicionada ao host por meio do método de extensão


AddServiceDiscoveryCore em IServiceCollection , o provedor de passagem pode ser

adicionado chamando o método de extensão AddPassThroughServiceEndPointResolver


em IServiceCollection .

Balanceamento de carga com seletores de


ponto de extremidade
Cada vez que um ponto de extremidade é resolvido por meio do pipeline HttpClient ,
um ponto de extremidade individual é selecionado do conjunto de todos os pontos de
extremidade conhecidos do serviço solicitado. Se vários pontos de extremidade
estiverem disponíveis, o ideal será equilibrar o tráfego em todos esses pontos de
extremidade. Para fazer isso, um seletor de ponto de extremidade personalizável pode
ser usado. Por padrão, os pontos de extremidade são selecionados na ordem round
robin. Para usar um seletor de ponto de extremidade diferente, forneça uma instância
de IServiceEndPointSelector à chamada de método UseServiceDiscovery. Por exemplo,
para selecionar um ponto de extremidade aleatório do conjunto de pontos de
extremidade resolvidos, especifique RandomServiceEndPointSelectorProvider.Instance
como o seletor de ponto de extremidade:

C#
builder.Services.AddHttpClient<CatalogServiceClient>(
static client => client.BaseAddress = new("http://catalog")
)
.UseServiceDiscovery(RandomServiceEndPointSelectorProvider.Instance);

O pacote Microsoft.Extensions.ServiceDiscovery inclui os seguintes provedores de


seletor de ponto de extremidade:

PickFirstServiceEndPointSelectorProvider.Instance: escolher o primeiro, que sempre


seleciona o primeiro ponto de extremidade.
RoundRobinServiceEndPointSelectorProvider.Instance: round robin, que percorre
os pontos de extremidade.
RandomServiceEndPointSelectorProvider.Instance: aleatório, que seleciona pontos
de extremidade aleatoriamente.
PowerOfTwoChoicesServiceEndPointSelectorProvider.Instance: poder de duas
escolhas, que tenta escolher o ponto de extremidade menos usado com base no
algoritmo Power of Two Choices para o balanceamento de carga distribuído,
degradando-se para selecionar aleatoriamente um ponto de extremidade quando
um dos pontos de extremidade fornecidos não tem o recurso
IEndPointLoadFeature.

Os seletores de ponto de extremidade são criados por meio de uma instância de


IServiceEndPointSelectorProvider, como os provedores já listados. O método
CreateSelector() do provedor é chamado para criar um seletor, que é uma instância de
IServiceEndPointSelector. A instância IServiceEndPointSelector recebe o conjunto de
pontos de extremidade conhecidos quando eles são resolvidos, por meio do método
IServiceEndPointSelector.SetEndPoints. Para escolher um ponto de extremidade da
coleção, o método IServiceEndPointSelector.GetEndPoint é chamado, retornando um
ServiceEndPoint individual. O valor context transmitido a GetEndPoint é usado para

fornecer um contexto extra que pode ser útil para o seletor. Por exemplo, no caso de
HttpClient , o valor HttpRequestMessage é transmitido. Nenhuma das implementações

fornecidas de IServiceEndPointSelector inspeciona o contexto, e ele pode ser ignorado,


a menos que você esteja usando um seletor, que faz uso dele.

Ordem de resolução
Quando os pontos de extremidade de serviço estão sendo resolvidos, cada resolvedor
registrado é chamado na ordem de registro e tem a oportunidade de modificar a
coleção de ServiceEndPoint s que são retornados ao chamador. Os provedores incluídos
na série de pacotes de Microsoft.Extensions.ServiceDiscovery ignoram a resolução se
há pontos de extremidade existentes na coleção quando eles são chamados. Por
exemplo, considere um caso em que os seguintes provedores estão registrados:
Configuração, DNS SRV e Passagem. Quando a resolução ocorre, os provedores são
chamados na ordem. Se os provedores de Configuração não descobrirem nenhum
ponto de extremidade, o provedor de SRV DNS executará a resolução e poderá
adicionar um ou mais pontos de extremidade. Se o provedor SRV DNS adicionar um
ponto de extremidade à coleção, o provedor de Passagem vai ignorar a resolução e será
retornado imediatamente.

Confira também
Descoberta de serviço no .NET Aspire

6 Colaborar conosco no Comentários do .NET


GitHub O .NET é um projeto código aberto.
A fonte deste conteúdo pode Selecione um link para fornecer
ser encontrada no GitHub, onde comentários:
você também pode criar e
revisar problemas e solicitações  Abrir um problema de
de pull. Para obter mais documentação
informações, confira o nosso
guia para colaboradores.  Fornecer comentários sobre o
produto
Suporte a HTTP no .NET
Artigo • 19/05/2023

O HTTP (ou Protocolo de Transferência de Hipertexto) é um protocolo para solicitar


recursos de um servidor Web. A classe System.Net.Http.HttpClient expõe a capacidade
de enviar solicitações HTTP e receber respostas HTTP de um recurso identificado por
um URI. Muitos tipos de recursos estão disponíveis na Web, e o HTTP define um
conjunto de métodos de solicitação para acessar esses recursos.

Métodos de solicitação HTTP


Os métodos de solicitação são diferenciados por meio de vários fatores, primeiro pelo
verbo, mas também pelas seguintes características:

Um método de solicitação será idempotente se puder ser processado com êxito


várias vezes sem alterar o resultado. Para obter mais informações, confira RFC
9110: 9.2.2. Métodos idempotentes .
Um método de solicitação pode ser armazenável em cache quando sua resposta
correspondente pode ser armazenada para reutilização. Para obter mais
informações, consulte RFC 9110: Seção 9.2.3. Métodos e cache .
Um método de solicitação será considerado um método seguro se ele não
modifica o estado de um recurso. Todos os métodos seguros também são
idempotentes, mas nem todos os métodos idempotentes são considerados seguros.
Para obter mais informações, confira RFC 9110: Seção 9.2.1. Métodos seguros .

Método HTTP É idempotente Pode ser armazenado em cache É seguro

GET ✔️Sim ✔️Sim ✔️Sim

POST ❌ Não ⚠️†Raramente ❌ Não

PUT ✔️Sim ❌ Não ❌ Não

PATCH ❌ Não ❌ Não ❌ Não

DELETE ✔️Sim ❌ Não ❌ Não

HEAD ✔️Sim ✔️Sim ✔️Sim

OPTIONS ✔️Sim ❌ Não ✔️Sim

TRACE ✔️Sim ❌ Não ✔️Sim

CONNECT ❌ Não ❌ Não ❌ Não


† O método POST só pode ser armazenado em cache quando os cabeçalhos
apropriados Cache-Control ou Expires de resposta estiverem presentes. Isso é
muito incomum na prática.

Códigos de status HTTP


O .NET fornece suporte abrangente ao protocolo HTTP, que representa a maior parte do
tráfego da Internet, com HttpClient. Para obter mais informações, confira Fazer
solicitações HTTP com a classe HttpClient. Aplicativos recebem erros de protocolo HTTP
capturando um HttpRequestException. Os códigos de status HTTP são relatados no
HttpResponseMessage com o HttpResponseMessage.StatusCode ou no
HttpRequestException com o HttpRequestException.StatusCode caso o método
chamado não retorne uma mensagem de resposta. Para obter mais informações sobre
tratamento de erros, confira Tratamento de erros HTTP e para obter mais informações
sobre códigos de status, confira RFC 9110, Semântica HTTP: Códigos de status .

Códigos de status informativos


Os códigos de status informativos refletem uma resposta provisória. A maioria das
respostas provisórias, por exemplo, HttpStatusCode.Continue, é tratada internamente
com HttpClient e nunca é exibida para o usuário.

Código de status HTTP

Você também pode gostar