Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Este artigo aborda como escrever bibliotecas para .NET usando a CLI .NET. A CLI fornece uma experiência eficiente e de baixo nível que funciona em qualquer sistema operacional suportado. Ainda podes construir bibliotecas com Visual Studio e, se essa for a tua experiência preferida consulta o guia Visual Studio.
Pré-requisitos
Precisas que o SDK .NET esteja instalado na tua máquina.
Para as secções deste documento que abordam .NET versões do Framework, precisa do .NET Framework instalado numa máquina Windows.
Além disso, se quiser dar suporte a versões antigas do .NET Framework, precisa de instalar pacotes de direcionamento ou pacotes de desenvolvedor da página de downloads do .NET Framework. Consulte esta tabela:
| Versão do framework .NET | O que fazer o download |
|---|---|
| 4.6.1 | .NET Framework 4.6.1 Pacote de Destino |
| 4.6 | Pacote de Destino do .NET Framework 4.6 |
| 4.5.2 | .NET Framework 4.5.2 Pacote de Desenvolvimento |
| 4.5.1 | .NET Framework 4.5.1 Pacote de Desenvolvedores |
| 4,5 | Kit de Desenvolvimento de Software Windows para Windows 8 |
| 4.0 | Windows SDK para Windows 7 e .NET Framework 4 |
| 2.0, 3.0 e 3.5 | .NET Framework 3.5 SP1 Runtime (ou versão Windows 8+) |
Como definir o alvo para .NET 5+ ou .NET Standard
Você controla a estrutura de destino do seu projeto adicionando-a ao seu arquivo de projeto (.csproj ou .fsproj). Para orientações sobre como escolher entre direcionar .NET 5+ ou .NET Standard, veja .NET 5+ e .NET Standard.
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>
</Project>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
Se quiser direcionar .NET Framework versões 4.0 ou inferiores, ou quiser usar uma API disponível no .NET Framework mas não no .NET Standard (por exemplo, System.Drawing), leia as secções seguintes e aprenda a fazer multitarget.
Como direcionar o .NET Framework
Nota
Estas instruções assumem que tens o .NET Framework instalado na tua máquina. Consulte os Pré-requisitos para a instalação de dependências.
Tenha em mente que algumas das versões do .NET Framework usadas aqui já não são suportadas. Consulte a FAQ .NET Política de Suporte ao Ciclo de Vida do Framework sobre versões não suportadas.
Se quiser atingir o máximo número possível de programadores e projetos, use o .NET Framework 4.0 como alvo base. Para direcionar o .NET Framework, comece por usar o Target Framework Moniker (TFM) correto que corresponda à versão do .NET Framework que pretende suportar.
| Versão do framework .NET | TFM |
|---|---|
| .NET Framework 2.0 | net20 |
| .NET Framework 3.0 | net30 |
| .NET Framework 3.5 | net35 |
| .NET Framework 4.0 | net40 |
| .NET Framework 4.5 | net45 |
| .NET Framework 4.5.1 | net451 |
| .NET Framework 4.5.2 | net452 |
| .NET Framework 4.6 | net46 |
| .NET Framework 4.6.1 | net461 |
| .NET Framework 4.6.2 | net462 |
| .NET Framework 4.7 | net47 |
| .NET Framework 4.8 | net48 |
Em seguida, insira esse TFM na TargetFramework seção do seu arquivo de projeto. Por exemplo, aqui está como escreverias uma biblioteca que visasse o .NET Framework 4.0:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net40</TargetFramework>
</PropertyGroup>
</Project>
E já está! Embora isto tenha sido compilado apenas para o .NET Framework 4, pode usar a biblioteca em versões mais recentes do .NET Framework.
Como realizar múltiplas segmentações
Nota
As instruções seguintes assumem que tem o .NET Framework instalado na sua máquina. Consulte a seção Pré-requisitos para saber quais dependências você precisa instalar e de onde baixá-las.
Pode ser necessário direcionar versões mais antigas do .NET Framework quando o seu projeto suporta tanto o .NET Framework como o .NET. Nesse cenário, se você quiser usar APIs mais recentes e construções de linguagem para os destinos mais recentes, use #if diretivas em seu código. Também pode ser necessário adicionar pacotes e dependências diferentes para cada plataforma que você está segmentando para incluir as diferentes APIs necessárias para cada caso.
Por exemplo, digamos que você tenha uma biblioteca que execute operações de rede por HTTP. Para .NET Standard e as versões 4.5 ou superiores do .NET Framework, pode usar a classe HttpClient do espaço de nomes System.Net.Http. No entanto, versões anteriores do .NET Framework não têm a classe HttpClient, por isso podes usar a classe WebClient do espaço de nomes System.Net para essas classes.
Seu arquivo de projeto pode ter esta aparência:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net40;net45</TargetFrameworks>
</PropertyGroup>
<!-- Need to conditionally bring in references for the .NET Framework 4.0 target -->
<ItemGroup Condition="'$(TargetFramework)' == 'net40'">
<Reference Include="System.Net" />
</ItemGroup>
<!-- Need to conditionally bring in references for the .NET Framework 4.5 target -->
<ItemGroup Condition="'$(TargetFramework)' == 'net45'">
<Reference Include="System.Net.Http" />
<Reference Include="System.Threading.Tasks" />
</ItemGroup>
</Project>
Você notará três grandes mudanças aqui:
- O
TargetFrameworknó foi substituído porTargetFrameworks, e três TFMs são expressos internamente. - Existe um nó
<ItemGroup>para o alvonet40que importa uma referência do .NET Framework. - Existe um nó
<ItemGroup>para o alvonet45que inclui duas referências .NET Framework.
Símbolos do pré-processador
O sistema de compilação está ciente dos seguintes símbolos de pré-processador usados nas diretivas #if
| Estruturas de destino | Símbolos | Símbolos adicionais (disponível em .NET 5+ SDKs) |
Símbolos da plataforma (disponível apenas quando você especifica um TFM específico do sistema operacional) |
|---|---|---|---|
| Framework .NET |
NETFRAMEWORK, NET481, , NET48, NET472, NET471, NET47NET462NET461NET46NET452NET451NET45NET40NET35NET20 |
NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, NET35_OR_GREATER, NET20_OR_GREATER |
|
| .NET Standard |
NETSTANDARD, NETSTANDARD2_1, , NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2NETSTANDARD1_1,NETSTANDARD1_0 |
NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATERNETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATER, NETSTANDARD1_0_OR_GREATER |
|
| .NET 5+ (e .NET Core) |
NET, NET10_0, , NET9_0, NET8_0, NET7_0, NET6_0NET5_0NETCOREAPPNETCOREAPP3_1NETCOREAPP3_0NETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1NETCOREAPP1_0 |
NET10_0_OR_GREATER, NET9_0_OR_GREATER, NET8_0_OR_GREATER, NET7_0_OR_GREATER, NET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, NETCOREAPP3_0_OR_GREATER, NETCOREAPP2_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, NETCOREAPP1_1_OR_GREATER, NETCOREAPP1_0_OR_GREATER |
ANDROID, BROWSER, IOS, MACCATALYST, MACOS, TVOS, WINDOWS[OS][version] (por exemplo IOS15_1),[OS][version]_OR_GREATER (por exemplo IOS15_1_OR_GREATER) |
Nota
- Os símbolos sem versão são definidos independentemente da versão que está a ser alvo.
- Os símbolos específicos da versão são definidos apenas para a versão que você está segmentando.
- Os
<framework>_OR_GREATERsímbolos são definidos para a versão alvo e todas as versões anteriores. Por exemplo, se estiver a direcionar para o .NET Framework 2.0, os seguintes símbolos estão definidos:NET20,NET20_OR_GREATER,NET11_OR_GREATEReNET10_OR_GREATER. - Os símbolos
NETSTANDARD<x>_<y>_OR_GREATERsão definidos apenas para alvos .NET Standard, e não para alvos que implementam .NET Standard, como .NET Core e .NET Framework. - Eles são diferentes dos identificadores de estrutura de destino (TFMs) usados pela propriedade
TargetFrameworke NuGet.
Aqui está um exemplo que faz uso da compilação condicional por destino:
using System;
using System.Text.RegularExpressions;
#if NET40
// This only compiles for the .NET Framework 4 targets
using System.Net;
#else
// This compiles for all other targets
using System.Net.Http;
using System.Threading.Tasks;
#endif
namespace MultitargetLib
{
public class Library
{
#if NET40
private readonly WebClient _client = new WebClient();
private readonly object _locker = new object();
#else
private readonly HttpClient _client = new HttpClient();
#endif
#if NET40
// .NET Framework 4.0 does not have async/await
public string GetDotNetCount()
{
string url = "https://www.dotnetfoundation.org/";
var uri = new Uri(url);
string result = "";
// Lock here to provide thread-safety.
lock(_locker)
{
result = _client.DownloadString(uri);
}
int dotNetCount = Regex.Matches(result, ".NET").Count;
return $"Dotnet Foundation mentions .NET {dotNetCount} times!";
}
#else
// .NET Framework 4.5+ can use async/await!
public async Task<string> GetDotNetCountAsync()
{
string url = "https://www.dotnetfoundation.org/";
// HttpClient is thread-safe, so no need to explicitly lock here
var result = await _client.GetStringAsync(url);
int dotNetCount = Regex.Matches(result, ".NET").Count;
return $"dotnetfoundation.org mentions .NET {dotNetCount} times in its HTML!";
}
#endif
}
}
Se tu compilares este projeto com dotnet build, notarás três diretórios na pasta bin/:
net40/
net45/
netstandard2.0/
Cada um deles contém os .dll arquivos para cada destino.
Como testar bibliotecas em .NET
É importante poder testar em todas as plataformas. Você pode usar xUnit ou MSTest fora da caixa. Ambos são perfeitamente adequados para testes unitários da tua biblioteca em .NET. A forma como você configura sua solução com projetos de teste dependerá da estrutura da sua solução. O exemplo a seguir pressupõe que os diretórios de teste e de origem vivam no mesmo diretório de nível superior.
Nota
Isto usa alguns comandos .NET CLI. Consulte dotnet new e dotnet sln para obter mais informações.
Configure a sua solução. Você pode fazer isso com os seguintes comandos:
mkdir SolutionWithSrcAndTest cd SolutionWithSrcAndTest dotnet new sln dotnet new classlib -o MyProject dotnet new xunit -o MyProject.Test dotnet sln add MyProject/MyProject.csproj dotnet sln add MyProject.Test/MyProject.Test.csprojIsso criará projetos e os conectará em uma solução. Seu diretório para
SolutionWithSrcAndTestdeve ter esta aparência:/SolutionWithSrcAndTest |__SolutionWithSrcAndTest.sln |__MyProject/ |__MyProject.Test/Navegue até o diretório do projeto de teste e adicione uma referência a
MyProject.TestdeMyProject.cd MyProject.Test dotnet reference add ../MyProject/MyProject.csprojRestaure pacotes e construa projetos:
dotnet restore dotnet buildVerifique se o xUnit é executado executando o
dotnet testcomando. Se você optar por usar o MSTest, o executor do console MSTest deverá ser executado.
E já está! Agora você pode testar sua biblioteca em todas as plataformas usando ferramentas de linha de comando. Para continuar a testar agora que tem tudo configurado, testar a sua biblioteca é muito simples:
- Faça alterações na sua biblioteca.
- Execute testes a partir da linha de comando, no diretório de teste, com
dotnet testo comando.
Seu código será reconstruído automaticamente quando você invocar dotnet test o comando.
Como usar vários projetos
Uma necessidade comum de bibliotecas maiores é colocar a funcionalidade em diferentes projetos.
Imagine que se pretende construir uma biblioteca que possa ser consumida de forma idiomática em C# e F#. Isso significaria que os consumidores da sua biblioteca a utilizam de maneiras naturais para C# ou F#. Por exemplo, em C# você pode consumir a biblioteca assim:
using AwesomeLibrary.CSharp;
public Task DoThings(Data data)
{
var convertResult = await AwesomeLibrary.ConvertAsync(data);
var result = AwesomeLibrary.Process(convertResult);
// do something with result
}
Em F#, pode ter esta aparência:
open AwesomeLibrary.FSharp
let doWork data = async {
let! result = AwesomeLibrary.AsyncConvert data // Uses an F# async function rather than C# async method
// do something with result
}
Cenários de consumo como esse significam que as APIs acessadas precisam ter uma estrutura diferente para C# e F#. Uma abordagem comum para alcançar isso é consolidar toda a lógica de uma biblioteca em um projeto núcleo, com projetos C# e F# a definir as camadas de API que recorrem a esse projeto núcleo. O restante da seção usará os seguintes nomes:
- AwesomeLibrary.Core - Um projeto principal que contém toda a lógica para a biblioteca
- AwesomeLibrary.CSharp - Um projeto com APIs públicas destinadas ao consumo em C#
- AwesomeLibrary.FSharp - Um projeto com APIs públicas destinadas ao consumo em F#
Você pode executar os seguintes comandos em seu terminal para produzir a mesma estrutura deste guia:
mkdir AwesomeLibrary && cd AwesomeLibrary
dotnet new sln
mkdir AwesomeLibrary.Core && cd AwesomeLibrary.Core && dotnet new classlib
cd ..
mkdir AwesomeLibrary.CSharp && cd AwesomeLibrary.CSharp && dotnet new classlib
cd ..
mkdir AwesomeLibrary.FSharp && cd AwesomeLibrary.FSharp && dotnet new classlib -lang "F#"
cd ..
dotnet sln add AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
dotnet sln add AwesomeLibrary.CSharp/AwesomeLibrary.CSharp.csproj
dotnet sln add AwesomeLibrary.FSharp/AwesomeLibrary.FSharp.fsproj
Isso adicionará os três projetos acima e um arquivo de solução que os vincula. Criar o arquivo de solução e vincular projetos permitirá que você restaure e construa projetos a partir de um nível superior.
Referência de projeto a projeto
A melhor forma de referenciar um projeto é usar a CLI .NET para adicionar uma referência de projeto. Nos diretórios de projeto AwesomeLibrary.CSharp e AwesomeLibrary.FSharp, você pode executar o seguinte comando:
dotnet reference add ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
Os arquivos de projeto para AwesomeLibrary.CSharp e AwesomeLibrary.FSharp agora farão referência a AwesomeLibrary.Core como destino ProjectReference . Você pode verificar isso inspecionando os arquivos de projeto e vendo o seguinte neles:
<ItemGroup>
<ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>
Pode adicionar esta secção manualmente a cada ficheiro de projeto se preferir não usar a CLI .NET.
Estruturação de uma solução
Outro aspeto importante das soluções multi-projeto é estabelecer uma boa estrutura geral do projeto. Você pode organizar o código como quiser, e desde que vincule cada projeto ao seu arquivo de solução com dotnet sln addo , você poderá executar dotnet restore e dotnet build no nível da solução.