Udostępnij za pomocą


Opracowywanie bibliotek przy użyciu interfejsu wiersza polecenia platformy .NET

W tym artykule opisano sposób pisania bibliotek dla platformy .NET przy użyciu interfejsu wiersza polecenia platformy .NET. Interfejs wiersza polecenia zapewnia wydajne i niskopoziomowe doświadczenie użytkownika, które działa w dowolnym obsługiwanym systemie operacyjnym. Nadal możesz tworzyć biblioteki za pomocą programu Visual Studio, a jeśli jest to preferowane środowisko, zapoznaj się z przewodnikiem programu Visual Studio.

Wymagania wstępne

Potrzebny jest zestaw .NET SDK zainstalowany na maszynie.

W sekcjach tego dokumentu do obsługi wersji programu .NET Framework potrzebny jest program .NET Framework zainstalowany na maszynie z systemem Windows.

Ponadto jeśli chcesz obsługiwać starsze elementy docelowe programu .NET Framework, musisz zainstalować pakiety docelowe lub pakiety deweloperskie ze strony pobierania programu .NET Framework. Zapoznaj się z tą tabelą:

Wersja systemu .NET Framework Co pobrać
4.6.1 Pakiet docelowy programu .NET Framework 4.6.1
4.6 Pakiet docelowy programu .NET Framework 4.6
4.5.2 Pakiet Dla Programistów .NET Framework 4.5.2
4.5.1 .NET Framework 4.5.1 Pakiet dla Programistów
4.5 Zestaw Windows Software Development Kit dla systemu Windows 8
4.0 Zestaw Windows SDK dla systemów Windows 7 i .NET Framework 4
2.0, 3.0 i 3.5 Środowisko uruchomieniowe programu .NET Framework 3.5 z dodatkiem SP1 (lub windows 8 lub nowszy)

Jak kierować na platformę .NET 5+ lub .NET Standard

Możesz kontrolować strukturę docelową projektu, dodając ją do pliku projektu (csproj lub .fsproj). Aby uzyskać wskazówki dotyczące wybierania między platformą .NET 5+ lub .NET Standard, zobacz .NET 5+ i .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>

Jeśli chcesz użyć platformy .NET Framework w wersji 4.0 lub wcześniejszej lub chcesz użyć interfejsu API dostępnego w programie .NET Framework, ale nie w programie .NET Standard (na przykład ), przeczytaj poniższe sekcje i dowiedz się, jak realizować wielocelowe kierowanie.

Jak celować w platformę .NET Framework

Uwaga

W tych instrukcjach założono, że na komputerze jest zainstalowany program .NET Framework. Zapoznaj się z tematem Wymagania wstępne , aby uzyskać zainstalowane zależności.

Pamiętaj, że niektóre wersje programu .NET Framework używane tutaj nie są już obsługiwane. Zapoznaj się z często zadawanymi pytaniami dotyczącymi zasad cyklu życia pomocy technicznej programu .NET Framework dotyczącymi nieobsługiwanych wersji.

Jeśli chcesz osiągnąć maksymalną liczbę deweloperów i projektów, użyj programu .NET Framework 4.0 jako celu odniesienia. Aby celować w .NET Framework, zacznij od użycia poprawnego identyfikatora docelowej platformy (TFM), który odpowiada wersji .NET Framework, której chcesz używać.

Wersja systemu .NET Framework moniker platformy docelowej
.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

Następnie wstawisz ten program TFM do TargetFramework sekcji pliku projektu. Na przykład poniżej przedstawiono sposób pisania biblioteki przeznaczonej dla programu .NET Framework 4.0:

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

I to wszystko. Mimo że ta kompilacja została skompilowana tylko dla programu .NET Framework 4, można użyć biblioteki w nowszych wersjach programu .NET Framework.

Jak wielotargetować

Uwaga

W poniższych instrukcjach założono, że na maszynie jest zainstalowany program .NET Framework. Zapoznaj się z sekcją Wymagania wstępne , aby dowiedzieć się, które zależności należy zainstalować i skąd je pobrać.

Może być konieczne celowanie w starsze wersje platformy .NET Framework, gdy projekt obsługuje zarówno platformę .NET Framework, jak i platformę .NET. W tym scenariuszu, jeśli chcesz użyć nowszych interfejsów API i konstrukcji językowych dla nowszych platform docelowych, użyj dyrektyw #if w swoim kodzie. Może być również konieczne dodanie różnych pakietów i zależności dla każdej docelowej platformy, aby uwzględnić różne interfejsy API potrzebne w każdym przypadku.

