Расставим точки над .NET Core и .NET Standard

перевод
Schvepsss 17 октября в 11:56 19,8k
Оригинал: Immo Landwerth
.NET Core и .NET Standard — самые новые на сегодняшний день технологии .NET, поэтому совершенно неудивительно, что не только они, но и их отличия от платформы .NET Framework вызывают много вопросов. В этой статье я расскажу, что эти технологии из себя представляют и в каких случаях будет более логично выбрать каждую их.




Перед тем как углубляться в детали, обсудим среду .NET в целом и место, которое в ней занимают .NET Core и .NET Standard.

Платформа .NET Framework появилась 15 лет назад. Тогда в её состав входил только один стек технологий .NET, с помощью которого можно было разрабатывать приложения для ПК под управлением Windows и веб-приложения. С тех пор появились и другие реализации .NET, например, Xamarin для разработки мобильных приложений для iOS и Android и классических приложений для macOS.



Какое же место здесь занимают .NET Core и .NET Standard?


  • .NET Core — это самая новая реализация .NET. Это проект Open Source с версиями для нескольких ОС. .NET Core позволяет создавать кроссплатформенные консольные приложения, а также приложения и облачные службы ASP.NET Core.
  • .NET Standard — это набор базовых API (другое их название — BCL, библиотека базовых классов), которые должны поддерживаться во всех реализациях .NET. .NET Standard позволяет создавать библиотеки, подходящие для любых приложений .NET, вне зависимости от реализации .NET или операционной системы, в которой они выполняются.

.NET Core


.NET Core — это новая кроссплатформенная и полностью открытая реализация .NET. В её основе лежат технологии .NET Framework и Silverlight. Она оптимизирована для мобильных и серверных рабочих нагрузок, поскольку обеспечивает поддержку самодостаточных развёртываний XCOPY.

Чтобы лучше понять, что такое .NET Core, давайте попробуем разработать небольшое приложение для .NET Core. При этом мы познакомимся с новыми программами командной строки. Разрабатывать решения .NET Core можно в Visual Studio 2017, но раз уж вы читаете эту статью, я предположу, что с Visual Studio вы знакомы неплохо, и буду рассказывать в первую очередь о новых возможностях.

При создании .NET одним из главных приоритетов была высокая скорость разработки приложений для Windows. На практике это означает, что разработка .NET была неразрывно связана с Visual Studio. Безусловно, Visual Studio — замечательный инструмент. Он позволяет работать эффективно, а его отладчик — однозначно лучший из тех, которые мне доводилось использовать.

Но в некоторых случаях Visual Studio — не самый удобный вариант. Допустим, вы хотите поэкспериментировать с .NET, чтобы изучить C#. Для этого не хотелось бы скачивать и устанавливать IDE объемом в несколько гигабайт. Или, допустим, вы подключаетесь к компьютеру с Linux через SSH. Тогда работать через IDE вообще не получится. А может быть, вам просто нравится командная строка.

Для таких случаев создана отличная программа для работы в командной строке — .NET Core CLI. Основной компонент .NET Core CLI называется dotnet. Его можно использовать для решения практически любых задач разработки, в частности, для создания, сборки, тестирования и упаковки проектов. Давайте посмотрим, как это работает.

Для начала создадим и запустим консольное приложение Hello World (я буду использовать PowerShell для Windows, но в Bash для macOS или Linux все делается аналогично).

$ dotnet new console -o hello
$ cd hello
$ dotnet run
Hello World!

Команда dotnet new делает то же самое, что элемент меню File – New Project в Visual Studio. С её помощью можно создавать проекты различных типов. Используйте команду dotnet new, чтобы вывести список предустановленных шаблонов.

Давайте переместим часть логики в библиотеку классов. Для этого в дополнение к проекту hello создадим проект библиотеки классов.

$ cd ..
$ dotnet new library -o logic
$ cd logic

Здесь мы хотим описать логику, которая будет формировать сообщение Hello World. Поэтому изменим содержимое файла Class1.cs на следующее:

namespace logic
{
  public static class HelloWorld
  {
      public static string GetMessage(string name) => $"Hello {name}!";
  }
}

Переименуем файл Class1.cs в HelloWorld.cs.

$ mv Class1.cs HelloWorld.cs

