Utveckla bibliotek med .NET CLI

Den här artikeln beskriver hur du skriver bibliotek för .NET med hjälp av .NET CLI. CLI ger en effektiv och låg nivå som fungerar i alla operativsystem som stöds. Du kan fortfarande skapa bibliotek med Visual Studio, och om det är din föredragna upplevelse kan du läsa Visual Studio-guiden.

Förutsättningar

Du behöver .NET SDK installerat på datorn.

För avsnitten i det här dokumentet som hanterar .NET Framework-versioner behöver du .NET Framework installerat på en Windows-dator.

Om du vill stödja äldre .NET Framework-mål måste du dessutom installera målpaket eller utvecklarpaket från nedladdningssidan för .NET Framework. Se den här tabellen:

.NET Framework-version Vad du ska ladda ned
4.6.1 Målpaket för .NET Framework 4.6.1
4,6 .NET Framework 4.6-målpaket
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 för Windows 8
4.0 Windows SDK för Windows 7 och .NET Framework 4
2.0, 3.0 och 3.5 .NET Framework 3.5 SP1 Runtime (eller Windows 8+ version)

Så här riktar du in dig på .NET 5+ eller .NET Standard

Du styr projektets målramverk genom att lägga till det i projektfilen (.csproj eller .fsproj). Vägledning om hur du väljer mellan att rikta in sig på .NET 5+ eller .NET Standard finns i .NET 5+ och .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>

Om du vill rikta in dig på .NET Framework version 4.0 eller senare, eller om du vill använda ett API som är tillgängligt i .NET Framework men inte i .NET Standard (till exempel System.Drawing), läser du följande avsnitt och lär dig hur du multitarget.

Så här riktar du in dig på .NET Framework

Kommentar

Dessa instruktioner förutsätter att du har .NET Framework installerat på datorn. Se Förutsättningar för att få beroenden installerade.

Tänk på att vissa av de .NET Framework-versioner som används här inte längre stöds. Läs vanliga frågor och svar om versioner som inte stöds i .NET Framework Support Lifecycle Policy.

Om du vill nå det maximala antalet utvecklare och projekt använder du .NET Framework 4.0 som baslinjemål. Börja med att använda rätt Target Framework Moniker (TFM) som motsvarar den .NET Framework-version som du vill stödja för att rikta in dig på .NET Framework.

.NET Framework-version TFM
.NET Framework 2.0 net20
.NET Framework 3.0 net30
.NET Framework 3.5 net35
.NET Framework 4.0 net40
Microsoft .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

Sedan infogar du den här TFM:en i TargetFramework avsnittet i projektfilen. Så här skriver du till exempel ett bibliotek som riktar sig till .NET Framework 4.0:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net40</TargetFramework>
  </PropertyGroup>
</Project>

Det var det! Även om detta endast kompilerades för .NET Framework 4 kan du använda biblioteket i nyare versioner av .NET Framework.

Så här gör du multitarget

Kommentar

Följande instruktioner förutsätter att du har .NET Framework installerat på datorn. Läs avsnittet Förutsättningar för att lära dig vilka beroenden du behöver installera och var du kan ladda ned dem från.

Du kan behöva rikta in dig på äldre versioner av .NET Framework när projektet stöder både .NET Framework och .NET. Om du i det här scenariot vill använda nyare API:er och språkkonstruktioner för de nyare målen använder #if du direktiv i koden. Du kan också behöva lägga till olika paket och beroenden för varje plattform som du riktar in dig på för att inkludera de olika API:er som behövs för varje ärende.

Anta till exempel att du har ett bibliotek som utför nätverksåtgärder via HTTP. För .NET Standard och .NET Framework version 4.5 eller senare kan du använda HttpClient klassen från System.Net.Http namnområdet. Tidigare versioner av .NET Framework har HttpClient dock inte klassen, så du kan använda WebClient klassen från System.Net namnområdet för dem i stället.

Projektfilen kan se ut så här:

<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>

Du kommer att märka tre större ändringar här:

  1. Noden TargetFramework har ersatts av TargetFrameworksoch tre TFM uttrycks inuti.
  2. Det finns en <ItemGroup> nod för målet som net40 hämtar en .NET Framework-referens.
  3. Det finns en <ItemGroup> nod för målet som net45 hämtar två .NET Framework-referenser.

Preprocessorsymboler

Byggsystemet är medvetet om följande preprocessorsymboler som används i #if direktiv:

Målramverk Symboler Ytterligare symboler
(finns i .NET 5+ SDK:er)
Plattformssymboler (endast tillgängliga
när du anger en OS-specifik TFM)
.NET Framework NETFRAMEWORK, NET48, NET472, NET471, NET47, NET462, NET461, NET46, , NET452, NET451, NET45, NET40, , NET35NET20 NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATER, NET47_OR_GREATER, NET462_OR_GREATER, NET461_OR_GREATER, NET46_OR_GREATER, NET452_OR_GREATER, , NET451_OR_GREATER, NET45_OR_GREATER, NET40_OR_GREATER, , , NET35_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, NETSTANDARD2_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, , NETSTANDARD1_3, NETSTANDARD1_2, , NETSTANDARD1_1NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, , NETSTANDARD1_2_OR_GREATER, , NETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (och .NET Core) NET, NET8_0, NET7_0, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, , NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, , , NETCOREAPP1_1NETCOREAPP1_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_2_OR_GREATER, NETCOREAPP2_1_OR_GREATER, NETCOREAPP2_0_OR_GREATER, , , NETCOREAPP1_1_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, BROWSER, IOS, MACCATALYST, MACOS, , TVOS, , WINDOWS
[OS][version] (till exempel IOS15_1),
[OS][version]_OR_GREATER (till exempel IOS15_1_OR_GREATER)