Załóżmy na przykład, że masz bibliotekę, która wykonuje operacje sieciowe za pośrednictwem protokołu HTTP. W przypadku platformy .NET Standard i wersji 4.5 lub nowszej programu .NET Framework można użyć klasy HttpClient z przestrzeni nazw System.Net.Http. Jednak wcześniejsze wersje programu .NET Framework nie mają HttpClient klasy , więc można użyć WebClient klasy z System.Net przestrzeni nazw dla tych elementów.

Plik projektu może wyglądać następująco:

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

W tym miejscu zauważysz trzy istotne zmiany:

  1. Węzeł TargetFramework został zastąpiony przez TargetFrameworks, przy czym wewnątrz znajdują się trzy TFM.
  2. Istnieje węzeł <ItemGroup> dla net40 celu, który przyciąga jedno odwołanie do .NET Framework.
  3. Istnieje węzeł <ItemGroup> dla docelowego net45, który ściąga dwa odwołania do .NET Framework.

Symbole preprocesora

System kompilacji zna następujące symbole preprocesora używane w #if dyrektywach:

Platformy docelowe Symbole Dodatkowe symbole
(dostępne w zestawach SDK .NET 5+)
Symbole platformy (dostępne tylko
podczas określania programu TFM specyficznego dla systemu operacyjnego)
.NET Framework NETFRAMEWORK, NET481, , , NET48NET472NET471NET47NET462NET461NET46NET452NET451NET45NET40NET35NET20 NET48_OR_GREATER, NET472_OR_GREATER, NET471_OR_GREATERNET47_OR_GREATERNET462_OR_GREATERNET461_OR_GREATERNET46_OR_GREATERNET452_OR_GREATERNET451_OR_GREATERNET45_OR_GREATERNET40_OR_GREATERNET35_OR_GREATERNET20_OR_GREATER
.NET Standard NETSTANDARD, NETSTANDARD2_1, , , NETSTANDARD2_0NETSTANDARD1_6NETSTANDARD1_5NETSTANDARD1_4NETSTANDARD1_3NETSTANDARD1_2NETSTANDARD1_1NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATER, , NETSTANDARD1_6_OR_GREATER, NETSTANDARD1_5_OR_GREATERNETSTANDARD1_4_OR_GREATER, NETSTANDARD1_3_OR_GREATER, , NETSTANDARD1_2_OR_GREATERNETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (i .NET Core) NET, NET10_0, , , NET9_0NET8_0NET7_0NET6_0NET5_0NETCOREAPPNETCOREAPP3_1NETCOREAPP3_0NETCOREAPP2_2NETCOREAPP2_1NETCOREAPP2_0NETCOREAPP1_1NETCOREAPP1_0 NET10_0_OR_GREATER, NET9_0_OR_GREATER, NET8_0_OR_GREATERNET7_0_OR_GREATERNET6_0_OR_GREATERNET5_0_OR_GREATERNETCOREAPP3_1_OR_GREATERNETCOREAPP3_0_OR_GREATERNETCOREAPP2_2_OR_GREATERNETCOREAPP2_1_OR_GREATERNETCOREAPP2_0_OR_GREATERNETCOREAPP1_1_OR_GREATERNETCOREAPP1_0_OR_GREATER ANDROID, BROWSER, , IOS, MACCATALYSTMACOS, , TVOS, , WINDOWS
[OS][version] (na przykład IOS15_1),
[OS][version]_OR_GREATER (na przykład IOS15_1_OR_GREATER)

Uwaga

  • Symbole bez wersji są definiowane niezależnie od docelowej wersji.
  • Symbole specyficzne dla wersji są definiowane tylko dla docelowej wersji.
  • Symbole <framework>_OR_GREATER są definiowane dla docelowej wersji i wszystkich wcześniejszych wersji. Jeśli na przykład używasz .NET Framework 2.0, zdefiniowane są następujące symbole: NET20, NET20_OR_GREATER, NET11_OR_GREATER i NET10_OR_GREATER.
  • Symbole NETSTANDARD<x>_<y>_OR_GREATER są definiowane tylko dla obiektów docelowych platformy .NET Standard, a nie dla obiektów docelowych implementujących platformę .NET Standard, takich jak .NET Core i .NET Framework.
  • Różnią się one od identyfikatorów frameworku docelowego (TFMs) używanych przez właściwość MSBuild i NuGet.

Oto przykład użycia kompilacji warunkowej dla elementu docelowego:

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

Jeśli stworzysz ten projekt za pomocą dotnet build, zauważysz trzy katalogi w folderze bin/:

net40/
net45/
netstandard2.0/

Każdy z nich zawiera .dll pliki dla każdego obiektu docelowego.

Jak testować biblioteki na platformie .NET

Ważne jest, aby móc testować na różnych platformach. Możesz użyć xUnit lub MSTest z pudełka. Oba są doskonale odpowiednie do testów jednostkowych twojej biblioteki na platformie .NET. Sposób konfigurowania rozwiązania przy użyciu projektów testowych zależy od struktury rozwiązania. W poniższym przykładzie przyjęto założenie, że katalogi testowe i źródłowe działają w tym samym katalogu najwyższego poziomu.

