Bibliotheken ontwikkelen met de .NET CLI
In dit artikel wordt beschreven hoe u bibliotheken voor .NET schrijft met behulp van de .NET CLI. De CLI biedt een efficiënte en lage ervaring die werkt in elk ondersteund besturingssysteem. U kunt nog steeds bibliotheken bouwen met Visual Studio en als dit uw voorkeurservaring is, raadpleegt u de Visual Studio-handleiding.
Vereisten
U moet de .NET SDK op uw computer hebben geïnstalleerd.
Voor de secties van dit document met .NET Framework-versies moet .NET Framework zijn geïnstalleerd op een Windows-computer.
Als u oudere .NET Framework-doelen wilt ondersteunen, moet u bovendien doelpakketten of ontwikkelaarspakketten installeren vanaf de downloadpagina van .NET Framework. Raadpleeg deze tabel:
Versie van .NET Framework | Wat u kunt downloaden |
---|---|
4.6.1 | .NET Framework 4.6.1 Targeting Pack |
4.6 | .NET Framework 4.6 Targeting Pack |
4.5.2 | .NET Framework 4.5.2 Developer Pack |
4.5.1 | .NET Framework 4.5.1 Developer Pack |
4.5 | Windows Software Development Kit voor Windows 8 |
4.0 | Windows SDK voor Windows 7 en .NET Framework 4 |
2.0, 3.0 en 3.5 | .NET Framework 3.5 SP1 Runtime (of Windows 8+ versie) |
.NET 5+ of .NET Standard targeten
U bepaalt het doelframework van uw project door het toe te voegen aan uw projectbestand (.csproj of .fsproj). Zie .NET 5+ en .NET Standard voor hulp bij het kiezen tussen doel .NET 5+ of .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>
Als u .NET Framework-versies 4.0 of lager wilt gebruiken of als u een API wilt gebruiken die beschikbaar is in .NET Framework, maar niet in .NET Standard (bijvoorbeeld System.Drawing
), leest u de volgende secties en leert u hoe u multitarget gebruikt.
.NET Framework targeten
Notitie
In deze instructies wordt ervan uitgegaan dat .NET Framework op uw computer is geïnstalleerd. Raadpleeg de vereisten voor het installeren van afhankelijkheden.
Houd er rekening mee dat sommige van de hier gebruikte .NET Framework-versies niet meer worden ondersteund. Raadpleeg de veelgestelde vragen over het levenscyclusbeleid voor .NET Framework-ondersteuning over niet-ondersteunde versies.
Als u het maximum aantal ontwikkelaars en projecten wilt bereiken, gebruikt u .NET Framework 4.0 als basislijndoel. Als u .NET Framework wilt targeten, gebruikt u eerst de juiste Target Framework Moniker (TFM) die overeenkomt met de .NET Framework-versie die u wilt ondersteunen.
Versie van .NET Framework | 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 |
Vervolgens voegt u deze TFM in de TargetFramework
sectie van het projectbestand in. U schrijft bijvoorbeeld als volgt een bibliotheek die is gericht op .NET Framework 4.0:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net40</TargetFramework>
</PropertyGroup>
</Project>
Dat is alles. Hoewel dit alleen is gecompileerd voor .NET Framework 4, kunt u de bibliotheek gebruiken voor nieuwere versies van .NET Framework.
Multitarget
Notitie
In de volgende instructies wordt ervan uitgegaan dat u .NET Framework op uw computer hebt geïnstalleerd. Raadpleeg de sectie Vereisten voor meer informatie over welke afhankelijkheden u moet installeren en waar u ze kunt downloaden.
Mogelijk moet u zich richten op oudere versies van .NET Framework wanneer uw project ondersteuning biedt voor zowel .NET Framework als .NET. Als u in dit scenario nieuwere API's en taalconstructies wilt gebruiken voor de nieuwere doelen, gebruikt #if
u instructies in uw code. Mogelijk moet u ook verschillende pakketten en afhankelijkheden toevoegen voor elk platform dat u wilt gebruiken om de verschillende API's op te nemen die nodig zijn voor elke case.
Stel dat u een bibliotheek hebt waarmee netwerkbewerkingen via HTTP worden uitgevoerd. Voor .NET Standard en .NET Framework-versies 4.5 of hoger kunt u de HttpClient
klasse uit de System.Net.Http
naamruimte gebruiken. Eerdere versies van .NET Framework hebben de HttpClient
klasse echter niet, dus u kunt de WebClient
klasse uit de System.Net
naamruimte gebruiken voor die versies.
Uw projectbestand kan er als volgt uitzien:
<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>
U ziet hier drie belangrijke wijzigingen:
- Het
TargetFramework
knooppunt is vervangen doorTargetFrameworks
en er worden drie TFM's uitgedrukt in. - Er is een
<ItemGroup>
knooppunt voor het ophalen van hetnet40
doel in één .NET Framework-verwijzing. - Er is een
<ItemGroup>
knooppunt voor denet45
doel pulling in twee .NET Framework-verwijzingen.
Preprocessorsymbolen
Het buildsysteem is op de hoogte van de volgende preprocessorsymbolen die in #if
richtlijnen worden gebruikt:
Doelframeworks | Symbolen | Aanvullende symbolen (beschikbaar in .NET 5+ SDK's) |
Platformsymbolen (alleen beschikbaar) wanneer u een besturingssysteemspecifieke TFM opgeeft) |
---|---|---|---|
.NET Framework | NETFRAMEWORK , , NET472 NET48 , NET471 , , NET47 , , NET462 NET461 , NET46 , NET451 NET452 NET45 NET40 NET35 ,NET20 |
NET48_OR_GREATER , , NET472_OR_GREATER , , , , NET461_OR_GREATER , NET46_OR_GREATER , NET452_OR_GREATER , NET45_OR_GREATER NET40_OR_GREATER NET35_OR_GREATER NET451_OR_GREATER NET462_OR_GREATER NET47_OR_GREATER NET471_OR_GREATER NET20_OR_GREATER |
|
.NET Standard | NETSTANDARD , , NETSTANDARD2_0 , NETSTANDARD1_6 NETSTANDARD2_1 , , NETSTANDARD1_5 , NETSTANDARD1_4 , , NETSTANDARD1_1 NETSTANDARD1_2 NETSTANDARD1_3 NETSTANDARD1_0 |
NETSTANDARD2_1_OR_GREATER , , NETSTANDARD1_6_OR_GREATER NETSTANDARD2_0_OR_GREATER , NETSTANDARD1_5_OR_GREATER , , NETSTANDARD1_4_OR_GREATER , NETSTANDARD1_3_OR_GREATER , NETSTANDARD1_1_OR_GREATER NETSTANDARD1_2_OR_GREATER NETSTANDARD1_0_OR_GREATER |
|
.NET 5+ (en .NET Core) | NET , , NET8_0 , , , , NETCOREAPP , NETCOREAPP3_1 , NETCOREAPP3_0 , NETCOREAPP2_1 NETCOREAPP2_0 NETCOREAPP1_1 NETCOREAPP2_2 NET5_0 NET6_0 NET7_0 NETCOREAPP1_0 |
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_0_OR_GREATER NETCOREAPP2_1_OR_GREATER NETCOREAPP1_1_OR_GREATER NETCOREAPP2_2_OR_GREATER NETCOREAPP1_0_OR_GREATER |
ANDROID , , BROWSER IOS , MACCATALYST , , MACOS , , TVOS WINDOWS [OS][version] (bijvoorbeeld IOS15_1 ),[OS][version]_OR_GREATER (bijvoorbeeld IOS15_1_OR_GREATER ) |
Notitie
- Versieloze symbolen worden gedefinieerd, ongeacht de versie die u wilt gebruiken.
- Versiespecifieke symbolen worden alleen gedefinieerd voor de versie waarop u zich richt.
- De
<framework>_OR_GREATER
symbolen worden gedefinieerd voor de versie waarop u zich richt en alle eerdere versies. Als u zich bijvoorbeeld richt op .NET Framework 2.0, worden de volgende symbolen gedefinieerd:NET20
,NET20_OR_GREATER
,NET11_OR_GREATER
enNET10_OR_GREATER
. - De
NETSTANDARD<x>_<y>_OR_GREATER
symbolen worden alleen gedefinieerd voor .NET Standard-doelen en niet voor doelen die .NET Standard implementeren, zoals .NET Core en .NET Framework. - Deze verschillen van de doelframework monikers (TFM's) die worden gebruikt door de eigenschap MSBuild
TargetFramework
en NuGet.
Hier volgt een voorbeeld van het gebruik van voorwaardelijke compilatie per doel:
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
}
}
Als u dit project bouwt met dotnet build
, ziet u drie mappen onder de bin/
map:
net40/
net45/
netstandard2.0/
Elk van deze bevat de .dll
bestanden voor elk doel.
Bibliotheken testen op .NET
Het is belangrijk om te kunnen testen op verschillende platforms. U kunt xUnit of MSTest uit de doos gebruiken. Beide zijn perfect geschikt voor het testen van uw bibliotheek op .NET. Hoe u uw oplossing instelt met testprojecten, is afhankelijk van de structuur van uw oplossing. In het volgende voorbeeld wordt ervan uitgegaan dat de test- en bronmappen zich in dezelfde map op het hoogste niveau bevinden.
Notitie
Hierbij worden enkele .NET CLI-opdrachten gebruikt. Zie dotnet new en dotnet sln voor meer informatie.
Stel uw oplossing in. U kunt dit doen met de volgende opdrachten:
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.csproj
Hiermee maakt u projecten en koppelt u ze aan elkaar in een oplossing. Uw map moet
SolutionWithSrcAndTest
er als volgt uitzien:/SolutionWithSrcAndTest |__SolutionWithSrcAndTest.sln |__MyProject/ |__MyProject.Test/
Navigeer naar de map van het testproject en voeg een verwijzing naar
MyProject.Test
van waaruitMyProject
.cd MyProject.Test dotnet add reference ../MyProject/MyProject.csproj
Pakketten herstellen en projecten bouwen:
dotnet restore dotnet build
Controleer of xUnit wordt uitgevoerd door de
dotnet test
opdracht uit te voeren. Als u ervoor kiest OM MSTest te gebruiken, moet de MSTest-consolerunner in plaats daarvan worden uitgevoerd.
Dat is alles. U kunt uw bibliotheek nu testen op alle platforms met opdrachtregelprogramma's. Als u wilt doorgaan met testen nu alles is ingesteld, is het testen van uw bibliotheek heel eenvoudig:
- Breng wijzigingen aan in uw bibliotheek.
- Voer tests uit vanaf de opdrachtregel, in uw testmap, met
dotnet test
de opdracht.
Uw code wordt automatisch opnieuw opgebouwd wanneer u de opdracht aanroept dotnet test
.
Meerdere projecten gebruiken
Een veelvoorkomende behoefte aan grotere bibliotheken is het plaatsen van functionaliteit in verschillende projecten.
Stel dat u een bibliotheek wilt bouwen die kan worden gebruikt in idiomatische C# en F#. Dat betekent dat gebruikers van uw bibliotheek deze gebruiken op manieren die natuurlijk zijn voor C# of F#. In C# kunt u bijvoorbeeld de bibliotheek als volgt gebruiken:
using AwesomeLibrary.CSharp;
public Task DoThings(Data data)
{
var convertResult = await AwesomeLibrary.ConvertAsync(data);
var result = AwesomeLibrary.Process(convertResult);
// do something with result
}
In F# kan dit er als volgt uitzien:
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
}
Verbruiksscenario's als deze betekenen dat de API's die worden geopend, een andere structuur hebben voor C# en F#. Een veelvoorkomende aanpak om dit te bereiken, is door alle logica van een bibliotheek te factoren in een kernproject, waarbij C#- en F#-projecten de API-lagen definiëren die in dat kernproject worden aangeroepen. In de rest van de sectie worden de volgende namen gebruikt:
- AwesomeLibrary.Core - Een kernproject dat alle logica voor de bibliotheek bevat
- AwesomeLibrary.CSharp - Een project met openbare API's bedoeld voor gebruik in C#
- AwesomeLibrary.FSharp - Een project met openbare API's bedoeld voor gebruik in F#
U kunt de volgende opdrachten uitvoeren in uw terminal om dezelfde structuur te produceren als in deze handleiding:
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
Hiermee worden de drie bovenstaande projecten en een oplossingsbestand toegevoegd dat ze aan elkaar koppelt. Als u het oplossingsbestand maakt en projecten koppelt, kunt u projecten op het hoogste niveau herstellen en bouwen.
Project-naar-project waarnaar wordt verwezen
De beste manier om naar een project te verwijzen, is door de .NET CLI te gebruiken om een projectreferentie toe te voegen. Vanuit de projectmappen AwesomeLibrary.CSharp en AwesomeLibrary.FSharp kunt u de volgende opdracht uitvoeren:
dotnet add reference ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
De projectbestanden voor zowel AwesomeLibrary.CSharp als AwesomeLibrary.FSharp verwijzen nu naar AwesomeLibrary.Core als doel ProjectReference
. U kunt dit controleren door de projectbestanden te controleren en het volgende te zien:
<ItemGroup>
<ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>
U kunt deze sectie handmatig toevoegen aan elk projectbestand als u de .NET CLI liever niet gebruikt.
Een oplossing structureren
Een ander belangrijk aspect van oplossingen voor meerdere projecten is het opzetten van een goede algehele projectstructuur. U kunt code naar eigen goeddunken organiseren en zolang u elk project aan uw oplossingsbestand dotnet sln add
koppelt, kunt u deze uitvoeren dotnet restore
en dotnet build
op oplossingsniveau.