Обратите внимание: обновлять файл проекта, чтобы отразить это изменение, не нужно. Новые файлы проекта в .NET Core уже включают все исходные файлы из каталога проекта. Поэтому при добавлении, удалении и переименовании файлов изменять проект больше не нужно. Это очень упрощает работу в командной строке.

Чтобы использовать класс HelloWorld, нужно добавить в приложение hello ссылку на библиотеку, в которой содержится логика. Для этого можно изменить файл проекта или воспользоваться командой dotnet add reference.

$ cd ../hello
$ dotnet add reference ../logic/logic.csproj

Теперь изменим файл Program.cs так, чтобы в нем использовался класс HelloWorld.

Обновление файла Program.cs для дальнейшего использования класса HelloWorld:

using System;
using logic;
namespace hello
{
class Program
{
static void Main(string[] args)
{
Console.Write("What's your name: ");
var name = Console.ReadLine();
var message = HelloWorld.GetMessage(name);
Console.WriteLine(message);
}
}
}

Чтобы собрать и запустить приложение, введите команду dotnet run.

$ dotnet run
What's your name: Immo
Hello Immo!

В командной строке также можно создавать тесты. Этот CLI поддерживает MSTest, а также популярную платформу xUnit. Давайте для примера воспользуемся xUnit.

$ cd ..
$ dotnet new xunit -o tests
$ cd tests
$ dotnet add reference ../logic/logic.csproj

Чтобы добавить тест, измените содержимое файла UnitTest1.cs, как показано ниже.

Добавление теста в файл UnitTest1.cs:

using System;
using Xunit;
using logic;
namespace tests
{
public class UnitTest1
{
[Fact]
public void Test1()
{
var expectedMessage = "Hello Immo!";
var actualMessage = HelloWorld.GetMessage("Immo");
Assert.Equal(expectedMessage, actualMessage);
}
}
}

Теперь можно запустить тесты с помощью команды dotnet test.

$ dotnet test
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.

Рассмотрим более интересный пример: создадим простой веб-сайт ASP.NET Core.

$ cd ..
$ dotnet new web -o web
$ cd web
$ dotnet add reference ../logic/logic.csproj

Измените вызов app.Run в файле Startup.cs так, чтобы в нём использовался класс HelloWorld.

app.Run(async (context) =>
{
  var name = Environment.UserName;
  var message = logic.HelloWorld.GetMessage(name);
  await context.Response.WriteAsync(message);
});

Чтобы запустить тестовый веб-сервер, вновь введите команду dotnet run.

$ dotnet run
Hosting environment: Production
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

Откройте в браузере URL-адрес, который был выведен в консоли (это должен быть адрес localhost:5000).

Сейчас структура вашего проекта должна соответствовать вот такой структуре.

Структура созданного проекта:
$ tree /f
│
├───hello
│ hello.csproj
│ Program.cs
│
├───logic
│ HelloWorld.cs
│ logic.csproj
│
├───tests
│ tests.csproj
│ UnitTest1.cs
│
└───web
Program.cs
Startup.cs
web.csproj

Чтобы упростить редактирование файлов в Visual Studio, создадим файл решения и добавим в него все проекты.

$ cd ..
$ dotnet new sln -n HelloWorld
$ ls -fi *.csproj -rec | % { dotnet sln add $_.FullName }

Как видите, .NET Core CLI — мощный и удобный инструмент, который покажется привычным даже тем разработчикам, которые никогда раньше не работали с .NET.

В этой статье мы использовали PowerShell в Windows, но в Linux и macOS все будет практически так же.

Ещё одно огромное преимущество .NET Core — поддержка самодостаточных развёртываний. Приложение можно поместить в контейнер Docker, содержащий собственную копию среды выполнения .NET Core. То есть благодаря контейнерам вы можете использовать различные версии .NET Core для запуска различных приложений на одном компьютере, и они не будут мешать друг другу. .NET Core — открытый продукт, поэтому вы можете пользоваться промежуточными сборками или даже версиями с модификациями, которые внесли вы сами. Но это уже совсем другая история.

.NET Standard


