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:

  1. Het TargetFramework knooppunt is vervangen door TargetFrameworksen er worden drie TFM's uitgedrukt in.
  2. Er is een <ItemGroup> knooppunt voor het ophalen van het net40 doel in één .NET Framework-verwijzing.
  3. Er is een <ItemGroup> knooppunt voor de net45 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, , NET472NET48, NET471, , NET47, , NET462NET461, NET46, NET451NET452NET45NET40NET35,NET20 NET48_OR_GREATER, , NET472_OR_GREATER, , , , NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, NET45_OR_GREATERNET40_OR_GREATERNET35_OR_GREATERNET451_OR_GREATERNET462_OR_GREATERNET47_OR_GREATERNET471_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, , NETSTANDARD2_0, NETSTANDARD1_6NETSTANDARD2_1, , NETSTANDARD1_5, NETSTANDARD1_4, , NETSTANDARD1_1NETSTANDARD1_2NETSTANDARD1_3NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, , NETSTANDARD1_6_OR_GREATERNETSTANDARD2_0_OR_GREATER, NETSTANDARD1_5_OR_GREATER, , NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_1_OR_GREATERNETSTANDARD1_2_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (en .NET Core) NET, , NET8_0, , , , NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1NETCOREAPP2_2NET5_0NET6_0NET7_0NETCOREAPP1_0 NET8_0_OR_GREATER, , NET7_0_OR_GREATERNET6_0_OR_GREATER, NET5_0_OR_GREATER, NETCOREAPP3_1_OR_GREATER, , NETCOREAPP3_0_OR_GREATER, , NETCOREAPP2_0_OR_GREATERNETCOREAPP2_1_OR_GREATERNETCOREAPP1_1_OR_GREATERNETCOREAPP2_2_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, , BROWSERIOS, MACCATALYST, , MACOS, , TVOSWINDOWS
[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_GREATERen NET10_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.

  1. 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/
    
  2. Navigeer naar de map van het testproject en voeg een verwijzing naar MyProject.Test van waaruit MyProject.

    cd MyProject.Test
    dotnet add reference ../MyProject/MyProject.csproj
    
  3. Pakketten herstellen en projecten bouwen:

    dotnet restore
    dotnet build
    
  4. 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:

  1. Breng wijzigingen aan in uw bibliotheek.
  2. 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 addkoppelt, kunt u deze uitvoeren dotnet restore en dotnet build op oplossingsniveau.