Freigeben über


Entwickeln von Bibliotheken mit der .NET CLI

In diesem Artikel wird beschrieben, wie Bibliotheken für .NET mithilfe der .NET CLI geschrieben werden. Die CLI bietet effiziente Funktionalität auf niedriger Stufe, die auf allen unterstützten Betriebssystemen funktioniert. Sie können weiterhin Bibliotheken mit Visual Studio erstellen, und wenn dies Ihr bevorzugtes Erlebnis ist, verweisen Sie auf das Visual Studio-Handbuch.

Voraussetzungen

Sie benötigen das .NET SDK auf Ihrem Computer installiert.

Für die Abschnitte dieses Dokuments mit .NET Framework-Versionen benötigen Sie das .NET Framework auf einem Windows Computer installiert.

Wenn Sie auch ältere .NET Framework-Ziele unterstützen möchten, müssen Sie Zielpakete oder Entwicklerpakete auf der Downloadseite .NET Framework installieren. Weitere Informationen finden Sie in dieser Tabelle:

.NET Framework-Version Empfohlene Downloads
4.6.1 .NET Framework 4.6.1 Zielpaket
4.6 .NET Framework 4.6 Zielpaket
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 und .NET Framework 4
2.0, 3.0 und 3.5 .NET Framework 3.5 SP1 Runtime (oder Windows 8+ Version)

So zielen Sie auf .NET 5+ oder .NET Standard

Sie können das Zielframework Ihres Projekts kontrollieren, indem Sie es zu Ihrer Projektdatei hinzufügen ( .csproj oder .fsproj). Anleitungen zum Auswählen zwischen der Zielbestimmung .NET 5+ oder .NET Standard finden Sie unter .NET 5+ und .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>

Wenn Sie .NET Framework-Versionen 4.0 oder darunter als Ziel verwenden möchten oder eine API verwenden möchten, die in .NET Framework, aber nicht in .NET Standard (z. B. System.Drawing) verfügbar ist, lesen Sie die folgenden Abschnitte und erfahren Sie, wie Sie multitargetieren.

So legen Sie .NET Framework als Ziel fest

Hinweis

In diesen Anweisungen wird davon ausgegangen, dass .NET Framework auf Ihrem Computer installiert ist. Informationen zum Installieren von Abhängigkeiten finden Sie unter Erforderliche Komponenten.

Beachten Sie, dass einige der hier verwendeten .NET Framework-Versionen nicht mehr unterstützt werden. Weitere Informationen zu nicht unterstützten Versionen finden Sie unter .NET Framework Support Lifecycle Policy.

Wenn Sie die maximale Anzahl von Entwicklern und Projekten erreichen möchten, verwenden Sie .NET Framework 4.0 als Basisziel. Um .NET Framework als Ziel zu verwenden, verwenden Sie zunächst den richtigen Target Framework Moniker (TFM), der der .NET Framework-Version entspricht, die Sie unterstützen möchten.

.NET Framework-Version 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

Sie fügen anschließend TFM in den TargetFramework-Abschnitt Ihrer Projektdatei ein. Hier erfahren Sie beispielsweise, wie Sie eine Bibliothek schreiben, die auf .NET Framework 4.0 ausgerichtet ist:

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

Das war’s! Obwohl dies nur für .NET Framework 4 kompiliert wurde, können Sie die Bibliothek für neuere Versionen von .NET Framework verwenden.

So macht man das: Mehrfachzielsetzung

Hinweis

In den folgenden Anweisungen wird davon ausgegangen, dass das .NET Framework auf Ihrem Computer installiert ist. Im Abschnitt Erforderliche Komponenten finden Sie weitere Informationen darüber, welche Abhängigkeiten Sie installieren müssen, und wo Sie diese herunterladen können.

Möglicherweise müssen Sie ältere Versionen des .NET Frameworks als Ziel verwenden, wenn Ihr Projekt sowohl das .NET Framework als auch .NET unterstützt. Wenn Sie neuere APIs und Sprachkonstrukte für die neueren Ziele verwenden möchten, verwenden Sie in diesem Szenario #if-Anweisungen in Ihrem Code. Sie müssen möglicherweise auch unterschiedliche Pakete und Abhängigkeiten für jede Zielplattform hinzufügen, um verschiedene APIs einzuschließen, die für jeden Fall benötigt werden.

