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 niskie środowisko, 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 | .NET Framework 4.5.2 Developer Pack |
4.5.1 | .NET Framework 4.5.1 Developer Pack |
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) |
Instrukcje dotyczące platformy .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 wyboru między określaniem wartości docelowej platformy .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 nowszej 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ę, System.Drawing
jak wieloskładniowy.
How to target .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 zastosować docelowy program .NET Framework, zacznij od użycia poprawnego programu Target Framework Moniker (TFM), który odpowiada wersji programu .NET Framework, którą chcesz obsługiwać.
Wersja systemu .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 |
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 skierowanie starszych wersji programu .NET Framework, gdy projekt obsługuje zarówno program .NET Framework, jak i platformę .NET. W tym scenariuszu, jeśli chcesz użyć nowszych interfejsów API i konstrukcji językowych dla nowszych obiektów docelowych, użyj #if
dyrektyw w kodzie. Może być również konieczne dodanie różnych pakietów i zależności dla każdej platformy, dla której jest przeznaczona wartość docelowa, aby uwzględnić różne interfejsy API potrzebne dla każdego 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 programu .NET Framework w wersji 4.5 lub nowszej System.Net.Http
można użyć HttpClient
klasy z przestrzeni nazw. 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:
- Węzeł
TargetFramework
został zastąpiony przezTargetFrameworks
program , a trzy maszyny TFM są wyrażane wewnątrz. <ItemGroup>
Istnieje węzeł donet40
ściągania docelowego w jednym odwołaniu programu .NET Framework.- Istnieje węzeł docelowy
<ItemGroup>
net45
ściągający w dwóch odwołaniach programu .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 .NET 5+ SDK) |
Symbole platformy (dostępne tylko podczas określania programu TFM specyficznego dla systemu operacyjnego) |
---|---|---|---|
.NET Framework | NETFRAMEWORK , NET481 , , , NET471 NET47 NET45 NET40 NET472 NET461 NET35 NET462 NET46 NET452 NET451 NET48 NET20 |
NET48_OR_GREATER , NET472_OR_GREATER , , , NET462_OR_GREATER NET452_OR_GREATER NET47_OR_GREATER NET46_OR_GREATER NET35_OR_GREATER NET461_OR_GREATER NET451_OR_GREATER NET45_OR_GREATER NET40_OR_GREATER NET471_OR_GREATER NET20_OR_GREATER |
|
.NET Standard | NETSTANDARD , NETSTANDARD2_1 , , , NETSTANDARD1_5 NETSTANDARD1_1 NETSTANDARD1_6 NETSTANDARD1_4 NETSTANDARD1_3 NETSTANDARD1_2 NETSTANDARD2_0 NETSTANDARD1_0 |
NETSTANDARD2_1_OR_GREATER , NETSTANDARD2_0_OR_GREATER , , NETSTANDARD1_6_OR_GREATER , NETSTANDARD1_4_OR_GREATER NETSTANDARD1_5_OR_GREATER , NETSTANDARD1_3_OR_GREATER , , NETSTANDARD1_1_OR_GREATER NETSTANDARD1_2_OR_GREATER NETSTANDARD1_0_OR_GREATER |
|
.NET 5+ (i .NET Core) | NET , NET9_0 , , , NET6_0 NET5_0 NETCOREAPP2_0 NET7_0 NETCOREAPP3_1 NETCOREAPP1_1 NETCOREAPP NETCOREAPP3_0 NETCOREAPP2_2 NETCOREAPP2_1 NET8_0 NETCOREAPP1_0 |
NET8_0_OR_GREATER , NET7_0_OR_GREATER , , , NETCOREAPP3_1_OR_GREATER NETCOREAPP2_1_OR_GREATER NETCOREAPP1_1_OR_GREATER NET5_0_OR_GREATER NETCOREAPP3_0_OR_GREATER NETCOREAPP2_2_OR_GREATER NETCOREAPP2_0_OR_GREATER NET6_0_OR_GREATER NETCOREAPP1_0_OR_GREATER |
ANDROID , BROWSER , , IOS , MACOS MACCATALYST , , 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 platformy .NET Framework 2.0, zdefiniowane są następujące symbole:NET20
, ,NET20_OR_GREATER
NET11_OR_GREATER
iNET10_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 obiektów docelowych monikers (TFMs) używanych przez właściwość MSBuild
TargetFramework
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 skompilujesz ten projekt za pomocą dotnet build
polecenia , 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. Obie te elementy doskonale nadają się do testowania jednostkowego 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ń interfejsu wiersza polecenia platformy .NET. Aby uzyskać więcej informacji, zobacz dotnet new and dotnet sln .
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. Katalog powinien
SolutionWithSrcAndTest
wyglądać następująco:/SolutionWithSrcAndTest |__SolutionWithSrcAndTest.sln |__MyProject/ |__MyProject.Test/
Przejdź do katalogu projektu testowego i dodaj odwołanie do
MyProject.Test
pliku .MyProject
cd MyProject.Test dotnet add reference ../MyProject/MyProject.csproj
Przywracanie pakietów i projektów kompilacji:
dotnet restore dotnet build
Sprawdź, czy narzędzie xUnit jest uruchamiane
dotnet test
, wykonując polecenie . Jeśli zdecydujesz się użyć narzędzia MSTest, moduł uruchamiający konsolę MSTest powinien zamiast tego uruchomić.
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:
- Wprowadź zmiany w bibliotece.
- Uruchom testy z wiersza polecenia w katalogu testowym za
dotnet test
pomocą polecenia .
Kod zostanie automatycznie skompilowany podczas wywoływania dotnet test
polecenia.
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ę 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 add reference ../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 sprawdzić, sprawdzając pliki projektu i wyświetlając w nich 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 jednak zorganizować kod i tak długo, jak łączysz każdy projekt z plikiem rozwiązania za pomocą dotnet sln add
polecenia , będzie można uruchomić dotnet restore
i dotnet build
na poziomie rozwiązania.