Uwaga

Używa to niektórych poleceń .NET CLI. Aby uzyskać więcej informacji, zobacz dotnet new and dotnet sln .

  1. Skonfiguruj rozwiązanie. Można to zrobić za pomocą następujących poleceń:

    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
    

    Spowoduje to utworzenie projektów i połączenie ich razem w rozwiązaniu. Twoja struktura katalogów dla SolutionWithSrcAndTest powinna wyglądać następująco:

    /SolutionWithSrcAndTest
    |__SolutionWithSrcAndTest.sln
    |__MyProject/
    |__MyProject.Test/
    
  2. Przejdź do katalogu projektu testowego i dodaj odwołanie do MyProject.Test z MyProject.

    cd MyProject.Test
    dotnet reference add ../MyProject/MyProject.csproj
    
  3. Przywracanie pakietów i kompilowanie projektów.

    dotnet restore
    dotnet build
    
  4. Sprawdź, czy xUnit wykonuje się, uruchamiając polecenie dotnet test. Jeśli zdecydujesz się użyć narzędzia MSTest, to moduł uruchamiający MSTest w konsoli powinien być uruchomiony zamiast tego.

I to wszystko. Teraz możesz przetestować bibliotekę na wszystkich platformach przy użyciu narzędzi wiersza polecenia. Aby kontynuować testowanie teraz, gdy wszystko jest skonfigurowane, testowanie biblioteki jest bardzo proste:

  1. Wprowadź zmiany w bibliotece.
  2. Uruchom testy z wiersza polecenia w katalogu testowym, używając komendy dotnet test.

Kod zostanie automatycznie skompilowany, gdy wywołasz polecenie dotnet test.

Jak używać wielu projektów

Typowym zapotrzebowaniem na większe biblioteki jest umieszczenie funkcji w różnych projektach.

Wyobraź sobie, że chcesz utworzyć bibliotekę, która może być zużywana w idiomatycznych językach C# i F#. Oznaczałoby to, że użytkownicy biblioteki używają jej w sposób naturalny dla języka C# lub F#. Na przykład w języku C# możesz użyć biblioteki w następujący sposób:

using AwesomeLibrary.CSharp;

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

W języku F# może to wyglądać następująco:

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
}

Takie scenariusze użycia oznaczają, że uzyskiwane interfejsy API muszą mieć inną strukturę dla języka C# i języka F#. Typowym podejściem do osiągnięcia tego celu jest uwzględnienie całej logiki biblioteki w podstawowym projekcie przy użyciu projektów języka C# i F# definiujących warstwy interfejsu API wywołujące ten podstawowy projekt. W pozostałej części sekcji będą używane następujące nazwy:

  • AwesomeLibrary.Core — podstawowy projekt zawierający całą logikę biblioteki
  • AwesomeLibrary.CSharp — projekt z publicznymi interfejsami API przeznaczonymi do użycia w języku C#
  • AwesomeLibrary.FSharp — projekt z publicznymi interfejsami API przeznaczonymi do użycia w języku F#

Następujące polecenia można uruchomić w terminalu, aby utworzyć taką samą strukturę jak w tym przewodniku:

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

Spowoduje to dodanie trzech projektów powyżej i pliku rozwiązania, który łączy je ze sobą. Utworzenie pliku rozwiązania i łączenie projektów umożliwi przywracanie i kompilowanie projektów z najwyższego poziomu.

Odwoływanie się z projektu do projektu

Najlepszym sposobem odwołowania się do projektu jest użycie interfejsu wiersza polecenia platformy .NET w celu dodania odwołania do projektu. W katalogach projektów AwesomeLibrary.CSharp i AwesomeLibrary.FSharp można uruchomić następujące polecenie:

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

Pliki projektu zarówno AwesomeLibrary.CSharp, jak i AwesomeLibrary.FSharp będą teraz odwoływać się do AwesomeLibrary.Core jako ProjectReference elementu docelowego. Możesz to zweryfikować, przeglądając pliki projektu, które zawierają następujące elementy:

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

Tę sekcję można dodać do każdego pliku projektu ręcznie, jeśli nie chcesz używać interfejsu wiersza polecenia platformy .NET.

Tworzenie struktury rozwiązania

Innym ważnym aspektem rozwiązań wieloprojektowych jest ustanowienie dobrej ogólnej struktury projektu. Możesz zorganizować kod w dowolny sposób, a tak długo, jak łączysz każdy projekt z plikiem rozwiązania za pomocą dotnet sln add, będzie można uruchomić dotnet restore i dotnet build na poziomie rozwiązania.