Nehmen wir z.B. einmal an, dass Sie eine Bibliothek haben, die Netzwerkvorgänge über HTTP ausführt. Für .NET Standard und die .NET Framework-Versionen 4.5 oder höher können Sie die klasse HttpClient aus dem Namespace System.Net.Http verwenden. Frühere Versionen des .NET Frameworks verfügen jedoch nicht über die klasse HttpClient, sodass Sie stattdessen die klasse WebClient aus dem namespace System.Net verwenden können.

Ihre Projektdatei könnte also Folgendermaßen aussehen:

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

Sie sehen hier drei große Veränderungen:

  1. Der TargetFramework-Knoten wurde durch TargetFrameworks ersetzt, und drei TFMs werden darin dargestellt.
  2. Es gibt einen <ItemGroup>-Knoten für das net40-Ziel, das einen .NET Framework-Verweis einbezieht.
  3. Es gibt einen <ItemGroup>-Knoten für das net45-Ziel, der zwei .NET Framework-Referenzen einbezieht.

Präprozessorsymbole

Das Buildsystem beachtet die folgenden Präprozessorsymbole, die in #if-Anweisungen verwendet werden:

Zielframeworks Symbole Zusätzliche Symbole
(verfügbar in .NET 5+ SDKs)
Plattformsymbole (nur verfügbar,
wenn Sie einen betriebssystemspezifischen TFM angeben)
.NET Framework NETFRAMEWORK, NET481, NET48, NET472, NET471, NET47, NET462, NET461, NET46, NET452, NET451, NET45, NET40, NET35, NET20 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_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, NETSTANDARD1_6_OR_GREATERNETSTANDARD1_5_OR_GREATER, NETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, NETSTANDARD1_2_OR_GREATER, NETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (und .NET Core) NET, NET10_0, NET9_0, NET8_0, NET7_0, NET6_0, NET5_0, NETCOREAPP, NETCOREAPP3_1, NETCOREAPP3_0, NETCOREAPP2_2, NETCOREAPP2_1, NETCOREAPP2_0, NETCOREAPP1_1, NETCOREAPP1_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] (z. B. IOS15_1),
[OS][version]_OR_GREATER (z. B. IOS15_1_OR_GREATER)

Hinweis

  • Versionslose Symbole werden unabhängig von der Zielversion definiert.
  • Versionsspezifische Symbole werden nur für die Version definiert, die Sie als Ziel verwenden.
  • Die <framework>_OR_GREATER-Symbole werden für die Zielversion und alle früheren Versionen definiert. Wenn Sie beispielsweise auf .NET Framework 2.0 abzielen, werden die folgenden Symbole definiert: NET20, NET20_OR_GREATER, NET11_OR_GREATER und NET10_OR_GREATER.
  • Die Symbole NETSTANDARD<x>_<y>_OR_GREATER werden nur für .NET Standardziele definiert und nicht für Ziele, die .NET Standard implementieren, z. B. .NET Core und .NET Framework.
  • Diese unterscheiden sich von den Zielframeworkmonikern (TFMs), die von der TargetFramework-Eigenschaft von MSBuild und NuGet verwendet werden.

Hier sehen sie ein Beispiel, das die bedingte Kompilierung pro Ziel verwendet:

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

Wenn Sie diese Projekt mit dotnet build erstellen, bemerken Sie drei Verzeichnisse unter dem bin/-Ordner:

net40/
net45/
netstandard2.0/

Jedes dieser Verzeichnisse enthält die .dll-Dateien für jedes Ziel.

So testen Sie Bibliotheken auf .NET

Es ist wichtig, über Plattformen hinweg testen zu können. Sie können entweder xUnit oder MSTest standardmäßig verwenden. Beide eignen sich perfekt zum Testen Ihrer Bibliothek auf .NET. Wie Sie Ihre Lösung mit Testprojekten einrichten, hängt von der Struktur Ihrer Lösung ab. Im folgenden Beispiel wird davon ausgegangen, dass Test- und Quellverzeichnisse im gleichen Verzeichnis auf oberster Ebene vorhanden sind.

Hinweis

