Разработка библиотек с помощью .NET CLI
В этой статье показано, как создавать библиотеки для .NET с помощью .NET CLI. CLI предоставляет эффективный и низкоуровневый интерфейс, работающий в любых поддерживаемых операционных системах. Вы по-прежнему можете создавать библиотеки с помощью Visual Studio. Если вы предпочитаете такой способ, обратитесь к руководству по Visual Studio.
Необходимые компоненты
На компьютере должен быть установлен пакет SDK для .NET.
При работе с разделами, в которых используются различные версии .NET Framework, на компьютере с ОС Windows должна быть установлена платформа .NET Framework.
Кроме того, если необходимо поддерживать целевые платформы .NET Framework предыдущих версий, требуется установить целевые пакеты или пакеты разработчиков со страницы с доступными для скачивания файлами для .NET Framework См. таблицу ниже.
Версия платформы .NET Framework | Скачиваемые компоненты |
---|---|
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 для Windows 8 |
4.0 | Пакет SDK для Windows 7 и .NET Framework 4 |
2.0, 3.0 и 3.5 | Среда выполнения .NET Framework 3.5 с пакетом обновления 1 (SP1) (либо версия для Windows 8 или более поздняя) |
Нацеливание на .NET 5+ или .NET Standard
Вы можете управлять целевой платформой проекта, добавив его в файл проекта (CSPROJ или FSPROJ). Инструкции по выбору между целевой платформой .NET 5+ и .NET Standard см. в разделе .NET 5+ и .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>
Если требуется нацеливание на .NET Framework версии 4.0 или более ранней или использование интерфейса API, доступного в .NET Framework, но не в .NET Standard (например, System.Drawing
), прочитайте следующие подразделы, чтобы узнать, как осуществляется настройка для разных версий.
Нацеливание на .NET Framework.
Примечание.
В этих инструкциях предполагается, что на компьютере установлена платформа .NET Framework. Чтобы установить зависимости, обратитесь к разделу Предварительные требования.
Имейте в виду, что некоторые используемые здесь версии .NET Framework больше не поддерживаются. Сведения о неподдерживаемых версиях см. в статье Вопросы и ответы о политике по срокам поддержки Microsoft .NET Framework.
Чтобы охватить максимальное количество разработчиков и проектов, используйте .NET Framework 4.0 в качестве базовой целевой платформы. Для нацеливания на .NET Framework начните использовать правильный моникер целевой платформы (TFM), соответствующий версии .NET Framework, которая должна поддерживаться.
Версия платформы .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 |
Вставьте этот моникер целевой платформы в раздел TargetFramework
файла проекта. Например, вот как создать библиотеку, предназначенную для .NET Framework 4.0:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net40</TargetFramework>
</PropertyGroup>
</Project>
Вот и все! Хотя эта библиотека компилируется только для .NET Framework 4, ее можно использовать в более поздних версиях .NET Framework.
Настройка для различных версий
Примечание.
В приведенных ниже инструкциях предполагается, что на компьютере установлена платформа .NET Framework. Сведения о зависимостях, которые необходимо установить, и о том, где их можно скачать, см. в разделе Предварительные требования.
Если проект поддерживает как .NET Framework, так и .NET, может потребоваться нацеливание на более старые версии .NET Framework. В такой ситуации, если вам нужно применять более новые интерфейсы API и языковые конструкции для новых целевых платформ, используйте директивы #if
в коде. Кроме того, может потребоваться добавить разные пакеты и зависимости для каждой целевой платформы, чтобы включить различные интерфейсы API, необходимые в каждом случае.
Предположим, имеется библиотека, выполняющая сетевые операции по протоколу HTTP. Для .NET Standard и .NET Framework версии 4.5 или более поздней можно использовать класс HttpClient
из пространства имен System.Net.Http
. Однако в более ранних версиях .NET Framework нет класса HttpClient
, поэтому вместо него можно использовать класс WebClient
из пространства имен System.Net
.
Файл проекта может выглядеть следующим образом:
<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>
Вы заметите три основных изменения:
- Узел
TargetFramework
был заменен наTargetFrameworks
, внутри которого содержатся три моникера целевой платформы. - Добавлен узел
<ItemGroup>
для целевой платформыnet40
, который извлекает одну ссылку на .NET Framework. - Добавлен узел
<ItemGroup>
для целевой платформыnet45
, который извлекает две ссылки на .NET Framework.
Символы препроцессора
Система сборки распознает следующие символы препроцессора, используемые в директивах #if
:
Требуемые версии .NET Framework | Символы | Дополнительные символы (доступно в пакетах SDK для .NET 5 и более поздних версий) |
Символы платформы (доступны только при указании TFM для конкретной ОС) |
---|---|---|---|
.NET Framework | NETFRAMEWORK NET48 , NET481 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_GREATER NETSTANDARD1_5_OR_GREATER NETSTANDARD1_4_OR_GREATER NETSTANDARD1_3_OR_GREATER NETSTANDARD1_2_OR_GREATER NETSTANDARD1_1_OR_GREATER NETSTANDARD1_0_OR_GREATER |
|
.NET 5+ (и .NET Core) | NET , 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 |
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] (например IOS15_1 ),[OS][version]_OR_GREATER (например IOS15_1_OR_GREATER ) |
Примечание.
- Символы без привязки к версии определены независимо от версии, которую вы хотите использовать в качестве целевой.
- Символы для определенных версий определены только для тех версий, которые вы хотите использовать в качестве целевых.
- Символы
<framework>_OR_GREATER
определены для версии, которую вы хотите использовать в качестве целевой, и всех более ранних версий. Например, если вы выбрали .NET Framework 2.0, определяются следующие символы:NET20
,NET20_OR_GREATER
,NET11_OR_GREATER
иNET10_OR_GREATER
. NETSTANDARD<x>_<y>_OR_GREATER
Символы определяются только для целевых объектов .NET Standard, а не для целевых объектов, реализующих .NET Standard, таких как .NET Core и платформа .NET Framework.- Они отличаются от моникеров целевой платформы (TFM), используемых свойством MSBuild
TargetFramework
и NuGet.
Ниже приведен пример использования условной компиляции для каждого целевого объекта:
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
}
}
При сборке этого проекта с dotnet build
вы увидите, что в папке bin/
появились три каталога:
net40/
net45/
netstandard2.0/
Каждый из них содержит файлы .dll
для соответствующего целевого объекта.
Тестирование библиотек в .NET
Необходимо иметь возможность тестирования проектов на различных платформах. Вы можете использовать xUnit или MSTest без дополнительной настройки. Обе платформы тестирования идеально подходят для модульного тестирования библиотеки в .NET. Настройка тестовых проектов для решения зависит от его структуры. В следующем примере предполагается, что каталог с тестами и каталог с исходным кодом находятся в одном и том же каталоге верхнего уровня.
Примечание.
В этом примере используются некоторые команды интерфейса командной строки .NET. Дополнительные сведения см. в разделах dotnet new и dotnet sln.
Настройте решение. Это можно сделать с помощью следующих команд:
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
Эти команды создадут проекты и объединят их в решение. Ваш каталог для
SolutionWithSrcAndTest
должен выглядеть следующим образом:/SolutionWithSrcAndTest |__SolutionWithSrcAndTest.sln |__MyProject/ |__MyProject.Test/
Перейдите в каталог тестового проекта и добавьте ссылку на
MyProject.Test
изMyProject
.cd MyProject.Test dotnet add reference ../MyProject/MyProject.csproj
Восстановите пакеты и соберите проекты:
dotnet restore dotnet build
Убедитесь, что xUnit запущен, выполнив команду
dotnet test
. Если вы решили использовать MSTest, запустите средство запуска консоли MSTest вместо xUnit.
Вот и все! Теперь вы можете протестировать библиотеку для всех платформ с помощью средств командной строки. Теперь, когда все настроено, протестировать библиотеку очень легко:
- Внесите изменения в библиотеку.
- Выполните тесты в тестовом каталоге из командной строки с помощью команды
dotnet test
.
При вызове команды dotnet test
будет автоматически выполнена повторная сборка кода.
Использование нескольких проектов
В случае с более крупными библиотеками, как правило, требуется реализовывать функциональность в разных проектах.
Представим, что необходимо создать библиотеку, которую можно использовать в идиоматичном коде на языках C# и F#. Это означает, что библиотека будет использоваться способами, естественными для языков C# и F#. Например, в C# можно использовать библиотеку следующим образом:
using AwesomeLibrary.CSharp;
public Task DoThings(Data data)
{
var convertResult = await AwesomeLibrary.ConvertAsync(data);
var result = AwesomeLibrary.Process(convertResult);
// do something with result
}
В F# это может выглядеть так.
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
}
Подобные сценарии использования предполагают, что интерфейсы API, к которым осуществляется доступ, должны иметь разную структуру для C# и F#. Стандартным подходом к решению этой задачи является факторинг всей логики библиотеки в базовом проекте и определение в проектах C# и F# уровней API, которые вызывают этот базовый проект. Далее в этом разделе будут использоваться следующие имена:
- AwesomeLibrary.Core — базовый проект, содержащий всю логику библиотеки;
- AwesomeLibrary.CSharp — проект с открытыми интерфейсами API, предназначенными для использования в коде на языке C#
- AwesomeLibrary.FSharp — проект с открытыми интерфейсами API, предназначенными для использования в коде на языке F#
Чтобы получить ту же структуру каталогов, что и в этом руководстве, выполните следующие команды в окне терминала:
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
Эти команды добавят три указанные выше проекта и файл решения, который их связывает. Создание файла решения и связывание проектов позволит создавать и восстанавливать проекты из верхнего уровня.
Ссылки проектов на проекты
Ссылку на проект лучше всего добавить с помощью интерфейса командной строки .NET. Из каталогов проекта AwesomeLibrary.CSharp и AwesomeLibrary.FSharp выполните следующую команду:
dotnet add reference ../AwesomeLibrary.Core/AwesomeLibrary.Core.csproj
Теперь файлы AwesomeLibrary.CSharp и AwesomeLibrary.FSharp будут ссылаться на AwesomeLibrary.Core в качестве целевого объекта ProjectReference
. Чтобы это проверить, просмотрите файлы проектов, и вы увидите в них следующий код:
<ItemGroup>
<ProjectReference Include="..\AwesomeLibrary.Core\AwesomeLibrary.Core.csproj" />
</ItemGroup>
Если вы не хотите использовать интерфейс командной строки .NET, можете добавить этот код в каждый файл проекта вручную.
Структурирование решения
Еще один важный аспект решений с несколькими проектами — правильное формирование общей структуры. Код можно упорядочить так, как вам удобно. Если каждый проект связан с файлом решения с помощью dotnet sln add
, вы сможете запускать команды dotnet restore
и dotnet build
на уровне проекта.