Kommentar

  • Versionslösa symboler definieras oavsett vilken version du riktar in dig på.
  • Versionsspecifika symboler definieras endast för den version som du riktar in dig på.
  • Symbolerna <framework>_OR_GREATER definieras för den version som du riktar in dig på och alla tidigare versioner. Om du till exempel riktar in dig på .NET Framework 2.0 definieras följande symboler: NET20, NET20_OR_GREATER, NET11_OR_GREATERoch NET10_OR_GREATER.
  • Symbolerna NETSTANDARD<x>_<y>_OR_GREATER definieras endast för .NET Standard-mål och inte för mål som implementerar .NET Standard, till exempel .NET Core och .NET Framework.
  • Dessa skiljer sig från målramverksmonikers (TFM: er) som används av egenskapen MSBuild TargetFramework och NuGet.

Här är ett exempel på användning av villkorsstyrd kompilering per mål:

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
    }
}

Om du skapar det här projektet med dotnet buildser du tre kataloger under bin/ mappen:

net40/
net45/
netstandard2.0/

Var och en .dll av dessa innehåller filerna för varje mål.

Testa bibliotek på .NET

Det är viktigt att kunna testa på olika plattformar. Du kan använda xUnit eller MSTest direkt. Båda passar perfekt för enhetstestning av biblioteket på .NET. Hur du konfigurerar din lösning med testprojekt beror på lösningens struktur. I följande exempel förutsätts att test- och källkatalogerna finns i samma toppnivåkatalog.

Kommentar

Detta använder vissa .NET CLI-kommandon . Mer information finns i dotnet new och dotnet sln .

  1. Konfigurera din lösning. Du kan göra det med följande kommandon:

    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
    

    Detta skapar projekt och kopplar samman dem i en lösning. Din katalog för SolutionWithSrcAndTest bör se ut så här:

    /SolutionWithSrcAndTest
    |__SolutionWithSrcAndTest.sln
    |__MyProject/
    |__MyProject.Test/
    
  2. Navigera till testprojektets katalog och lägg till en referens till MyProject.Test från MyProject.

    cd MyProject.Test
    dotnet add reference ../MyProject/MyProject.csproj
    
  3. Återställa paket och byggprojekt:

    dotnet restore
    dotnet build
    
  4. Kontrollera att xUnit körs genom att dotnet test köra kommandot. Om du väljer att använda MSTest ska MSTest-konsollöparen köras i stället.

Det var det! Nu kan du testa biblioteket på alla plattformar med hjälp av kommandoradsverktyg. Om du vill fortsätta testa nu när allt har konfigurerats är det mycket enkelt att testa biblioteket:

  1. Gör ändringar i biblioteket.
  2. Kör tester från kommandoraden i testkatalogen med dotnet test kommandot .

Koden återskapas automatiskt när du anropar dotnet test kommandot.

Så här använder du flera projekt

Ett vanligt behov av större bibliotek är att placera funktioner i olika projekt.

Anta att du vill skapa ett bibliotek som kan användas i idiomatiska C# och F#. Det skulle innebära att användarna av biblioteket använder det på ett sätt som är naturligt för C# eller F#. I C# kan du till exempel använda biblioteket så här:

using AwesomeLibrary.CSharp;

public Task DoThings(Data data)
{
    var convertResult = await AwesomeLibrary.ConvertAsync(data);
    var result = AwesomeLibrary.Process(convertResult);
    // do something with result
}

I F# kan det se ut så här:

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
}

Förbrukningsscenarier som detta innebär att DE API:er som används måste ha en annan struktur för C# och F#. En vanlig metod för att åstadkomma detta är att ta med all logik i ett bibliotek i ett kärnprojekt, med C#- och F#-projekt som definierar DE API-lager som anropar till kärnprojektet. Resten av avsnittet använder följande namn:

  • AwesomeLibrary.Core – ett kärnprojekt som innehåller all logik för biblioteket
  • AwesomeLibrary.CSharp – ett projekt med offentliga API:er avsedda för förbrukning i C#
  • AwesomeLibrary.FSharp – ett projekt med offentliga API:er avsedda för förbrukning i F#

Du kan köra följande kommandon i terminalen för att skapa samma struktur som den här guiden:

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

Detta lägger till de tre projekten ovan och en lösningsfil som länkar ihop dem. Genom att skapa lösningsfilen och länka projekt kan du återställa och skapa projekt från en toppnivå.

Projekt-till-projekt-referens

Det bästa sättet att referera till ett projekt är att använda .NET CLI för att lägga till en projektreferens. Från projektkatalogerna AwesomeLibrary.CSharp och AwesomeLibrary.FSharp kan du köra följande kommando:

dotnet add reference ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj

Projektfilerna för både AwesomeLibrary.CSharp och AwesomeLibrary.FSharp refererar nu till AwesomeLibrary.Core som mål ProjectReference . Du kan kontrollera detta genom att granska projektfilerna och se följande i dem:

<ItemGroup>
  <ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>

Du kan lägga till det här avsnittet i varje projektfil manuellt om du föredrar att inte använda .NET CLI.

Strukturera en lösning

En annan viktig aspekt av lösningar för flera projekt är att upprätta en bra övergripande projektstruktur. Du kan ordna kod hur du vill, och så länge du länkar varje projekt till lösningsfilen med dotnet sln addkan du köra dotnet restore och dotnet build på lösningsnivå.