Dies verwendet einige .NET CLIBefehle. Weitere Informationen finden Sie unter dotnet new und dotnet sln.

  1. Richten Sie Ihre Lösung ein. Verwenden Sie dazu folgende Befehle:

    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
    

    Dadurch werden Projekte erstellt und in einer Lösung verknüpft. Ihr Verzeichnis für SolutionWithSrcAndTest sollte in etwa so aussehen:

    /SolutionWithSrcAndTest
    |__SolutionWithSrcAndTest.sln
    |__MyProject/
    |__MyProject.Test/
    
  2. Navigieren Sie zum Verzeichnis des Testprojekts, und fügen Sie einen Verweis zu MyProject.Test von MyProject hinzu.

    cd MyProject.Test
    dotnet reference add ../MyProject/MyProject.csproj
    
  3. So stellen Sie Pakete wieder her und erstellen Projekte:

    dotnet restore
    dotnet build
    
  4. Überprüfen Sie, ob xUnit durch Ausführung des dotnet test-Befehls ausgeführt wird. Wenn Sie MSTests verwenden möchten, dann muss stattdessen das MSTest-Konsolenausführungsprogramm ausgeführt werden.

Das war’s! Jetzt können Sie Ihre Bibliothek mithilfe der Befehlszeilentools plattformübergreifend testen. Nachdem jetzt alles eingerichtet ist, ist das weitere Testen Ihrer Bibliothek sehr einfach:

  1. Nehmen Sie Änderungen an Ihrer Bibliothek vor.
  2. Führen Sie mit dem Befehl dotnet test in Ihrem Testverzeichnis Tests über die Befehlszeile aus.

Der Code wird automatisch neu erstellt, wenn Sie den Befehl dotnet test aufrufen.

So verwenden Sie mehrere Projekte

Eine häufige Anforderung an größere Bibliotheken ist es, Funktionalität in verschiedenen Projekten zu bieten.

Stellen Sie sich vor, Sie möchten eine Bibliothek erstellen, die in idiomatischem C# und F# genutzt werden kann. Das würde bedeuten, dass Benutzer Ihrer Bibliotheken diese auf eine Art nutzen, die natürlich für C# und F# ist. In C# z.B. könnten Sie die Bibliothek wie folgt nutzen:

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# könnte es so aussehen:

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
}

Solche Nutzungsszenarios bedeuten, dass die APIs, auf die zugegriffen wird, eine andere Struktur für C# und F# haben müssen. Eine gängige Methode, dies zu erreichen ist, die gesamte Logik einer Bibliothek in ein Kernprojekt zu zerlegen, in dem C# und F# Projekte die API-Schichten definieren, die das Kernprojekt aufrufen. Im übrigen Abschnitt werden die folgenden Namen verwendet:

  • AwesomeLibrary.Core – Ein Kernprojekt, das die gesamte Logik für die Bibliothek enthält
  • AwesomeLibrary.CSharp – Ein Projekt mit öffentlichen APIs, die für den Gebrauch in C# vorgesehen sind
  • AwesomeLibrary.FSharp – Ein Projekt mit öffentlichen APIs, die für den Gebrauch in F# vorgesehen sind

Sie können die folgenden Befehle in Ihrem Terminal ausführen, um die gleiche Struktur wie dieses Handbuch zu erstellen:

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

Dadurch werden die drei oben genannten Projekte und eine Lösungsdatei hinzugefügt, die sie verknüpft. Durch das Erstellen der Projektmappendatei und das Verknüpfen der Projekte können Sie Projekte auf übergeordneter Ebene wiederherstellen und erstellen.

Projekt-zu-Projekt-Verweise

Die beste Methode zum Verweisen auf ein Projekt ist die Verwendung der .NET CLI zum Hinzufügen eines Projektverweises. Sie können den folgenden Befehl von den Projektverzeichnissen AwesomeLibrary.CSharp und AwesomeLibrary.FSharp ausführen:

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

Die Projektdateien für AwesomeLibrary.CSharp und AwesomeLibrary.FSharp verweisen nun auf AwesomeLibrary.Core als ProjectReference-Ziel. Sie können dies überprüfen, indem Sie die Projektdateien prüfen und Folgendes darin sehen:

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

Sie können diesen Abschnitt manuell zu jeder Projektdatei hinzufügen, wenn Sie die .NET CLI nicht verwenden möchten.

Strukturieren einer Lösung

Ein weiterer wichtiger Aspekt von Mehrprojektlösungen ist die Einrichtung einer guten allgemeinen Projektstruktur. Sie können den Code beliebig organisieren, solange Sie jedes Projekt mit Ihrer Projektmappendatei mit dotnet sln add verknüpfen. Dadurch können Sie dotnet restore und dotnet build auf Projektmappenebene ausführen.