Часть I Язык программирования C# и платформа .NET 5

Глава 1 Введение в C# и .NET (Core) 5

Платформа Microsoft .NET и язык программирования C# впервые были представлены приблизительно в 2002 году и быстро стали главной опорой современной индустрии разработки программного обеспечения. Платформа .NET позволяет большому числу языков программирования (включая С#, VB.NET и F#) взаимодействовать друг с другом. На программу, написанную на С#, может ссылаться другая программа, написанная на VB.NET. Такая способность к взаимодействию более подробно обсуждается позже в главе.

В 2016 году компания Microsoft официально выпустила инфраструктуру .NET Core. Подобно .NET инфраструктура .NET Core позволяет языкам взаимодействовать друг с другом (хотя поддерживает ограниченное количество языков). Что более важно, новая инфраструктура способна функционировать не только под управлением операционной системы Windows, но также может запускаться (и позволять разрабатывать приложения) в средах iOS и Linux. Такая независимость от платформы открыла язык C# для гораздо большего числа разработчиков. Несмотря на то что межплатформенное использование C# поддерживалось и до выхода .NET Core, это делалось через ряд других инфраструктур, таких как проект

Mono
.


На заметку! Возможно, вас заинтересовало наличие круглых скобок в названии главы. С выходом .NET 5 часть "Core" в имени была отброшена с целью указания на то, что эта версия является унификацией всей платформы .NET. Но все же ради ясности повсюду в книге будут применяться термины .NET Core и .NET Framework.


10 ноября 2020 года компания Microsoft выпустила C# 9 и .NET 5. Как и C# 8, версия C# 9 привязана к определенной версии инфраструктуры и будет функционировать только под управлением .NET 5.0 и последующих версий. Привязка версии языка к версии инфраструктуры давала команде разработчиков C# свободу в плане ввода новых средств в С#, которые в противном случае не удалось бы добавить из-за ограничений инфраструктуры.

Во введении книги отмечалось, что при ее написании преследовались две цели. Первая из них — предоставление читателям глубокого и подробного описания синтаксиса и семантики языка С#. Вторая (не менее важная) цель — иллюстрация использования многочисленных API-интерфейсов .NET Core. В перечень рассматриваемых тем входят доступ к базам данных с помощью ADO.NET и Entity Framework (EF) Core, построение пользовательских интерфейсов посредством Windows Presentation Foundation (WPF), а также создание веб-служб REST и веб-приложений с применением ASP.NET Core. Как говорят, пеший поход длиной тысячу километров начинается с первого шага, который и будет сделан в настоящей главе.

Первая глава закладывает концептуальную основу для успешного освоения остального материала книги. Здесь вы найдете высокоуровневое обсуждение нескольких связанных с .NET тем, таких как сборки, общий промежуточный язык (Common Intermediate Language — CIL) и оперативная (Just-In-Time — JIT) компиляция. В дополнение к предварительному обзору ряда ключевых слов C# вы узнаете о взаимоотношениях между разнообразными компонентами .NET Core. Сюда входит исполняющая среда .NET Runtime, которая объединяет общеязыковую исполняющую среду .NET Core (.NET Core Common Language Runtime — CoreCLR) и библиотеки .NET Core (.NET Core Libraries — CoreFX) в единую кодовую базу, общая система типов (Common Type System — CTS), общеязыковая спецификация (Common Language Specification — CLS) и .NET Standard.

Кроме того, в главе представлен обзор функциональности, поставляемой в библиотеках базовых классов .NET Core, для обозначения которых иногда применяется аббревиатура BCL (base class library — библиотека базовых классов). Вы кратко ознакомитесь с независимой от языка и платформы природой .NET Core. Как несложно догадаться, многие затронутые здесь темы будут более детально исследоваться в оставшихся главах книги.


На заметку! Многие средства, рассматриваемые в настоящей главе (и повсюду в книге), также присутствуют в первоначальной инфраструктуре .NET Framework. В этой книге всегда будут использоваться термины "инфраструктура .NET Core" и "исполняющая среда .NET Core", а не общий термин ". NET", чтобы четко указывать, какие средства поддерживаются в .NET Core.

Некоторые основные преимущества инфраструктуры .NET Core

Инфраструктура .NET Core представляет собой программную платформу для построения веб-приложений и систем на основе служб, функционирующих под управлением операционных систем Windows, iOS и Linux, а также приложений Windows Forms и WPF для Windows. Ниже приведен краткий перечень основных средств, предлагаемых .NET Core.

Возможность взаимодействия с существующим кодом. Несомненно, это очень полезно. Существующее программное обеспечение .NET Framework может взаимодействовать с более новым программным обеспечением .NET Core. Обратное взаимодействие тоже возможно через .NET Standard.

Поддержка многочисленных языков программирования. Приложения .NET Core могут создаваться с использованием языков программирования С#, F# и VB.NET (при этом C# и F# являются основными языками для ASP.NET Core).

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

Языковая интеграция. В .NET Core поддерживается межъязыковое наследование, межъязыковая обработка исключений и межъязыковая отладка кода. Например, можно определить базовый класс в C# и расширить этот тип в VB.NET.

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

Упрощенная модель развертывания. Библиотеки .NET Core не регистрируются в системном реестре. Более того, платформа .NET Core позволяет нескольким версиям инфраструктуры и приложения гармонично сосуществовать на одном компьютере.

Всесторонняя поддержка командной строки. Интерфейс командной строки .NET Core (command-line interface — CLI) является межплатформенной цепочкой инструментов для разработки и пакетирования приложений .NET Core. Помимо стандартных инструментов, поставляемых в составе .NET Core SDK, могут быть установлены дополнительные инструменты.


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

Понятие жизненного цикла поддержки .NET Core

Версии .NET Core выходят гораздо чаще, нежели версии .NET Framework . Из-за обилия доступных выпусков может быть трудно не отставать, особенно в корпоративной среде разработки. Чтобы лучше определить жизненный цикл поддержки для выпусков, компания Microsoft приняла вариацию модели долгосрочной поддержки (Long-Term Support — LTS)[1], обычно применяемой современными инфраструктурами с открытым кодом.

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

• три года после первоначального выпуска;

• один год технической поддержки после следующего выпуска LTS.


В Microsoft решили именовать выпуски LTS как Current (текущие), которые являются промежуточными выпусками между крупными выпусками LTS. Они поддерживаются на протяжении трех месяцев после следующего выпуска Current или LTS.

Как упоминалось ранее, версия .NET 5 вышла 10 ноября 2020 года. Она была выпущена как версия Current, а не LTS. Это значит, что поддержка .NET 5 прекратится через три месяца после выхода следующего выпуска. Версия .NET Core 3.1, выпущенная в декабре 2019 года, представляет собой версию LTS и полноценно поддерживается вплоть до 3 декабря 2022 года.


На заметку! Следующим запланированным выпуском .NET будет версия .NET 6, которая по графику должна появиться в ноябре 2021 года. В итоге получается примерно 15 месяцев поддержки.NET 5.Однако если в Microsoft решат выпустить исправления (скажем, .NET 5.1), тогда трехмесячный срок начнется с этого выпуска. Мы рекомендуем обдумать такую политику поддержки, когда вы будете выбирать версию для разработки производственных приложений. Важно понимать: речь не идет о том, что вы не должны использовать .NET 5. Мы всего лишь настоятельно советуем надлежащим образом разобраться в политике поддержки при выборе версий .NET (Core) для разработки производственных приложений.


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

https://dotnet.microsoft.com/platform/support-policy/dotnet-core
.

Предварительный обзор строительных блоков .NET Core (.NET Runtime, CTS и CLS)

Теперь, когда вы узнали кое-что об основных преимуществах, присущих .NET Core, давайте ознакомимся с ключевыми (и взаимосвязанными) компонентами, которые делают возможным все упомянутое ранее — Core Runtime (формально CoreCLR и CoreFX), CTS и CLS. С точки зрения программиста приложений платформу .NET Core можно воспринимать как исполняющую среду и обширную библиотеку базовых классов. Уровень исполняющей среды содержит набор минимальных реализаций, которые привязаны к конкретным платформам (Windows, iOS, Linux) и архитектурам (х86, х64, ARM), а также все базовые типы для .NET Core.

Еще одним строительным блоком .NET Core является общая система типов (CTS). Спецификация CTS полностью описывает все возможные типы данных и все программные конструкции, поддерживаемые исполняющей средой, указывает, каким образом эти сущности могут взаимодействовать друг с другом, и как они представлены в формате метаданных .NET Core (дополнительную информацию о метаданных ищите далее в главе, а исчерпывающие сведения — в главе 17).

Важно понимать, что отдельно взятый язык .NET Core может не поддерживать абсолютно все функциональные средства, определяемые спецификацией CTS. Существует родственная общеязыковая спецификация (CLS), где описано подмножество общих типов и программных конструкций, которое должны поддерживать все языки программирования .NET Core. Таким образом, если вы строите типы .NET Core, открывающие доступ только к совместимым с CLS средствам, то можете быть уверены в том, что их смогут потреблять все языки .NET Core. И наоборот, если вы применяете тип данных или программную конструкцию, которая выходит за границы CLS, тогда не сможете гарантировать, что каждый язык программирования .NET Core окажется способным взаимодействовать с вашей библиотекой кода .NET Core. К счастью, как вы увидите далее в главе, компилятору C# довольно просто сообщить о необходимости проверки всего кода на предмет совместимости с CLS.

Роль библиотек базовых классов

Инфраструктура .NET Core также предоставляет набор библиотек базовых классов (BCL), которые доступны всем языкам программирования .NET Core. Библиотеки базовых классов не только инкапсулируют разнообразные примитивы вроде потоков, файлового ввода-вывода, систем визуализации графики и механизмов взаимодействия с разнообразными внешними устройствами, но вдобавок обеспечивают поддержку для многочисленных служб, требуемых большинством реальных приложений.

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

Роль .NET Standard

Даже с учетом выхода .NET 5.0 количество библиотек базовых классов в .NET Framework намного превышает количество библиотек подобного рода в .NET Core. Учитывая 14-летнее преимущество .NET Framework над .NET Core, ситуация вполне объяснима. Такое несоответствие создает проблемы при попытке использования кода .NET Framework с кодом .NET Core. Решением (и требованием) для взаимодействия .NET Framework/. NET Core является стандарт .NET Standard.

.NET Standard — это спецификация, определяющая доступность API-интерфейсов .NET и библиотек базовых классов, которые должны присутствовать в каждой реализации. Стандарт обладает следующими характеристиками:

• определяет унифицированный набор API-интерфейсов BCL для всех реализаций .NET, которые должны быть созданы независимо от рабочей нагрузки;

• позволяет разработчикам производить переносимые библиотеки, пригодные для потребления во всех реализациях .NET, с использованием одного и того же набора API-интерфейсов;

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