Современные приложения часто приходится адаптировать к устройствам различных типов, а значит и к различным реализациям .NET. Клиенты воспринимают как должное доступ к хранящимся в облаке данным через веб-приложение с мобильного телефона или веб-браузер с ноутбука. В собственной инфраструктуре вам, скорее всего, захочется пользоваться программами командной строки и позволить сотрудникам управлять системой с помощью классических приложений. В таблице ниже показано, для решения каких задач подходят различные реализации .NET.
ОС Open Source Назначение
.NET Framework Windows Нет Создание классических Windows-приложений и веб-приложений ASP.NET для IIS.
.NET Core Windows, Linux, macOS Да Создание кроссплатформенных консольных приложений, а также веб-приложений и облачных служб ASP.NET Core.
Xamarin iOS, Android, macOS Да Создание мобильных приложений для iOS и Android, классических приложений для macOS.
.NET Standard Да Создание библиотек, которые можно использовать в любых реализациях .NET, в том числе .NET Framework, .NET Core и Xamarin.

В такой среде поддержка общего кода становится сложной задачей. Нужно понять, какие API доступны, и убедиться, что общие компоненты работают только с теми API, которые поддерживаются во всех используемых реализациях .NET.

Для решения этой задачи и создан .NET Standard. По сути он представляет собой спецификацию. В каждой версии .NET Standard определен некоторый набор API. Все реализации .NET, соответствующие этой версии, должны поддерживать все эти API. Эту систему можно рассматривать как еще один стек .NET, для которого можно создавать только библиотеки (но не приложения). Именно эту реализацию .NET следует использовать при создании библиотек, которые будут применяться в различных системах.

Возможно, вы задаетесь вопросом, какие именно API входят в .NET Standard. Если вы знакомы с платформой .NET Framework, то¬, наверное, знаете про библиотеку BCL — мы ее уже упоминали. BCL — это набор базовых API, не зависящих от инфраструктур пользовательского интерфейса и моделей приложений. В него входят простые типы, файловый ввод-вывод, сетевые API, API сериализации, XML и другое.

Каждый стек .NET реализует определенную версию .NET Standard. Обычно каждая новая версия реализации .NET реализует самую последнюю (на этот момент) версию .NET Standard.

Хорошая аналогия — язык HTML и браузеры. Представьте, что спецификация HTML — это .NET Standard, а различные браузеры — это реализации .NET (например, .NET Framework, .NET Core и Xamarin).

Вероятно, вы уже задаетесь вопросом, как можно пользоваться .NET Standard. На самом деле мы им уже воспользовались, когда создавали библиотеку с классом логики. Давайте взглянем на файл проекта внимательнее.

$ cd logic
$ cat logic.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

Сравним его с файлом проекта консольного приложения hello.

$ cd ..\hello
$ cat hello.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\logic\logic.csproj" />
  </ItemGroup>

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

</Project>

Как видите, для параметра TargetFramework библиотеки логики задано значение netstandard2.0, а для соответствующего параметра консольного приложения — значение netcoreapp2.0. Параметр TargetFramework соответствует целевой версии реализации .NET. Таким образом, целевая реализация для консольного приложения — .NET Core 2.0, а для библиотеки — .NET Standard 2.0. Это значит, что к библиотеке логики можно обращаться не только из приложения .NET Core, но и из приложения для .NET Framework или Xamarin.

К сожалению, для большей части современных библиотек целевой реализацией является не .NET Standard, а .NET Framework. Конечно, .NET Standard в качестве целевой версии подходит не для всех библиотек, и это совершенно нормально. Например, целевой реализацией для библиотеки, в которой содержатся элементы управления Windows Presentation Foundation (WPF), должна быть .NET Framework, потому что компоненты пользовательского интерфейса не являются частью стандарта. Однако с большей частью библиотек общего назначения ситуация другая: для них целевой реализацией является .NET Framework просто потому, что на момент их создания .NET Standard еще не существовало.

В .NET Standard 2.0 содержится достаточно большой набор API для того, чтобы в подавляющей части библиотек общего назначения в качестве целевой реализации можно было использовать .NET Standard. В 70% библиотек, доступных сегодня в NuGet, используются только API, входящие в .NET Standard. Однако лишь немногие из них явным образом отмечены как совместимые с .NET Standard.

Чтобы разработчики могли использовать не только их, был добавлен режим совместимости. Если вы устанавливаете пакет NuGet, в котором нет библиотеки ни для вашей целевой платформы, ни для .NET Standard, то NuGet попробует использовать .NET Framework в качестве резервного варианта. Другими словами, вы можете обращаться к библиотекам .NET Framework так, как если бы для них в качестве целевой реализации был указан .NET Standard.

