Поделиться через


Разработка библиотек с помощью интерфейса командной строки .NET

В этой статье описывается, как создавать библиотеки для .NET с помощью интерфейса командной строки .NET. CLI предоставляет эффективный и низкоуровневый интерфейс, работающий в любых поддерживаемых операционных системах. Вы по-прежнему можете создавать библиотеки с Visual Studio, и если это ваш предпочтительный опыт обратитесь к руководству по Visual Studio.

Предпосылки

На вашем компьютере должен быть установлен .NET SDK.

В разделах этого документа, посвященном версиям .NET Framework, необходимо установить .NET Framework на компьютере Windows.

Кроме того, если вы хотите поддерживать более старые целевые объекты платформы .NET Framework, необходимо установить целевые пакеты или пакеты разработчиков на странице загрузки .NET Framework downloads. См. таблицу ниже.

версия .NET Framework Что скачать
4.6.1 Пакет для целевых платформ .NET Framework 4.6.1
4,6 пакет целевого объекта .NET Framework 4.6
4.5.2 пакет разработчика .NET Framework 4.5.2
4.5.1 пакет разработчика .NET Framework 4.5.1
4,5 пакет средств разработки программного обеспечения Windows для Windows 8
4.0 пакет SDK Windows для 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, используемые здесь, больше не поддерживаются. Дополнительные сведения о неподдерживаемых версиях см. в разделе Часто задаваемые вопросы по политике жизненного цикла поддержки .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

Вставьте этот TFM в раздел 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. В такой ситуации, если вам нужно применять более новые интерфейсы 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>

Вы заметите три основных изменения:

  1. Узел TargetFramework был заменен на TargetFrameworks, внутри которого содержатся три моникера целевой платформы.
  2. Существует узел <ItemGroup> для цели net40, подключающий одну ссылку на .NET Framework.
  3. Существует узел <ItemGroup> для целевого объекта net45, который извлекается из двух ссылок .NET Framework.

Символы препроцессора

Система сборки распознает следующие символы препроцессора, используемые в директивах #if:

Требуемые версии .NET Framework Символы Дополнительные символы
(доступно в SDK для .NET 5+)
Символы платформы (доступны только
при указании TFM для конкретной ОС)
платформа .NET NETFRAMEWORK,NET481,NET48,NET472,NET471,NET47,NET462,NET461,NET46,NET452,NET451,NET45,NET40,NET35,NET20 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_0, NETSTANDARD1_6, NETSTANDARD1_5, NETSTANDARD1_4, NETSTANDARD1_3, NETSTANDARD1_2, NETSTANDARD1_1, NETSTANDARD1_0 NETSTANDARD2_1_OR_GREATER, NETSTANDARD2_0_OR_GREATERNETSTANDARD1_6_OR_GREATERNETSTANDARD1_5_OR_GREATERNETSTANDARD1_4_OR_GREATERNETSTANDARD1_3_OR_GREATERNETSTANDARD1_2_OR_GREATERNETSTANDARD1_1_OR_GREATERNETSTANDARD1_0_OR_GREATER
.NET 5+ (и .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_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, IOSMACCATALYSTMACOSTVOSWINDOWS
[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 категории "Стандартный", а не для целевых объектов, реализующих .NET Standard, таких как .NET Core и .NET Framework.
  • Они отличаются от моникеров целевой платформы (TFM), используемых свойством 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 CLI. Дополнительные сведения см. в разделах dotnet new и dotnet sln.

  1. Настройте решение. Это можно сделать с помощью следующих команд:

    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/
    
  2. Перейдите в каталог тестового проекта и добавьте ссылку на MyProject.Test из MyProject.

    cd MyProject.Test
    dotnet reference add ../MyProject/MyProject.csproj
    
  3. Восстановите пакеты и соберите проекты:

    dotnet restore
    dotnet build
    
  4. Убедитесь, что xUnit запущен, выполнив команду dotnet test. Если вы решили использовать MSTest, то вместо этого должно запускаться средство запуска консоли MSTest.

Вот и все! Теперь вы можете протестировать библиотеку для всех платформ с помощью средств командной строки. Теперь, когда все настроено, протестировать библиотеку очень легко:

  1. Внесите изменения в библиотеку.
  2. Выполните тесты в тестовом каталоге из командной строки с помощью команды 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 reference add ../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 на уровне проекта.