В таблице, приведенной в документации от Microsoft (

https://docs.microsoft.com/ru-ru/dotnet/standard/net-standard
), указаны минимальные версии реализаций, которые поддерживают каждый стандарт .NET Standard. Она полезна в случае применения предшествующих версий С#. Тем не менее, версия C# 9 будет функционировать только в среде .NET 5.0 (или выше) либо .NET Standard 2.1, а стандарт .NET Standard 2.1 не является доступным для .NET Framework.

Что привносит язык C#

Синтаксис языка программирования C# выглядит очень похожим на синтаксис языка Java. Однако называть C# клоном Java неправильно. В действительности и С#, и Java являются членами семейства языков программирования, основанных на С (например, С, Objective-C, C++), поэтому они разделяют сходный синтаксис.

Правда заключается в том, что многие синтаксические конструкции C# смоделированы в соответствии с разнообразными аспектами языков VB и C++. Например, подобно VB язык C# поддерживает понятия свойств класса (как противоположность традиционным методам извлечения и установки) и необязательных параметров. Подобно C++ язык C# позволяет перегружать операции, а также создавать структуры, перечисления и функции обратного вызова (посредством делегатов).

Более того, по мере проработки материала книги вы очень скоро заметите, что C# поддерживает средства, такие как лямбда-выражения и анонимные типы, которые традиционно встречаются в различных языках функционального программирования (например, LISP или Haskell). Вдобавок с появлением технологии LINQ (Language Integrated Query — язык интегрированных запросов) язык C# стал поддерживать конструкции, которые делают его довольно-таки уникальным в мире программирования. Но, несмотря на все это, наибольшее влияние на него оказали именно языки, основанные на С.

Поскольку C# — гибрид из нескольких языков, он является таким же синтаксически чистым, как Java (если не чище), почти настолько же простым, как VB, и практически таким же мощным и гибким, как C++. Ниже приведен неполный перечень ключевых особенностей языка С#, которые характерны для всех его версий.

• Указатели необязательны ! В программах на C# обычно не возникает потребности в прямых манипуляциях указателями (хотя в случае абсолютной необходимости можно опуститься и на уровень указателей, как объясняется в главе 11).

• Автоматическое управление памятью посредством сборки мусора. С учетом этого в C# не поддерживается ключевое слово вроде

delete
.

• Формальные синтаксические конструкции для классов, интерфейсов, структур, перечислений и делегатов.

• Аналогичная языку C++ возможность перегрузки операций для специальных типов без особой сложности.

• Поддержка программирования на основе атрибутов. Разработка такого вида позволяет аннотировать типы и их члены для дополнительного уточнения их поведения. Например, если пометить метод атрибутом

[Obsolete]
, то при попытке его использования программисты увидят ваше специальное предупреждение.


C# 9 уже является мощным языком, который в сочетании с .NET Core позволяет строить широкий спектр приложений разнообразных видов.

Основные средства в предшествующих выпусках

С выходом версии .NET 2.0 (примерно в 2005 году) язык программирования C# был обновлен с целью поддержки многочисленных новых функциональных возможностей, наиболее значимые из которых перечислены далее.

• Возможность создания обобщенных типов и обобщенных членов. Применяя обобщения, можно писать очень эффективный и безопасный к типам код, который определяет множество заполнителей, указываемых во время взаимодействия с обобщенными элементами.

• Поддержка анонимных методов, которые позволяют предоставлять встраиваемую функцию везде, где требуется тип делегата.

• Возможность определения одиночного типа в нескольких файлах кода (или при необходимости в виде представления в памяти) с использованием ключевого слова

partial
.


В версии .NET 3.5 (вышедшей приблизительно в 2008 году) к языку программирования C# была добавлена дополнительная функциональность, в том числе следующие средства.

• Поддержка строго типизированных запросов (например, LINQ), применяемых для взаимодействия с разнообразными формами данных. Вы впервые встретите запросы LINQ в главе 13.

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

• Возможность расширения функциональности существующего типа (не создавая его подклассы) с использованием расширяющих методов.

• Включение лямбда-операции (

=>
), которая еще больше упрощает работу с типами делегатов .NET.

• Новый синтаксис инициализации объектов, позволяющий устанавливать значения свойств во время создания объекта.


В версии .NET 4.0 (выпущенной в 2010 году) язык C# снова был дополнен рядом средств, которые указаны ниже.

• Поддержка необязательных параметров и именованных аргументов в методах.

• Поддержка динамического поиска членов во время выполнения через ключевое слово

dynamic
. Как будет показано в главе 19, это обеспечивает универсальный подход к вызову членов на лету независимо от инфраструктуры, в которой они реализованы (COM, IronRuby, IronPython или службы рефлексии .NET).

• Работа с обобщенными типами стала намного понятнее, учитывая возможность легкого отображения обобщенных данных на универсальные коллекции

System.Object
через ковариантность и контравариантность.


В выпуске .NET 4.5 язык C# обрел пару новых ключевых слов (

async
и
await
), которые значительно упрощают многопоточное и асинхронное программирование. Если вы работали с предшествующими версиями С#, то можете вспомнить, что вызов методов через вторичные потоки требовал довольно большого объема малопонятного кода и применения разнообразных пространств имен .NET. Учитывая то, что теперь в C# поддерживаются языковые ключевые слова, которые автоматически устраняют эту сложность, процесс вызова методов асинхронным образом оказывается почти настолько же легким, как их вызов в синхронной манере. Данные темы детально раскрываются в главе 15.

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

• Встраиваемая инициализация для автоматических свойств, а также поддержка автоматических свойств, предназначенных только для чтения.

• Реализация однострочных методов с использованием лямбда-операции С#.

• Поддержка статического импортирования для предоставления прямого доступа к статическим членам внутри пространства имен.

null
-условная операция, которая помогает проверять параметры на предмет
null
в реализации метода.

• Новый синтаксис форматирования строк, называемый интерполяцией строк.

• Возможность фильтрации исключений с применением нового ключевого слова

when
.

• Использование

await
в блоках
catch
и
finally
.

• Выражения

nameof
для возвращения строкового представления символов.

• Инициализаторы индексов.

• Улучшенное распознавание перегруженных версий.


В версии C# 7, выпущенной вместе с .NET 4.7 в марте 2017 года, были введены дополнительные средства для упрощения кодовой базы и добавлено несколько более значительных средств (вроде кортежей и ссылочных локальных переменных, а также возвращаемых ссылочных значений), которые разработчики просили включить довольно долгое время. Вот краткий обзор новых средств C# 7.

• Объявление переменных

out
как встраиваемых аргументов.

• Локальные функции.

• Дополнительные члены, сжатые до выражений.

• Обобщенные асинхронные возвращаемые типы.

• Новые маркеры для улучшения читабельности числовых констант.

• Легковесные неименованные типы (называемые кортежами), которые содержат множество полей.

• Обновления логического потока с применением сопоставления с типом вдобавок к проверке значений (сопоставлению с образцом).

• Возвращение ссылки на значение вместо только самого значения (ссылочные локальные переменные и возвращаемые ссылочные значения).

• Введение легковесных одноразовых переменных (называется отбрасыванием).

• Выражения

throw
, позволяющие размещать конструкцию
throw
в большем числе мест — в условных выражениях, лямбда-выражениях и др.


С версией C# 7 связаны два младших выпуска, которые добавили следующие средства.

• Возможность иметь асинхронный метод

Main()
программы.

• Новый литерал

default
, который делает возможной инициализацию любого типа.

• Устранение проблемы при сопоставлении с образцом, которая препятствовала использованию обобщений в этом новом средстве сопоставления с образцом.

• Подобно анонимным методам имена кортежей могут выводиться из проекции, которая их создает.

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

• За именованными аргументами могут следовать позиционные аргументы.

• Числовые литералы теперь могут иметь ведущие символы подчеркивания перед любыми печатаемыми цифрами.

• Модификатор доступа

private protected
делает возможным доступ для производных классов в той же самой сборке.

• Результатом условного выражения (

?:
) теперь может быть ссылка.


Кроме того, в этом издании книги к заголовкам разделов добавляются указания "(нововведение в версии 7.x)" и "(обновление в версии 7.x)", чтобы облегчить поиск изменений в языке по сравнению с предыдущей версией. Буква "х" означает младшую версию C# 7, такую как 7.1.

В версии C# 8, ставшей доступной 23 сентября 2019 года в рамках .NET Core 3.0, были введены дополнительные средства для упрощения кодовой базы и добавлен ряд более значимых средств (вроде кортежей, а также ссылочных локальных переменных и возвращаемых значений), которые разработчики просили включить в спецификацию языка в течение довольно долгого времени.

Версия C# 8 имеет два младших выпуска, которые добавили следующие средства:

• члены, допускающие только чтение, для структур:

• стандартные члены интерфейса;

• улучшения сопоставления с образцом;

• использование объявлений;

• статические локальные функции;

• освобождаемые ссылочные структуры;

• ссылочные типы, допускающие значение

null
;

• асинхронные потоки;

• индексы и диапазоны;

• присваивание с объединением с

null
;

• неуправляемые сконструированные типы;

• применение

stackalloc
во вложенных выражениях;

• усовершенствование интерполированных дословных строк.


Новые средства в C# 8 обозначаются как "(нововведение в версии 8)" в заголовках разделов, которые им посвящены, а обновленные средства помечаются как "(обновление в версии 8.0)".

Новые средства в C# 9

В версию C# 9, выпущенную 10 ноября 2020 года в составе .NET 5, добавлены следующие средства:

• записи;

• средства доступа только для инициализации;

• операторы верхнего уровня;

• улучшения сопоставления с образцом;

• улучшения производительности для взаимодействия;

• средства "подгонки и доводки";

• поддержка для генераторов кода.


Новые средства в C# 9 обозначаются как "(нововведение в версии 9.0)" в заголовках разделов, которые им посвящены, а обновленные средства помечаются как "(обновление в версии 9.0)" .

Сравнение управляемого и неуправляемого кода

Важно отметить, что язык C# может применяться только для построения программного обеспечения, которое функционирует под управлением исполняющей среды .NET Core (вы никогда не будете использовать C# для создания COM-сервера или неуправляемого приложения в стиле C/C++). Выражаясь официально, для обозначения кода, ориентированного на исполняющую среду .NET Core, используется термин управляемый код. Двоичный модуль, который содержит управляемый код, называется сборкой (сборки более подробно рассматриваются далее в главе). И наоборот, код, который не может напрямую обслуживаться исполняющей средой .NET Core, называется неуправляемым кодом.

Как упоминалось ранее, инфраструктура .NET Core способна функционировать в средах разнообразных операционных систем. Таким образом, вполне вероятно создавать приложение C# на машине Windows с применением Visual Studio и запускать его под управлением iOS с использованием исполняющей среды .NET Core. Кроме того, приложение C# можно построить на машине Linux с помощью Visual Studio Code и запускать его на машине Windows. С помощью Visual Studio для Mac на компьютере Мае можно разрабатывать приложения .NET Core, предназначенные для выполнения под управлением Windows, macOS или Linux.

Программа C# по-прежнему может иметь доступ к неуправляемому коду, но тогда она привяжет вас к специфической цели разработки и развертывания.

Использование дополнительных языков программирования, ориентированных на .NET Core

Имейте в виду, что C# — не единственный язык, который может применяться для построения приложений .NET Core. В целом приложения .NET Core могут строиться с помощью С#, Visual Basic и F#, которые представляют собой три языка, напрямую поддерживаемые Microsoft.

Обзор сборок .NET

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

*.dll
), внутренне они устроены совершенно по-другому. В частности, двоичные модули .NET Core содержат не специфические, а независимые от платформы инструкции на промежуточном языке (Intermediate Language — IL) и метаданные типов.


На заметку! Язык IL также известен как промежуточный язык Microsoft (Microsoft Intermediate Language — MSIL) или общий промежуточный язык (Common Intermediate Language — CIL). Таким образом, при чтении литературы по .NET/.NET Core не забывайте о том, что IL, MSIL и CIL описывают в точности одну и ту же концепцию. В настоящей книге при ссылке на этот низкоуровневый набор инструкций будет применяться аббревиатура CIL.


Когда файл

*.dll
был создан с использованием компилятора .NET Core, результирующий большой двоичный объект называется сборкой. Все многочисленные детали, касающиеся сборок .NET Core, подробно рассматриваются в главе 16. Тем не менее, для упрощения текущего обсуждения вы должны усвоить четыре основных свойства нового файлового формата.

Во-первых, в отличие от сборок .NET Framework, которые могут быть файлами

*.dll
или
*.ехе
, проекты .NET Core всегда компилируются в файл с расширением
.dll
, даже если проект является исполняемым модулем. Исполняемые сборки .NET Core выполняются с помощью команды
dotnet<имя_сборки>.dll
. Нововведение .NET Core 3.0 (и последующих версий) заключается в том, что команда
dotnet.ехе
копирует файл в каталог сборки и переименовывает его на
<имя_сборки>.ехе
. Запуск этой команды автоматически выполняет эквивалент
dotnet<имя_сборки>.ехе
. Файл
*.ехе
с именем вашего проекта фактически не относится к коду проекта; он является удобным сокращением для запуска вашего приложения.

Нововведением .NET 5 стало то, что ваше приложение может быть сведено до единственного файла, который запускается напрямую. Хотя такой единственный файл выглядит и действует подобно собственному исполняемому модулю в стиле C++, его преимущество заключается в пакетировании. Он содержит все файлы, необходимые для выполнения вашего приложения и потенциально даже саму исполняющую среду .NET 5! Но помните о том, что ваш код по-прежнему выполняется в управляемом контейнере, как если бы он был опубликован в виде множества файлов.

Во-вторых, сборка содержит код CIL, который концептуально похож на байт-код Java тем, что не компилируется в специфичные для платформы инструкции до тех пор, пока это не станет абсолютно необходимым. Обычно "абсолютная необходимость" наступает тогда, когда на блок инструкций CIL (такой как реализация метода) производится ссылка с целью его применения исполняющей средой .NEIT Core.

В-третьих, сборки также содержат метаданные, которые детально описывают характеристики каждого "типа" внутри двоичного модуля. Например, если имеется класс по имени

SportsCar
, то метаданные типа представляют такие детали, как базовый класс
SportsCar
, указывают реализуемые
SportsCar
интерфейсы (если есть) и дают полные описания всех членов, поддерживаемых типом
SportsCar
. Метаданные .NET Core всегда присутствуют внутри сборки и автоматически генерируются компилятором языка.

Наконец, в-четвертых, помимо инструкций CIL и метаданных типов сами сборки также описываются с помощью метаданных, которые официально называются манифестом. Манифест содержит информацию о текущей версии сборки, сведения о культуре (используемые для локализации строковых и графических ресурсов) и список ссылок на все внешние сборки, которые требуются для надлежащего функционирования. Разнообразные инструменты, которые можно применять для исследования типов, метаданных и манифестов сборок, рассматриваются в нескольких последующих главах.

Роль языка CIL

Теперь давайте займемся детальными исследованиями кода CIL, метаданных типов и манифеста сборки. Язык CIL находится выше любого набора инструкций, специфичных для конкретной платформы. Например, приведенный далее код C# моделирует простой калькулятор. Не углубляясь пока в подробности синтаксиса, обратите внимание на формат метода

Add()
в классе
Calc
.


// Calc.cs

using System;

namespace CalculatorExamples

{

  // Этот класс содержит точку входа приложения.

  class Program

  {

   static void Main(string[] args)

    {

    Calc c = new Calc();

    int ans = c.Add(10, 84);

    Console.WriteLine("10 + 84 is {0}.", ans);

    // Ожидать нажатия пользователем клавиши 

    // перед завершением работы.

    Console.ReadLine();

   }

  }


  // Калькулятор С#.

  class Calc

  {

   public int Add(int addendl, int addend2)

   {

    return addendl + addend2;

   }

  }

}


Результатом компиляции такого кода будет файл

*.dll
сборки, который содержит манифест, инструкции CIL и метаданные, описывающие каждый аспект классов
Calc
и
Program
.


На заметку! В главе 2 будет показано, как использовать для компиляции файлов кода графические среды интегрированной разработки (integrated development environment — IDE), такие как Visual Studio Community.


Например, если вы выведете код IL из полученной сборки с помощью

ildasm.exe
(рассматривается чуть позже в главе), то обнаружите, что метод
Add()
был представлен в CIL следующим образом:


.method public hidebysig instance int32

   Add(int32 addendl, int32 addend2) cil managed

{

  // Code size   9(0x9)

  // Размер кода  9(0x9)

  .maxstack 2

  .locals init (int32 V_0)

  IL_0000: nop

  IL_0001: ldarg.1

  IL_0002: ldarg.2

  IL_0003: add

  IL_0004: stloc.0

  IL_0005: br.s  IL_0007

  IL_0007: ldloc.0

  IL 0008: ret

} // end of method Calc::Add  конец метода Calc::Add


Не беспокойтесь, если результирующий код CIL этого метода выглядит непонятным — в главе 19 будут описаны базовые аспекты языка программирования CIL. Важно понимать, что компилятор C# выпускает код CIL, а не инструкции, специфичные для платформы.

Теперь вспомните, что сказанное справедливо для всех компиляторов .NET. В целях иллюстрации создадим то же самое приложение на языке Visual Basic вместо С#:


' Calc.vb

Namespace CalculatorExample

  Module Program

    ' Этот класс содержит точку входа приложения.

   Sub Main(args As String())

    Dim c As New Calc

     Dim ans As Integer = c.Add(10, 84)

     Console.WriteLine("10 + 84 is {0}", ans)

    ' Ожидать нажатия пользователем клавиши 

     ' перед завершением работы.

     Console.ReadLine()

    End Sub

  End Module


  ' Калькулятор VB.NET.

  Class Calc

   Public Function Add(ByVal addendl As Integer,

             ByVal addend2 As Integer) As Integer

    Return addendl + addend2

  End Function

  End Class

End Namespace


Просмотрев код CIL такого метода

Add()
, можно найти похожие инструкции (слегка скорректированные компилятором Visual Basic):


.method public hidebysig instance int32

   Add(int32 addendl, int32 addend2) cil managed

{

  // Code size   9(0x9)

  // Размер кода  9(0x9)

  .maxstack 2

  .locals init (int32 V_0)

  IL_0000: nop

  IL_0001: ldarg.1

  IL_0002: ldarg.2

  IL_0003: add

  IL_0004: stloc.0

  IL_0005: br.s  IL_0007

  IL_0007: ldloc.0

  IL 0008: ret

} // end of method Calc::Add

  // конец метода Calc::Add


В качестве финального примера ниже представлена та же самая простая программа

Calc
, разработанная на F# (еще одном языке .NET Core):


// Узнайте больше о языке F# на веб-сайте http://fsharp.org

// Calc.fs

open System


module Calc =

  let add addendl addend2 =

   addendl + addend2


[]

let main argv =

  let ans = Calc.add 10 84

  printfn "10 + 84 is %d" ans

  Console.ReadLine()

  0


Если вы просмотрите код CIL для метода

Add()
, то снова найдете похожие инструкции (слегка скорректированные компилятором F#).


.method public static int32 Add(int32 addendl,

int32 addend2) cil managed

{

  .custom instance void [FSharp.Core]Microsoft.FSharp.Core.

CompilationArgumentCountsAttribute::.ctor(int32[]) = ( 01 00 02 00 00

00 01 00 00 00 01 00 00 00 00 00 )

  // Code size 4 (0x4)

  // Размер кода 4 (0x4)

  .maxstack 8

  IL_0000: ldarg.0

  IL_0001: ldarg.l

  IL_0002: add

  IL_0003: ret

} // end of method Calc::'add

  // конец метода Calc::'add'

Преимущества языка CIL

В этот момент вас может интересовать, какую выгоду приносит компиляция исходного кода в CIL, а не напрямую в специфичный набор инструкций. Одним из преимуществ является языковая интеграция. Как вы уже видели, все компиляторы .NET Core выпускают практически идентичные инструкции CIL. Следовательно, все языки способны взаимодействовать в рамках четко определенной "двоичной арены".

Более того, учитывая независимость от платформы языка CIL, сама инфраструктура .NET Core не зависит от платформы и обеспечивает те же самые преимущества, к которым так привыкли разработчики на Java (например, единую кодовую базу, функционирующую в средах многочисленных операционных систем). В действительности для языка C# предусмотрен международный стандарт. До выхода .NET Core существовало множество реализаций .NET для платформ, отличающихся от Windows, таких как Mono. Они по-прежнему доступны, хотя благодаря межплатформенной природе .NET Core потребность в них значительно снизилась.

Компиляция кода CIL в инструкции, специфичные для платформы

Поскольку сборки содержат инструкции CIL, а не инструкции, специфичные для платформы, перед применением код CIL должен компилироваться на лету. Компонентом, который транслирует код CIL в содержательные инструкции центрального процессора (ЦП), является оперативный (JIT) компилятор (иногда называемый jitter). Для каждого целевого ЦП исполняющая среда .NET Core задействует JIT-компилятор, который оптимизирован под лежащую в основе платформу.

Скажем, если строится приложение .NET Core, предназначенное для развертывания на карманном устройстве (наподобие смартфона с iOS или Android), то соответствующий JIT-компилятор будет оснащен возможностями запуска в среде с ограниченным объемом памяти. С другой стороны, если сборка развертывается на внутреннем сервере компании (где память редко оказывается проблемой), тогда JIT-компилятор будет оптимизирован для функционирования в среде с большим объемом памяти. Таким образом, разработчики могут писать единственный блок кода, который способен эффективно транслироваться JIT-компилятором и выполняться на машинах с разной архитектурой.

Вдобавок при трансляции инструкций CIL в соответствующий машинный код JIT-компилятор будет кешировать результаты в памяти в манере, подходящей для целевой ОС. В таком случае, если производится вызов метода по имени

PrintDocument()
, то инструкции CIL компилируются в специфичные для платформы инструкции при первом вызове и остаются в памяти для более позднего использования. Благодаря этому при вызове метода
PrintDocument()
в следующий раз повторная компиляция инструкций CIL не понадобится.

Предварительная компиляция кода CIL в инструкции, специфичные для платформы

В .NET Core имеется утилита под названием

crossgen.exe
, которую вы можете использовать для предварительной компиляции JIT своего кода. К счастью, в .NET Core 3.0 возможность производить "готовые к запуску" сборки встроена в инфраструктуру. Более подробно об этом речь пойдет позже в книге.

Роль метаданных типов .NET Core

В дополнение к инструкциям CIL сборка .NET Core содержит полные и точные метаданные, которые описывают каждый определенный в двоичном модуле тип (например, класс, структуру, перечисление), а также члены каждого типа (скажем, свойства, методы, события). К счастью, за выпуск актуальных метаданных типов всегда отвечает компилятор, а не программист. Из-за того, что метаданные .NET Core настолько основательны, сборки являются целиком самоописательными сущностями.

Чтобы проиллюстрировать формат метаданных типов .NET Core, давайте взглянем на метаданные, которые были сгенерированы для исследуемого ранее метода

Add()
класса
Calc
, написанного на C# (метаданные для версии Visual Basic метода
Add()
похожи, так что будет исследоваться только версия С#):


TypeDef #2 (02000003)

--------------------------------------------------------

  TypDefName: CalculatorExamples.Calc (02000003)

  Flags   :[NotPublic] [AutoLayout] [Class] [AnsiClass]

[BeforeFieldlnit] (00100000)

Роль манифеста сборки

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

Calc.cs
(ради краткости некоторые строки не показаны):


.assembly extern /*23000001*/ System.Runtime

{

  .publickeytoken = (ВО 3F 5F 7F 11 D5 0A ЗА ) // .?_....:

  .ver 5:0:0:0

}

.assembly extern /*23000002*/ System.Console

{

  .publickeytoken = (B0 3F 5F 7F 11 D5 0A ЗА ) // .?_....:

  .ver 5:0:0:0

}

.assembly /*20000001*/ Calc.Cs

{

  .hash algorithm 0x00008004

  .ver 1:0:0:0

}

.module Calc.Cs.dll

.imagebase 0x00400000

.file alignment 0x00000200

.stackreserve 0x00100000


Выражаясь кратко, показанный манифест документирует набор внешних сборок, требуемых для

Calc.dll
(в директиве
.assembly extern
), а также разнообразные характеристики самой сборки (вроде номера версии и имени модуля). Полезность данных манифеста будет более подробно исследоваться в главе 16.

Понятие общей системы типов

Сборка может содержать любое количество различающихся типов. В мире .NЕТ Core тип ― это просто общий термин, применяемый для ссылки на член из на­ бора {класс, интерфейс, структура, перечисление, делегат}. При построении решений на любом языке .NЕТ Core почти наверняка придется взаимодействовать со многими такими типами. Например, в сборке может быть определен класс, реализующий не­ которое количество интерфейсов. Возможно, метод одного из интерфейсов принимает перечисление в качестве входного параметра и возвращает вызывающему компоненту структуру.

Вспомните, что СТS является формальной спецификацией, которая документирует, каким образом типы должны быть определены, чтобы они могли обслуживаться .NЕТ Runtime. Внутренние детали СТS обычно интересуют только тех, кто занимается построением инструментов и/или компиляторов, предназначенных для .NЕТ Core. Однако всем программистам .NЕТ Core важно знать о том, как работать с пятью типами, определенными в CTS, на выбранных ими языках. Ниже приведен краткий обзор.

Типы классов CTS

В каждом языке .NЕТ Core поддерживается, по меньшей мере, понятие типа класса, которое является краеугольным камнем объектно-ориентированного программирования. Класс может состоять из любого количества членов (таких как конструкторы, свойства, методы и события) и элементов данных (полей). В языке С# классы объявляются с использованием ключевого слова

class
, примерно так:


// Тип класса С# с одним методом.

class Calc

{

  public int Add(int addendl, int addend2)

  {

   return addendl + addend2;

  }

}


Формальное знакомство с построением типов классов в С# начнется в главе 5, а пока в таблице 1.1 приведен перечень характеристик, свойственных типам классов.


Типы интерфейсов CTS

Интерфейсы представляют собой всего лишь именованные коллекции определений и/или (начиная с версии C# 8) стандартных реализаций абстрактных членов, которые могут быть реализованными (необязательно при наличии стандартных реализаций) в заданном классе или структуре. В языке C# типы интерфейсов определяются с применением ключевого слова

interface
.

По соглашению имена всех интерфейсов .NET Core начинаются с прописной буквы

I
, как показано в следующем примере:


// Тип интерфейса C# обычно объявляется как

// public, чтобы позволить типам из других

// сборок реализовывать его поведение.

public interface IDraw

{

  void Draw();

}


Сами по себе интерфейсы приносят не особо много пользы. Тем не менее, когда класс или структура реализует выбранный интерфейс уникальным об разом, появляется возможность получать доступ к предоставленной функциональности, используя ссылку на этот интерфейс в полиморфной манере. Программирование на основе интерфейсов подробно рассматривается в главе 8.

Типы структур CTS

Концепция структуры также формализована в CTS. Если вы имели дело с языком С, то вас наверняка обрадует, что эти определяемые пользователем типы (user-defined type — UDT) сохранились в мире .NET Core (хотя их внутреннее поведение несколько изменилось). Попросту говоря, структуру можно считать легковесным типом класса, который имеет семантику, основанную на значении. Тонкости структур более подробно исследуются в главе 4. Обычно структуры лучше всего подходят для моделирования геометрических и математических данных и создаются в языке C# с применением ключевого слова

struct
, например:


// Тип структуры С #.

struct Point

{

  // Структуры могут содержать поля.

  public int xPos, yPos;


  // Структуры могут содержать параметризованные конструкторы.

  public Point(int х, int у)

  { xPos = x; yPos = y;}


  // В структурах могут определяться методы.

  public void PrintPosition()

  {

   Console.WriteLine("({0}, {!})", xPos, yPos);

  }

}

Типы перечислений CTS

Перечисления — это удобная программная конструкция, которая позволяет группировать пары "имя-значение". Например, предположим, что требуется создать игровое приложение, в котором игроку разрешено выбирать персонажа из трех категорий:

Wizard
(маг),
Fighter
(воин) или
Thief
(вор). Вместо отслеживания простых числовых значений, представляющих каждую категорию, можно было бы создать строго типизированное перечисление, используя ключевое слово
enum
:


// Тип перечисления C#.

enum CharacterType

{

  Wizard = 100,

  Fighter = 200,

  Thief = 300

}


По умолчанию для хранения каждого элемента выделяется блок памяти, соответствующий 32-битному целому, однако при необходимости (скажем, при программировании для устройств с малым объемом памяти наподобие мобильных устройств) область хранения можно изменить. Кроме того, спецификация CTS требует, чтобы перечислимые типы были производными от общего базового класса

System.Enum
. Как будет показано в главе 4, в этом базовом классе определено несколько интересных членов, которые позволяют извлекать, манипулировать и преобразовывать лежащие в основе пары "имя-значение" программным образом.

Типы делегатов CTS

Делегаты являются эквивалентом .NET Core указателей на функции в стиле С, безопасных в отношении типов. Основная разница в том, что делегат .NET Core представляет собой класс, производный от

System.MulticastDelegate
, а не простой указатель на низкоуровневый адрес в памяти. В языке C# делегаты объявляются с помощью ключевого слова
delegate
:


// Этот тип делегата C# может "указывать" на любой метод,

// возвращающий тип int и принимающий два значения int.

delegate int BinaryOp(int x, int y);


Делегаты критически важны, когда объект необходимо наделить возможностью перенаправления вызова другому объекту, и они формируют основу архитектуры событий .NET Core. Как будет показано в главах 12 и 14, делегаты обладают внутренней поддержкой группового вызова (т.е. перенаправления запроса множеству получателей) и асинхронного вызова методов (т.е. вызова методов во вторичном потоке).

Члены типов CTS

Теперь, когда было представлено краткое описание каждого типа, формализованного в CTS, следует осознать тот факт, что большинство таких типов располагает любым количеством членов. Формально член типа ограничен набором {конструктор, финализатор, статический конструктор, вложенный тип, операция, метод, свойство, индексатор, поле, поле только для чтения, константа, событие}.

В спецификации CTS определены разнообразные характеристики, которые могут быть ассоциированы с заданным членом. Например, каждый член может иметь характеристику видимости (открытый, закрытый или защищенный). Некоторые члены могут быть объявлены как абстрактные (чтобы обеспечить полиморфное поведение в производных типах) или как виртуальные (чтобы определить заготовленную, но допускающую переопределение реализацию). Вдобавок большинство членов могут быть сконфигурированы как статические (связанные с уровнем класса) или члены экземпляра (связанные с уровнем объекта). Создание членов типов будет описано в нескольких последующих главах.


На заметку! Как объясняется в главе 10, в языке C# также поддерживается создание обобщенных типов и обобщенных членов.

Встроенные типы данных CTS

Финальный аспект спецификации CTS, о котором следует знать на текущий момент, заключается в том, что она устанавливает четко определенный набор фундаментальных типов данных. Хотя в каждом отдельном языке для объявления фундаментального типа данных обычно имеется уникальное ключевое слово, ключевые слова всех языков .NET Core в конечном итоге распознаются как один и тот же тип CTS, определенный в сборке по имени

mscorlib.dll
. В табл. 1.2 показано, каким образом основные типы данных CTS выражаются в языках VB. NET и С#.



Учитывая, что уникальные ключевые слова в управляемом языке являются просто сокращенными обозначениями для реальных типов в пространстве имен

System
, больше не нужно беспокоиться об условиях переполнения/потери значимости для числовых данных или о том, как строки и булевские значения внутренне представляются в разных языках. Взгляните на следующие фрагменты кода, в которых определяются 32-битные целочисленные переменные в C# и Visual Basic с применением ключевых слов языка, а также формального типа данных CTS:


// Определение целочисленных переменных в С #.

int i = 0;

System.Int32 j = 0;


' Определение целочисленных переменных в VB.

Dim i As Integer = 0

Dim j As System.Int32 = 0

Понятие общеязыковой спецификации

Как вы уже знаете, разные языки программирования выражают одни и те же программные конструкции с помощью уникальных и специфичных для конкретного языка терминов. Например, в C# конкатенация строк обозначается с использованием операции "плюс" (

+
), а в VB для этого обычно применяется амперсанд (
&
). Даже если два разных языка выражают одну и ту же программную идиому (скажем, функцию, не возвращающую значение), то высока вероятность того, что синтаксис на первый взгляд будет выглядеть не сильно отличающимся:


// Ничего не возвращающий метод С #.

public void MyMethodO

{

  // Некоторый код...

}

' Ничего не возвращающий метод VB.

Public Sub MyMethodO

  ' Некоторый код...

End Sub


Ранее вы уже видели, что такие небольшие синтаксические вариации для исполняющей среды .NET Core несущественны, учитывая, что соответствующие компиляторы (в данном случае

csc.exe
и
vbc.exe
) выпускают похожий набор инструкций CIL. Тем не менее, языки могут также отличаться в отношении общего уровня функциональности. Например, язык .NET Core может иметь или не иметь ключевое слово для представления данных без знака и поддерживать или не поддерживать типы указателей. При таких возможных вариациях было бы идеально располагать опорными требованиями, которым удовлетворяли бы все языки .NET Core.

Спецификация CLS — это набор правил, подробно описывающих минимальное и полное множество характеристик, которые отдельный компилятор .NET Core должен поддерживать, чтобы генерировать код, обслуживаемый средой CLR и в то же время доступный в унифицированной манере всем ориентированным на платформу .NET Core языкам. Во многих отношениях CLS можно рассматривать как подмножество полной функциональности, определенной в CTS.

В конечном итоге CLS является набором правил, которых должны придерживаться создатели компиляторов, если они намерены обеспечить гладкое функционирование своих продуктов в мире .NET Core. Каждое правило имеет простое название (например, "Правило номер 6"), и каждое правило описывает воздействие на тех, кто строит компиляторы, и на тех, кто (каким-либо образом) взаимодействует с ними. Самым важным в CLS является правило номер 1.


• Правило номер 1. Правила CLS применяются только к тем частям типа, которые видны извне определяющей сборки.


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

В целях иллюстрации ниже представлен метод

Add()
в С#, который не совместим с CLS, поскольку его параметры и возвращаемое значение используют данные без знака (что не является требованием CLS):


class Calc

{

  // Открытые для доступа данные без знака не совместимы с CLS!

  public ulong Add(ulong addendl, ulong addend2)

  {

   return addendl + addend2;

  }

}


Тем не менее, взгляните на следующий класс, который взаимодействует с данными без знака внутри метода:


class Calc

{

  public int Add(int addendl, int addend2)

  {

   // Поскольку эта переменная ulong используется только

   // внутренне, совместимость с CLS сохраняется.

   ulong temp = 0;

   ...

   return addendl + addend2;

  }

}


Класс

Calc
по-прежнему соблюдает правила CLS и можно иметь уверенность в том, что все языки .NET Core смогут вызывать его метод
Add()
.

Разумеется, помимо "Правила номер 1" в спецификации CLS определено множество других правил. Например, в CLS описано, каким образом заданный язык должен представлять текстовые строки, как внутренне представлять перечисления (базовый тип, применяемый для хранения их значений), каким образом определять статические члены и т.д. К счастью, для того, чтобы стать умелым разработчиком приложений .NET Core, запоминать все правила вовсе не обязательно. В общем и целом глубоко разбираться в спецификациях CTS и CLS обычно должны только создатели инструментов и компиляторов.

Обеспечение совместимости с CLS

Как вы увидите при чтении книги, в языке C# определено несколько программных конструкций, несовместимых с CLS. Однако хорошая новость заключается в том, что компилятор C# можно инструктировать о необходимости проверки кода на предмет совместимости с CLS, используя единственный атрибут .NET Core:


// Сообщить компилятору C# о том, что он должен

// осуществлять проверку на совместимость с CLS.

[assembly: CLSCompliant(true) ]


Детали программирования на основе атрибутов подробно рассматриваются в главе 17. А пока следует просто запомнить, что атрибут

[CLSCompliant]
заставляет компилятор C# проверять каждую строку кода на соответствие правилам CLS. В случае обнаружения любых нарушений спецификации CLS компилятор выдаст предупреждение с описанием проблемного кода.

Понятие .NET Core Runtime

В дополнение к спецификациям CTS и CLS осталось рассмотреть финальный фрагмент головоломки — .NET Core Runtime или просто .NET Runtime. В рамках программирования термин исполняющая среда можно понимать как коллекцию служб, которые требуются для выполнения скомпилированной единицы кода. Например, когда разработчики на Java развертывают программное обеспечение на новом компьютере, им необходимо удостовериться в том, что на компьютере установлена виртуальная машина Java (Java Virtual Machine — JVM), которая обеспечит выполнение их программного обеспечения.

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

Различия между сборкой пространством имен и типом

Любой из нас понимает важность библиотек кода. Главное назначение библиотек платформы — предоставлять разработчикам четко определенный набор готового кода, который можно задействовать в создаваемых приложениях. Однако C# не поставляется с какой-то специфичной для языка библиотекой кода. Взамен разработчики на С# используют нейтральные к языкам библиотеки .NET Core. Для поддержания всех типов внутри библиотек базовых классов в организованном виде внутри .NET Core широко применяется концепция пространств имен.

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

System.IO
содержит типы, относящиеся к файловому вводу-выводу, пространство имен
System.Data
— типы для работы с базами данных и т.д. Важно понимать, что одна сборка может содержать любое количество пространств имен, каждое из которых может иметь любое число типов.

Основное отличие между таким подходом и специфичной для языка библиотекой заключается в том, что любой язык, ориентированный на исполняющую среду .NET Core, использует те же самые пространства имен и те же самые типы. Например, следующие две программы представляют собой вездесущее приложение "Hello World", написанное на языках C# и VB:


// Приложение "Hello World" на языке С #.

using System;


public class MyApp

{

  static void Main()

  {

   Console.WriteLine("Hi from C#");

  }

}


' Приложение "Hello World" на языке VB.

Imports System

Public Module MyApp

  Sub Main()

   Console.WriteLine("Hi from VB")

  End Sub

End Module


Обратите внимание, что во всех языках применяется класс

Console
, определенный в пространстве имен
System
. Помимо очевидных синтаксических различий представленные приложения выглядят довольно похожими как физически, так и логически.

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

System
. Оно предлагает основной набор типов, которые вам как разработчику в .NET Core придется задействовать неоднократно. Фактически без добавления, по крайней мере, ссылки на пространство имен
System
построить сколько-нибудь функциональное приложение C# невозможно, т.к. в
System
определены основные типы данных (например,
System.Int32
и
System.String
). В табл. 1.3 приведены краткие описания некоторых (конечно же, не всех) пространств имен .NET Core, сгруппированные по функциональности.

Доступ к пространству имен программным образом

Полезно снова повторить, что пространство имен — всего лишь удобный способ логической организации связанных типов, содействующий их пониманию. Давайте еще раз обратимся к пространству имен

System
. С точки зрения разработчика можно предположить, что конструкция
System.Console
представляет класс по имени
Console
, который содержится внутри пространства имен под названием
System
. Однако с точки зрения исполняющей среды .NET Core это не так. Исполняющая среда видит только одиночный класс по имени
System.Console
.



В языке C# ключевое слово

using
упрощает процесс ссылки на типы, определенные в отдельном пространстве имен. Давайте посмотрим, каким образом оно работает. В приведенном ранее примере
Calc
в начале файла находится единственный оператор
using
:


using System;


Он делает возможной следующую строку кода:


Console.WriteLine ("10 + 84 is {0}." , ans);


Без оператора

using
пришлось бы записывать так:


System.Console.WriteLine ("10 + 84 is {0}.", ans);


Хотя определение типа с использованием полностью заданного имени позволяет делать код более читабельным, трудно не согласиться с тем, что применение ключевого слова

using
в C# значительно сокращает объем набора на клавиатуре. В настоящей книге полностью заданные имена в основном использоваться не будут (разве что для устранения установленной неоднозначности), а предпочтение отдается упрощенному подходу с применением ключевого слова
using
.

Однако не забывайте о том, что ключевое слово

using
— просто сокращенный способ указать полностью заданное имя типа. Любой из подходов дает в результате тот же самый код CIL (учитывая, что в коде CIL всегда используются полностью заданные имена) и не влияет ни на производительность, ни на размер сборки.

Ссылка на внешние сборки

В предшествующих версиях .NET Framework для установки библиотек инфраструктуры применялось общее местоположение, известное как глобальный кеш сборок (Global Assembly Cache — GAC). Инфраструктура .NET Core не использует GAC. Взамен каждая версия (включая младшие выпуски) устанавливается в собственное местоположение на компьютере (согласно версии). В среде Windows каждая версия исполняющей среды и SDK устанавливаются в

с:\Program Files\dotnet
.

В большинстве проектов .NET Core сборки добавляются путем добавления пакетов NuGet (раскрываются позже в книге). Тем не менее, приложения .NET Core, нацеленные и разрабатываемые в среде Windows, по-прежнему располагают доступом к библиотекам СОМ, что тоже рассматривается позже в книге.

Чтобы сборка имела доступ к другой сборке, которую строите вы (или кто-то другой), необходимо добавить ссылку из вашей сборки на другую сборку и обладать физическим доступом к этой другой сборке. В зависимости от инструмента разработки, применяемого для построения приложений .NET Core, вам будут доступны различные способы информирования компилятора о том, какие сборки должны включаться в цикл компиляции.

Исследование сборки с помощью ildasm.exe

Если вас начинает беспокоить мысль о необходимости освоения всех пространств имен .NET Core, то просто вспомните о том, что уникальность пространству имен придает факт наличия в нем типов, которые каким-то образом семантически связаны. Следовательно, если в качестве пользовательского интерфейса достаточно простого консольного режима, то можно вообще не думать о пространствах имен, предназначенных для построения интерфейсов настольных и веб-приложений. Если вы создаете приложение для рисования, тогда вам вряд ли понадобятся пространства имен, ориентированные на работу с базами данных. Со временем вы изучите те пространства имен, которые больше всего соответствуют вашим потребностям в программировании.

Утилита

ildasm.exe
(Intermediate Language Disassembler — дизассемблер промежуточного языка) дает возможность загрузить любую сборку .NET Core и изучить ее содержимое, включая ассоциированный с ней манифест, код CIL и метаданные типов. Инструмент
ildasm.exe
позволяет программистам более подробно разобраться, как их код C# отображается на код CIL, и в итоге помогает понять внутреннюю механику функционирования .NET Core. Хотя для того, чтобы стать опытным программистом приложений .NET Core, использовать
ildasm.exe
вовсе не обязательно, настоятельно рекомендуется время от времени применять данный инструмент, чтобы лучше понимать, каким образом написанный код C# укладывается в концепции исполняющей среды.


На заметку! Утилита

ildasm.exe
не поставляется с исполняющей средой .NET 5. Получить этот инструмент в свое распоряжение можно двумя способами. Первый способ предусматривает его компиляцию из исходного кода исполняющей среды .NET 5, который доступен по ссылке
https://github.com/dotnet/runtime
. Второй и более простой способ — получить пакет NuGet по ссылке
https://www.nuget.org/packages/Microsoft.NETCore.iLDAsm/
. Удостоверьтесь в том, что выбираете корректную версию (для книги понадобится версия 5.0.0 или выше). Добавьте пакет
ILdasm
в свой проект с помощью команды
dotnet add package Microsoft .NETCore.ILDAsm --version 5.0.0
. На самом деле команда не загружает
ILDasm.exe
в ваш проект, а помещает его в папку пакета (на компьютере Windows):
%userprofile%\.nuget\packages\microsoft.netcore.ildasm\5.0.0\runtimes\native\
.

Утилита

ILDasm.exe
версии 5.0.0 также включена в папку
Chapter_01
(и в папки для других глав, где применяется
ILDasm.exe
) хранилища GitHub для данной книги.


После загрузки утилиты

ildasm.exe
на свой компьютер вы можете запустить ее из командной строки и просмотреть справочную информацию. Чтобы извлечь код CIL, понадобится указать как минимум имя сборки.

Вот пример команды:


ildasm /all /METADATA /out=csharp.il calc.cs.dll


Команда создаст файл по имени

csharp.il
и экспортирует в него все доступные данные.

Резюме

Задачей настоящей главы было формирование концептуальной основы, требуемой для освоения остального материала книги. Сначала исследовались ограничения и сложности, присущие технологиям, которые предшествовали инфраструктуре .NET Core, после чего в общих чертах было показано, как .NET Core и C# пытаются упростить текущее положение дел.

По существу .NET Core сводится к механизму исполняющей среды (.NET Runtime) и библиотекам базовых классов. Исполняющая среда способна обслуживать любые двоичные модули .NET Core (называемые сборками), которые следуют правилам управляемого кода. Вы видели, что сборки содержат инструкции CIL (в дополнение к метаданным типов и манифестам сборок), которые с помощью JIT-компилятора транслируются в инструкции, специфичные для платформы. Кроме того, вы ознакомились с ролью общеязыковой спецификации (CLS) и общей системы типов (CTS).

В следующей главе будет предложен обзор распространенных IDE-сред, которые можно применять при построении программных проектов на языке С#. Вас наверняка обрадует тот факт, что в книге будут использоваться полностью бесплатные (и богатые возможностями) IDE-среды, поэтому вы начнете изучение мира .NET Core без каких-либо финансовых затрат.

Глава 2 Создание приложений на языке C#

Как программист на языке С#, вы можете выбрать подходящий инструмент среди многочисленных средств для построения приложений .NET Core. Выбор инструмента (или инструментов) будет осуществляться главным образом на основе трех факторов: сопутствующие финансовые затраты, операционная система (ОС), используемая при разработке программного обеспечения, и вычислительные платформы, на которые оно ориентируется. Цель настоящей главы — предложить сведения об установке .NET 5 SDK и исполняющей среды, а также кратко представить флагманские IDE-среды производства Microsoft — Visual Studio Code и Visual Studio.

Сначала в главе раскрывается установка на ваш компьютер .NET 5 SDK и исполняющей среды. Затем будет исследоваться построение первого приложения на C# с помощью Visual Studio Code и Visual Studio Community Edition.


На заметку! Экранные снимки в этой и последующих главах сделаны в IDE-среде Visual Studio Code v1.51.1 или Visual Studio 2019 Community Edition v16.8.1 на компьютере с ОС Windows. Если вы хотите строить свои приложения на компьютере с другой ОС или IDE-средой, то глава укажет правильное направление, но окна выбранной вами IDE-среды будут отличаться от изображенных на экранных снимках, приводимых в тексте.

Установка .NET 5

Чтобы приступить к разработке приложений с помощью C# 9 и .NET 5 (в среде Windows, macOS или Linux), необходимо установить комплект .NET 5 SDK (который также устанавливает исполняющую среду .NET 5). Все установочные файлы для .NET и .NET Core расположены на удобном веб-сайте

www.dot.net
. Находясь на домашней странице, щелкните на кнопке Download (Загрузить) и затем на ссылке All .NET downloads (Все загрузочные файлы .NET) под заголовком .NET. После щелчка на ссылке All .NET downloads вы увидите две LTS-версии .NET Core (2.1 и 3.1) и ссылку на .NET 5.0. Щелкните на ссылке .NET 5.0 (recommended) (.NET 5.0 (рекомендуется)). На появившейся странице выберите комплект .NET 5 SDK, который подходит для вашей ОС. В примерах книги предполагается, что вы установите SDK для .NET Core версии 5.0.100 или выше, что также приведет к установке исполняющих сред .NET, ASP.NET и .NET Desktop (в Windows).


На заметку! С выходом .NET 5 станица загрузки изменилась. Теперь на ней есть три колонки с заголовками .NET, .NET Core и .NET Framework. Щелчок на ссылке All .NET Core downloads под заголовком .NET или .NET Core приводит к переходу на одну и ту же страницу. При установке Visual Studio 2019 также устанавливается .NET Core SDK и исполняющая среда.

Понятие схемы нумерации версий .NET 5

На момент написания книги актуальной версией .NET 5 SDK была 5.0.100. Первые два числа (5.0) указывают наивысшую версию исполняющей среды, на которую можно нацеливаться, в данном случае — 5.0. Это означает, что SDK также поддерживает разработку для более низких версий исполняющей среды, таких как .NET Core 3.1. Следующее число (1) представляет квартальный диапазон средств. Поскольку речь идет о первом квартале года выпуска, оно равно 1. Последние два числа (00) указывают версию исправления. Если вы добавите в уме разделитель к версии, думая о текущей версии, как о 5.0.1.00, то ситуация чуть прояснится.

Подтверждение успешности установки .NET 5

Чтобы проверить, успешно ли установлены комплект SDK и исполняющая среда, откройте окно командной подсказки и воспользуйтесь интерфейсом командной строки (CLI) .NET 5, т.е.

dotnet.ехе
. В интерфейсе CLI доступны параметры и команды SDK. Команды включают создание, компиляцию, запуск и опубликование проектов и решений; позже в книге вы встретите примеры применения упомянутых команд. В этом разделе мы исследуем параметры SDK, которых четыре, как показано в табл. 2.1.



Параметр

--version
позволяет отобразить наивысшую версию комплекта SDK, установленного на компьютере, или версию, которая указана в файле
global.json
, расположенном в текущем каталоге или выше него. Проверьте версию .NET 5 SDK, установленную на компьютере, за счет ввода следующей команды:


dotnet -- version


Для настоящей книги результатом должен быть 5.0.100 (или выше).

Чтобы просмотреть все исполняющие среды .NET Core, установленные на компьютере, введите такую команду:


dotnet --list-runtimes


Существует три разных исполняющих среды:

Microsoft.AspNetCore.Арр
(для построения приложений ASP.NET Core);

Microsoft.NETCore.Арр
(основная исполняющая среда для .NET Core);

Microsoft.WindowsDesktop.Арр
(для построения приложений Windows Forms и WPF).


В случае если ваш компьютер работает под управлением ОС Windows, тогда версией каждой из перечисленных исполняющих сред должна быть 5.0.0 (или выше). Для ОС, отличающихся от Windows, понадобятся первые две исполняющих среды,

Microsoft.NETCore.Арр
и
Microsoft.AspNetCore.Арр
, версией которых тоже должна быть 5.0.0 (или выше).

Наконец, чтобы увидеть все установленные комплекты SDK, введите следующую команду:


dotnet --list-sdks


И снова версией должна быть 5.0.100 (или выше).

Использование более ранних версий .NET (Core) SDK

Если вам необходимо привязать свой проект к более ранней версии .NET Core SDK, то можно воспользоваться файлом

global.json
, который создается с помощью такой команды:


dotnet new globaljson --sdk-version 3.1.404


В результате создается файл

global.json
с содержимым следующего вида:


{

  "sdk": {

   "version": "3.1.404"

  }

}


Этот файл "прикрепляет" текущий каталог и все его подкаталоги к версии 3.1.404 комплекта .NET Core SDK. Запуск команды

dotnet.exe --version
в таком каталоге возвратит 3.1.404.

Построение приложений .NET Core с помощью Visual Studio

Если у вас есть опыт построения приложений с применением технологий Microsoft предшествующих версий, то вполне вероятно, что вы знакомы с Visual Studio. На протяжении времени жизни продукта названия редакций и наборы функциональных возможностей менялись, но с момента выпуска .NET Core остались неизменными. Инструмент Visual Studio доступен в виде следующий редакций (для Window и Маc):

• Visual Studio 2019 Community (бесплатная);

• Visual Studio 2019 Professional (платная);

• Visual Studio 2019 Enterprise (платная).


Редакции Community и Professional no существу одинаковы. Наиболее значительная разница связана с моделью лицензирования. Редакция Community лицензируется для использования проектов с открытым кодом, в учебных учреждениях и на малых предприятиях. Редакции Professional и Enterprise являются коммерческими продуктами, которые лицензируются для любой разработки, включая корпоративную. Редакция Enterprise по сравнению с Professional вполне ожидаемо предлагает многочисленные дополнительные средства.


На заметку! Детали лицензирования доступны на веб-сайте

www.visualstudio.com
. Лицензирование продуктов Microsoft может показаться сложным и в книге его подробности не раскрываются. Для написания (и проработки) настоящей книги законно применять редакцию Community.


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

Установка Visual Studio 2019 (Windows)

Чтобы продукт Visual Studio 2019 можно было использовать для разработки, запуска и отладки приложений С#, его необходимо установить. По сравнению с версией Visual Studio 2017 процесс установки значительно изменился и потому заслуживает более подробного обсуждения.


На заметку! Загрузить Visual Studio 2019 Community можно по ссылке

www.visualstudio.com/downloads
. Удостоверьтесь в том, что загружаете и устанавливаете минимум версию 16.8.1 или более позднюю.


Процесс установки Visual Studio 2019 теперь разбит на рабочие нагрузки по типам приложений. В результате появляется возможность устанавливать только те компоненты, которые нужны для построения планируемого типа приложений. Например, если вы собираетесь строить веб-приложения, тогда должны установить рабочую нагрузку ASP.NET and web development (Разработка приложений ASP.NET и веб-приложений).

Еще одно (крайне) важное изменение связано с тем, что Visual Studio 2019 поддерживает подлинную установку бок о бок. Обратите внимание, что речь идет не о параллельной установке с предшествующими версиями, а о самом продукте Visual Studio 2019! Скажем, на главном рабочем компьютере может быть установлена редакция Visual Studio 2019 Enterprise для профессиональной работы и редакция Visual Studio 2019 Community для работы с настоящей книгой. При наличии редакции Professional или Enterprise, предоставленной вашим работодателем, вы по-прежнему можете установить редакцию Community для работы над проектами с открытым кодом (или с кодом данной книги).

После запуска программы установки Visual Studio 2019 Community появляется экран, показанный на рис. 2.1. На нем предлагаются все доступные рабочие нагрузки, возможность выбора отдельных компонентов и сводка (в правой части), которая отображает, что было выбрано.

Для этой книги понадобится установить следующие рабочие нагрузки:

• .NET desktop development (Разработка классических приложений .NET)

• ASP.NET and web development (ASP.NET и разработка веб-приложений)

• Data storage and processing (Хранение и обработка данных)

• .NET Core cross-platform development (Межплатформенная разработка для .NET Core)


На вкладке Individual components (Отдельные компоненты) отметьте флажки Class Designer (Конструктор классов), Git for Windows (Git для Windows) и GitHub extension for Visual Studio (Расширение GitHub для Visual Studio) в группе Code tools (Средства для работы с кодом). После выбора всех указанных элементов щелкните на кнопке Install (Установить). В итоге вам будет предоставлено все, что необходимо для проработки примеров в настоящей книге.


Испытание Visual Studio 2019

Среда Visual Studio 2019 — это универсальный инструмент для разработки программного обеспечения с помощью платформы .NET и языка С#. Давайте бегло посмотрим на работу Visual Studio, построив простое консольное приложение .NET 5.

Использование нового диалогового окна для создания проекта и редактора кода C#

Запустив Visual Studio, вы увидите обновленное диалоговое окно запуска, которое показано на рис. 2.2.



В левой части диалогового окна находятся недавно использованные решения, а в правой части — варианты запуска Visual Studio путем запуска кода из хранилища, открытия существующего проекта/решения, открытия локальной папки или создания нового проекта. Существует также вариант продолжения без кода, который обеспечивает просто запуск IDE-среды Visual Studio.

Выберите вариант Create a new project (Создать новый проект); отобразится диалоговое окно Create a new project (Создание нового проекта). Как видно на рис. 2.3, слева располагаются недавно использованные шаблоны (при их наличии), а справа — все доступные шаблоны, включая набор фильтров и поле поиска.



Начните с создания проекта типа Console Арр (.NET Core) (Консольное приложение (.NET Core)) на языке С#, выбрав версию С#, но не Visual Basic.

Откроется диалоговое окно Configure your new project (Конфигурирование нового проекта), представленное на рис. 2.4.



Введите

SimpleCSharpConsoleApp
в качестве имени проекта и выберите местоположение для проекта. Мастер также создаст решение Visual Studio, по умолчанию получающее имя проекта.


На заметку! Создавать решения и проекты можно также с применением интерфейса командной строки .NET Core, как будет объясняться при рассмотрении Visual Studio Code.


После создания проекта вы увидите начальное содержимое файла кода C# (по имени

Program.cs
), который открывается в редакторе кода. Замените единственную строку кода в методе
Main()
приведенным ниже кодом. По мере набора кода вы заметите, что во время применения операции точки активизируется средство IntelliSense.


static void Main(string[] args) 

{ 

  // Настройка консольного пользовательского интерфейса. 

  Console.Title = "My Rocking App"; 

  Console.ForegroundColor = ConsoleColor.Yellow; 

  Console.BackgroundColor = ConsoleColor.Blue; 

  Console.WriteLine(*****************************************); 

  Console.WriteLine("***** Welcome to My Rocking App *****");

  Console.WriteLine("*************************************");

  Console.BackgroundColor = ConsoleColor.Black;


  // Ожидание нажатия клавиши .

  Console.ReadLine();

}


Здесь используется класс

Console
, определенный в пространстве имен
System
. Поскольку пространство имен
System
было автоматически включено посредством оператора
using
в начале файла, указывать
System
перед именем класса не обязательно (например,
System.Console.WriteLine()
). Данная программа не делает ничего особо интересного; тем не менее, обратите внимание на последний вызов
Console.ReadLine()
. Он просто обеспечивает поведение, при котором пользователь должен нажать клавишу <Enter>, чтобы завершить приложение. При работе в Visual Studio 2019 поступать так не обязательно, потому что встроенный отладчик приостановит про грамму, предотвращая ее завершение. Но без вызова
Console.ReadLine()
при запуске скомпилированной версии программа прекратит работу почти мгновенно!


На заметку! Если вы хотите, чтобы отладчик VS завершал программу автоматически, тогда установите флажок Automatically close the console when debugging stops (Автоматически закрывать окно консоли при останове отладки) в диалоговом окне, доступном через пункт меню ToolsOptionsDebugging (Сервис►Параметры►Отладка).

Изменение целевой инфраструктуры .NET Core

Стандартной версией .NET Core для консольных приложений .NET Core и библиотек классов является последняя версия LTS — .NET Core 3.1. Чтобы использовать .NET 5 или просто проверить версию .NET (Core), на которую нацелено ваше приложение, дважды щелкните на имени проекта в окне Solution Explorer (Проводник решений). В окне редактора откроется файл проекта (новая возможность Visual Studio 2019 и .NET Core). Отредактировать файл проекта можно также, щелкнув правой кнопкой мыши на имени проекта в окне Solution Explorer и выбрав в контекстном меню пункт Edit Project file (Редактировать файл проекта). Вы увидите следующее содержимое:


  

   Exe

   netcoreapp3.l

  


Для смены версии .NET Core на .NET 5 измените значение

TargetFramework
на
net5.0
:


  

   Exe

   net5.0

  


Изменить целевую инфраструктуру можно и по-другому. Щелкните правой кнопкой мыши на имени проекта в окне Solution Explorer, выберите в контекстном меню пункт Properties (Свойства), в открывшемся диалоговом окне Properties (Свойства) перейдите на вкладку Application (Приложение) и выберите в раскрывающемся списке Target framework (Целевая инфраструктура) вариант .NET 5.0, как показано на рис. 2.5.


Использование функциональных средств C# 9

В предшествующих версиях .NET можно было изменять версию С#, поддерживаемую проектом. С выходом .NET Core 3.0+ применяемая версия C# привязана к версии инфраструктуры. Чтобы удостовериться в этом, щелкните правой кнопкой на имени проекта в окне Solution Explorer и выберите в контекстном меню пункт Properties (Свойства). В открывшемся диалоговом окне Properties (Свойства) перейдите на вкладку Build (Сборка) и щелкните на кнопке Advanced (Дополнительно) в нижнем правом углу. Откроется диалоговое окно Advanced Build Settings (Расширенные настройки сборки), представленное на рис. 2.6.



Для проектов .NET 5.0 версия языка зафиксирована как C# 9. В табл. 2.2 перечислены целевые инфраструктуры (.NET Core, .NET Standard и .NET Framework) и задействованные по умолчанию версии С#.


Запуск и отладка проекта

Чтобы запустить программу и просмотреть вывод, нажмите комбинацию клавиш <Ctrl+F5> (или выберите пункт меню DebugsStart Without Debugging (Отладкам Запустить без отладки)). На экране появится окно консоли Windows с вашим специальным (раскрашенным) сообщением. Имейте в виду, что при "запуске" своей программы с помощью <Ctrl+F5> вы обходите интегрированный отладчик.


На заметку! Приложения .NET Core можно компилировать и запускать также с применением интерфейса командной строки. Чтобы запустить свой проект, введите команду

dotnet run
в каталоге, где находится файл проекта (
SimpleCSharpApp.csproj
в рассматриваемом примере). Команда
dotnet run
вдобавок автоматически компилирует проект.


Если написанный код необходимо отладить (что определенно будет важным при построении более крупных программ), тогда первым делом понадобится поместить точки останова на операторы кода, которые необходимо исследовать. Хотя в рассматриваемом примере не особо много кода, установите точку останова, щелкнув на крайней слева полосе серого цвета в окне редактора кода (обратите внимание, что точки останова помечаются значком в виде красного кружка (рис. 2.7)).



Если теперь нажать клавишу <F5> (или выбрать пункт меню DebugStartDebugging (Отладкам►Запустить с отладкой) либо щелкнуть на кнопке с зеленой стрелкой и надписью Start (Пуск) в панели инструментов), то программа будет прекращать работу на каждой точке останова. Как и можно было ожидать, у вас есть возможность взаимодействовать с отладчиком с помощью разнообразных кнопок панели инструментов и пунктов меню IDE-среды. После прохождения всех точек останова приложение в конечном итоге завершится, когда закончится метод

Main()
.


На заметку! Предлагаемые Microsoft среды IDE снабжены современными отладчиками, и в последующих главах вы изучите разнообразные приемы работы с ними. Пока нужно лишь знать, что при нахождении в сеансе отладки в меню Debug появляется большое количество полезных пунктов. Выделите время на ознакомление с ними.

Использование окна Solution Explorer

Взглянув на правую часть IDE-среды, вы заметите окно Solution Explorer (Проводник решений), в котором отображено несколько важных элементов. Первым делом обратите внимание, что IDE-среда создала решение с единственным проектом. Поначалу это может сбивать с толку, т.к. решение и проект имеют одно и то же имя (

SimpleCSharpConsoleApp
). Идея в том, что "решение" может содержать множество проектов, работающих совместно. Скажем, в состав решения могут входить три библиотеки классов, одно приложение WPF и одна веб-служба ASP.NET Core. В начальных главах книги будет всегда применяться одиночный проект; однако, когда мы займемся построением более сложных примеров, будет показано, каким образом добавлять новые проекты в первоначальное пространство решения.


На заметку! Учтите, что в случае выбора решения в самом верху окна Solution Explorer система меню IDE-среды будет отображать набор пунктов, который отличается от ситуации, когда выбран проект. Если вы когда-нибудь обнаружите, что определенный пункт меню исчез, то проверьте, не выбран ли случайно неправильный узел.

Использование визуального конструктора классов

Среда Visual Studio также снабжает вас возможностью конструирования классов и других типов (вроде интерфейсов или делегатов) в визуальной манере. Утилита Class Designer (Визуальный конструктор классов) позволяет просматривать и модифицировать отношения между типами (классами, интерфейсами, структурами, перечислениями и делегатами) в проекте. С помощью данного средства можно визуально добавлять (или удалять) члены типа с отражением этих изменений в соответствующем файле кода С#. Кроме того, по мере модификации отдельного файла кода C# изменения отражаются в диаграмме классов.

Для доступа к инструментам визуального конструктора классов сначала понадобится вставить новый файл диаграммы классов. Выберите пункт меню ProjectsAdd New Item (Проект►Добавить новый элемент) и в открывшемся окне найдите элемент Class Diagram (Диаграмма классов), как показано на рис. 2.8.



Первоначально поверхность визуального конструктора будет пустой; тем не менее, вы можете перетаскивать на нее файлы из окна Solution Explorer. Например, после перетаскивания на поверхность конструктора файла

Program.сs
вы увидите визуальное представление класса
Program
. Щелкая на значке с изображением стрелки для заданного типа, можно отображать или скрывать его члены (рис. 2.9).



На заметку! Используя панель инструментов утилиты Class Designer, можно настраивать параметры отображения поверхности визуального конструктора.


Утилита Class Designer работает в сочетании с двумя другими средствами Visual Studio — окном Class Details (Детали класса), которое открывается через меню ViewOther Windows (Вид►Другие окна), и панелью инструментов Class Designer, отображаемой выбором пункта меню ViewToolbox (Вид►Панель инструментов). Окно Class Details не только показывает подробные сведения о текущем выбранном элементе диаграммы, но также позволяет модифицировать существующие члены и вставлять новые члены на лету (рис. 2.10).



Панель инструментов Class Designer, которая также может быть активизирована с применением меню View, позволяет вставлять в проект новые типы (и создавать между ними отношения) визуальным образом (рис. 2.11).



(Чтобы видеть эту панель инструментов, должно быть активным окно диаграммы классов.) По мере выполнения таких действий IDE-среда создает на заднем плане новые определения типов на С#.

В качестве примера перетащите элемент Class (Класс) из панели инструментов Class Designer в окно Class Designer. В открывшемся диалоговом окне назначьте ему имя

Car
. В результате создается новый файл C# по имени
Car.cs
и автоматически добавляется к проекту. Теперь, используя окно Class Details, добавьте открытое поле типа
string
с именем
PetName
(рис. 2.12).



Заглянув в определение C# класса

Car
, вы увидите, что оно было соответствующим образом обновлено (за исключением приведенного ниже комментария):


public class Car

{

  // Использовать открытые данные

  // обычно не рекомендуется,

  // но здесь это упрощает пример.

  public string petName;

}


Снова активизируйте утилиту Class Designer, перетащите на поверхность визуального конструктора еще один элемент Class и назначьте ему имя

SportsCar
. Далее выберите значок Inheritance (Наследование) в панели инструментов Class Designer и щелкните в верхней части значка
SportsCar
. Щелкните в верхней части значка класса
Car
. Если все было сделано правильно, тогда класс
SportsCar
станет производным от класса
Car
(рис. 2.13).



На заметку! Концепция наследования подробно объясняется в главе 6.


Чтобы завершить пример, обновите сгенерированный класс

SportsCar
, добавив открытый метод по имени
GetPetName()
со следующим кодом:


public class SportsCar : Car

{

  public string GetPetNameO

  {

   petName = "Fred";

   return petName;

  }

}


Как и можно было ожидать, визуальный конструктор отобразит метод, добавленный в класс

SportsCar
.

На этом краткий обзор Visual Studio завершен. В оставшемся материале книги вы встретите дополнительные примеры применения Visual Studio для построения приложений с использованием C# 9 и .NET 5.

Построение приложений .NET Core с помощью Visual Studio Code

Еще одной популярной IDE-средой от Microsoft следует считать Visual Studio Code (VSC). Продукт VSC — относительно новая редакция в семействе Microsoft. Он является бесплатным и межплатформенным, поставляется с открытым кодом и получил широкое распространение среди разработчиков в экосистеме .NET Core и за ее пределами. Как должно быть понятно из названия, в центре внимания Visual Studio Code находится код вашего приложения. Продукт VSC лишен многих встроенных средств, входящих в состав Visual Studio. Однако существуют дополнительные средства, которые можно добавить к VSC через расширения, что позволяет получить быструю IDE-среду, настроенную для имеющегося рабочего потока. Многочисленные примеры в данной книге были собраны и протестированы с помощью VSC. Загрузить VSC можно по ссылке

https://code.visualstudio.com/download
.

После установки VSC вы наверняка захотите добавить расширение С#, которое доступно по следующей ссылке:

https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.csharp


На заметку! Продукт VSC используется для разработки разных видов приложений на основе множества языков. Существуют расширения для Angular, View, РНР, Java и многих других языков.

Испытание Visual Studio Code

Давайте применим VSC для построения того же самого консольного приложения .NET 5, которое создавалось ранее в Visual Studio.

Создание решений и проектов

После запуска VSC вы начинаете с "чистого листа". Решения и проекты должны создаваться с использованием интерфейса командной строки (command-line interface — CLI) платформы .NET 5. Первым делом откройте папку в VSC, выбрав пункт меню FileOpen Folder (Файл►Открыть папку), и с помощью окна проводника перейдите туда, куда планируется поместить решение и проект. Затем откройте терминальное окно, выбрав пункт меню TerminalNew Terminal (Терминал►Новое терминальное окно) или нажав комбинацию клавиш <Ctl+Shift+ '>.

Введите в терминальном окне следующую команду, чтобы создать пустой файл решения .NET 5:


dotnet new sin -n SimpleCSharpConsoleApp -o. \WisualStudioCode


Команда создает новый файл решения с именем

SimpleCSharpConsoleApp
(указанным посредством
-n
) в подкаталоге (внутри текущего каталога) по имени
VisualStudioCode
. В случае применения VSC с единственным проектом нет необходимости в создании файла решения. Продукт Visual Studio ориентирован на решения, a Visual Studio Code — на код. Файл решения здесь создан для того, чтобы повторить процесс построения примера приложения в Visual Studio.


На заметку! В примерах используются разделители каталогов Windows. Вы должны применять разделители, принятые в вашей операционной системе.


Далее создайте новое консольное приложение C# 9/.NET 5

(-f net 5.0
) по имени
SimpleCSharpConsoleApp
(
-n
) в подкаталоге (
) с таким же именем (команда должна вводиться в одной строке):


dotnet new console -lang c# -n SimpleCSharpConsoleApp -o .\VisualStudioCode\

SimpleCSharpConsoleApp -f net5.0


На заметку! Поскольку целевая инфраструктура была указана с использованием параметра

-f
, обновлять файл проекта, как делалось в Visual Studio, не понадобится.


Наконец, добавьте созданный проект к решению с применением следующей команды:


dotnet sin .\VisualStudioCode\SimpleCSharpConsoleApp.sln

add .\VisualStudioCode\SimpleCSharpConsoleApp


На заметку! Это всего лишь небольшой пример того, на что способен интерфейс командной строки. Чтобы выяснить, что CLI может делать, введите команду

dotnet -h
.

Исследование рабочей области Visual Studio Code

Как легко заметить на рис. 2.14, рабочая область VSC ориентирована на код, но также предлагает множество дополнительных средств, предназначенных для повышения вашей продуктивности. Проводник (1) представляет собой встроенный проводник файлов и выбран на рисунке. Управление исходным кодом (2) интегрируется с Git. Значок отладки (3) отвечает за запуск соответствующего отладчика (исходя из предположения о том, что установлено корректное расширение). Ниже находится диспетчер расширений (4). Щелчок на значке отладки приводит к отображению списка рекомендуемых и всех доступных расширений. Диспетчер расширений чувствителен к контексту и будет выдавать рекомендации на основе типа кода в открытом каталоге и подкаталогах.



Редактор кода (5) снабжен цветовым кодированием и поддержкой IntelliSense; оба средства полагаются на расширения. Кодовая карта (6) показывает карту всего файла кода, а консоль отладки (7) получает вывод из сеансов отладки и принимает ввод от пользователя (подобно окну Immediate (Интерпретация) в Visual Studio).

Восстановление пакетов, компиляция и запуск программ

Интерфейс командной строки .NET 5 обладает всеми возможностями для восстановления пакетов, сборки решений, компиляции проектов и запуска приложений. Чтобы восстановить все пакеты NuGet, требуемые для вашего решения и проекта, введите в терминальном окне (или в окне командной подсказки вне VSC) приведенную ниже команду, находясь в каталоге, который содержит файл решения:


dotnet restore


Чтобы скомпилировать все проекты в решении, введите в терминальном окне или в окне командной подсказки следующую команду (снова находясь в каталоге, где содержится файл решения):


dotnet build


На заметку! Когда команды

dotnet restore
и
dotnet build
выполняются в каталоге, содержащем файл решения, они воздействуют на все проекты в решении. Команды также можно запускать для одиночного проекта, вводя их в каталоге с файлом проекта C# (
*.csproj
).


Чтобы запустить проект без отладки, введите в каталоге с файлом проекта (

SimpleCSharpConsoleApp.csproj
) следующую команду .NET CLI:


dotnet run

Отладка проекта

Для запуска отладки проекта нажмите клавишу <F5> или щелкните на значке отладки (на рис. 2.14 она помечена цифрой 2). Исходя из предположения, что вы загрузили расширение C# для VSC, программа запустится в режиме отладки. Управление точками останова производится точно так же, как в Visual Studio, хотя в редакторе они не настолько четко выражены (рис. 2.15).



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

launch.json
(находящийся в каталоге
.vscode
). Измените запись "
console
" с
internalConsole
на
integratedTerminal
, как показано ниже:


{

  // Используйте IntelliSense, чтобы выяснить, какие атрибуты

  // существуют для отладки С#.

  // Наводите курсор на существующие атрибуты, чтобы получить их описание.

  // Дополнительные сведения ищите по ссылке

  // https://github.com/OmniSharp/omnisharp-vscode/blob/master/

  // debugger-launchjson.md

  "version": "0.2.0",

  "configurations": [

   {

     "name": ".NET Core Launch (console)",

     "type": "coreclr",

    "request": "launch",

     "preLaunchTask": "build",

     // Если вы изменили целевые платформы, тогда не забудьте

    // обновить путь в program.

     "program": "${workspaceFolder}/SimpleCSharpConsoleApp/bin/

          Debug/net5.0/SimpleCSharpConsoleApp.Cs.dll",

     "args": [],

     "cwd": "${workspaceFolder}/SimpleCSharpConsoleApp",

     // Дополнительные сведения об атрибуте console ищите по ссылке

    // https://code.visualstudio.com/docs/editor/

     // debugging# _launchjson - attributes

     "console": "integratedTerminal",

    "stopAtEntry": false

    },

    {

     "name": ".NET Core Attach",

     "type": "coreclr",

    "request": "attach",

     "processId": "${command:pickProcess}"

   }

  ]

}

Документация по .NET Core и C#

Документация .NET Core и C# представляет собой исключительно хороший, понятный и насыщенный полезной информацией источник. Учитывая огромное количество предопределенных типов .NET (их тысячи), вы должны быть готовы засучить рукава и тщательно исследовать предлагаемую документацию. Вся документация от Microsoft доступна по ссылке

https://docs.microsoft.com/ru-ru/dotnet/
.

В первой половине книги вы будете чаще всего использовать документацию по C# и документацию по .NET Core, которые доступны по следующим ссылкам:

https://docs.microsoft.com/ru-ru/dotnet/csharp/

https://docs.microsoft.com/ru-ru/dotnet/fundamentals/

Резюме

Цель этой главы заключалась в том, чтобы предоставить информацию по настройке вашей среды разработки с комплектом .NET 5 SDK и исполняющими средами, а также провести краткий экскурс в Visual Studio 2019 Community Edition и Visual Studio Code. Если вас интересует только построение межплатформенных приложений .NET Core, то доступно множество вариантов. Visual Studio (только Windows), Visual Studio для Mac (только Mac) и Visual Studio Code (межплатформенная версия) поставляются компанией Microsoft. Построение приложений WPF или Windows Forms по-прежнему требует Visual Studio на компьютере с Windows.

Загрузка...