Давайте посмотрим, как это работает. В моем примере мы использовали популярную библиотеку коллекций PowerCollections, которая была создана в 2007 году. Она долго не обновлялась, и в качестве целевой платформы для нее по-прежнему указана .NET Framework 2.0. Добавим ее через NuGet в приложение hello.

$ dotnet add package Huitian.PowerCollections

Эта библиотека поддерживает дополнительные типы коллекций, которых нет в BCL. Один из них — тип Bag, не гарантирующий какого-либо порядка элементов. Изменим наше приложение hello так, чтобы в нем использовался этот тип.

Пример приложения с использованием PowerCollections:

using System;
using Wintellect.PowerCollections;
namespace hello
{
class Program
{
static void Main(string[] args)
{
var data = new Bag<int>() { 1, 2, 3 };
foreach (var element in data)
Console.WriteLine(element);
}
}
}

Если вы запустите программу, то увидите следующее:

$ dotnet run
hello.csproj : warning NU1701: Package 'Huitian.PowerCollections 1.0.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.0'. This may cause compatibility problems.
1
3
2

Что же произошло? Целевой реализацией для приложения hello является платформа .NET Core 2.0. .NET Core 2.0 является одной из реализаций .NET Standard 2.0, поэтому она поддерживает режим совместимости для обращения к библиотекам .NET Framework. Однако некоторые библиотеки .NET Framework в определенных реализациях .NET работать не будут. Например, это библиотеки, в которых используются API Windows Forms или WPF. NuGet не может об этом знать, поэтому он выдает сообщение с предупреждением о возможных проблемах.

Обратите внимание: это уведомление будет выводиться при каждой сборке. Иначе вы могли бы пропустить его при установке пакета или просто забыть о нем.

Конечно, неустранимые предупреждения, которые выводятся при каждой сборке, не могут не раздражать. Поэтому после проверки приложения вы можете отключить предупреждение, связанное с конкретным пакетом. Наше приложение работает правильно (корректно выводит содержимое созданной коллекции типа Bag), поэтому сейчас мы отключим это сообщение. Для этого изменим файл hello.csproj и добавим атрибут NoWarn в ссылку на пакет.

<PackageReference Include="Huitian.PowerCollections" Version="1.0.0" 
  NoWarn="NU1701" />

Если вы снова запустите приложение, предупреждения уже не будет. Однако, если вы установите другой пакет, в котором используется режим совместимости, появятся новые предупреждения. При необходимости их тоже можно будет отключить.

Еще новый набор инструментов позволяет создавать пакеты NuGet в процессе сборки проектов — библиотек классов. Таким образом можно легко предоставить доступ к библиотекам неограниченному числу пользователей (достаточно просто выложить их на nuget.org) или только сотрудникам вашей организации (для этого добавьте их в вашу ленту пакетов в Visual Studio Team Services или в MyGet).

В новых проектах поддерживается возможность задать несколько целевых платформ и выполнить сборку одного проекта для нескольких реализаций .NET. Это значит, что вы можете адаптировать библиотеку к конкретным реализациям .NET с помощью механизма условной компиляции (#if). Также вы можете создавать оболочки .NET Standard для API, относящихся к конкретным платформам. Но это уже совсем другая история.

Заключение


.NET Standard представляет собой спецификацию API, которые должны содержаться во всех реализациях .NET. Он делает семейство технологий .NET более организованным и позволяет разработчикам создавать библиотеки, которые можно использовать в любой реализации .NET. Этот стандарт заменяет библиотеки PCL в роли механизма создания общих компонентов.

.NET Core — реализация .NET Standard, оптимизированная для создания консольных приложений, веб-приложений и облачных служб с использованием ASP.NET Core. В состав соответствующего SDK входит несколько мощных инструментов, которые дополняют возможности Visual Studio, позволяя решать задачи разработки с помощью командной строки.

Подробная информация доступна на GitHub .NET Standard FAQ и сайте .NET and C# — Get Started in 10 Minutes.


P.S. Спасибо Косте Кичинскому (Quantum Quintum) за иллюстрацию к этой статье.
Проголосовать:
+26
Сохранить: