Вы находитесь на странице: 1из 20

ББК 32.973.2-018.

1
УДК 004.43
Г85

Гриффитс Иэн
Г85 Программируем на C# 8.0. Разработка приложений. — СПб: Питер, 2021. —
944 с.: ил. — (Серия «Бестселлеры O’Reilly»).
ISBN 978-5-4461-1638-6
C# —универсальный язык, который может практически всё! Иэн Гриффитс рассказывает
о его возможностях с точки зрения разработчика, перед которым стоит задача быстро и эффек-
тивно создавать приложения любой сложности. Множество примеров кода научат работать
с шаблонами, LINQ и асинхронными возможностями языка. Вы разберетесь с асинхронными
потоками, ссылочными типами, допускающими значение NULL, сопоставлениями с образцом,
реализациями по умолчанию для метода интерфейса, диапазонами и синтаксисом индексации
и многим другим.

16+ (В соответствии с Федеральным законом от 29 декабря 2010 г. № 436-ФЗ.)

ББК 32.973.2-018.1
УДК 004.43

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

ISBN 978-1492056812 англ. Authorized Russian translation of the English edition of Programming C# 8.0
ISBN 9781492056812 © 2020 Ian Griffiths
This translation is published and sold by permission of O’Reilly Media, Inc.,
which owns or controls all rights to publish and sell the same
ISBN 978-5-4461-1638-6 © Перевод на русский язык ООО Издательство «Питер», 2021
© Издание на русском языке, оформление
ООО Издательство «Питер», 2021
© Серия «Бестселлеры O’Reilly», 2021
Оглавление

Предисловие.............................................................................................. 12
Для кого эта книга.......................................................................................12
Условные обозначения.................................................................................12
Использование примеров кода.....................................................................13
Благодарности..............................................................................................14
От издательства...........................................................................................15

Глава 1. Знакомство с языком C#.....................................................................16


Почему C#?..................................................................................................17
Отличительные черты С#.............................................................................19
Стандарты и реализация C#.........................................................................23
Visual Studio и Visual Studio Code .................................................................30
Анатомия простой программы .....................................................................34
Итог.............................................................................................................54

Глава 2. Основы написания кода на C#............................................................55


Локальные переменные . .............................................................................56
Инструкции и выражения.............................................................................68
Комментарии и пробелы . ............................................................................77
Директивы препроцессора ..........................................................................80
Основные типы данных................................................................................86
Операторы................................................................................................. 114
Управление потоком ................................................................................. 121
Шаблоны.................................................................................................... 131
Итог........................................................................................................... 140

Глава 3. Типы ............................................................................................... 141


Классы....................................................................................................... 141
Структуры.................................................................................................. 162
Оглавление    
 7

Члены ....................................................................................................... 175


Интерфейсы............................................................................................... 231
Перечисления ........................................................................................... 236
Другие типы............................................................................................... 240
Частичные типы и методы.......................................................................... 244
Итог........................................................................................................... 245

Глава 4. Обобщения....................................................................................... 247


Обобщенные типы...................................................................................... 248
Ограничения.............................................................................................. 250
Нулевые значения...................................................................................... 261
Обобщенные методы.................................................................................. 263
Обобщения и кортежи................................................................................ 264
Внутренние обобщения.............................................................................. 266
Итог........................................................................................................... 269

Глава 5. Коллекции........................................................................................ 271


Массивы..................................................................................................... 271
Класс List<T>............................................................................................. 292
Интерфейсы списков и последовательностей............................................. 295
Реализация списков и последовательностей............................................... 303
Обращение к элементам по индексу и синтаксис диапазона....................... 311
Словари .................................................................................................... 321
Множества................................................................................................. 327
Очереди и стеки......................................................................................... 329
Связные списки.......................................................................................... 330
Параллельные коллекции........................................................................... 331
Неизменяемые коллекции ......................................................................... 332
Итог........................................................................................................... 336

Глава 6. Наследование . ................................................................................ 337


Наследование и преобразования................................................................ 339
Наследование интерфейса......................................................................... 343
Обобщения . .............................................................................................. 344
System.Object............................................................................................. 352
8    Оглавление

Доступность и наследование...................................................................... 354


Виртуальные методы.................................................................................. 356
Запечатанные методы и классы.................................................................. 368
Доступ к членам базового класса............................................................... 369
Наследование и конструирование ............................................................. 370
Специальные базовые типы ...................................................................... 376
Итог........................................................................................................... 377

Глава 7. Время жизни объекта....................................................................... 379


Сборка мусора............................................................................................ 380
Деструкторы и финализация...................................................................... 412
IDisposable................................................................................................. 416
Упаковка.................................................................................................... 426
Итог........................................................................................................... 434

Глава 8. Исключения...................................................................................... 435


Источники исключений............................................................................... 438
Обработка исключений ............................................................................. 443
Выдача исключений................................................................................... 453
Типы исключений....................................................................................... 460
Необработанные исключения..................................................................... 466
Итог........................................................................................................... 469

Глава 9. Делегаты, лямбды и события . ......................................................... 470


Делегаты . ................................................................................................. 471
Анонимные функции ................................................................................. 491
События..................................................................................................... 507
Сравнение делегатов и интерфейсов . ....................................................... 517
Итог........................................................................................................... 518

Глава 10. LINQ .............................................................................................. 520


Выражения запроса ................................................................................... 521
Отложенное вычисление............................................................................ 532
LINQ, обобщения и IQueryable<T>............................................................. 536
Стандартные операторы LINQ.................................................................... 538
Оглавление   9

Генерирование последовательностей......................................................... 585


Другие реализации LINQ............................................................................ 586
Итог........................................................................................................... 588

Глава 11. Реактивные расширения................................................................. 590


Ключевые интерфейсы............................................................................... 592
Публикация и подписка с использованием делегатов................................. 604
Построители последовательностей............................................................. 610
Запросы LINQ ............................................................................................ 613
Операторы запроса из Rx........................................................................... 627
Планировщики........................................................................................... 641
Субъекты.................................................................................................... 646
Адаптация.................................................................................................. 650
Операции для работы со временем ........................................................... 658
Итог........................................................................................................... 666

Глава 12. Сборки........................................................................................... 667


Анатомия сборки........................................................................................ 668
Определение типа...................................................................................... 673
Загрузка сборок ........................................................................................ 677
Имена сборок............................................................................................. 687
Итог........................................................................................................... 700

Глава 13. Отражение..................................................................................... 701


Типы отражения......................................................................................... 702
Контексты отражения................................................................................. 722
Итог........................................................................................................... 725

Глава 14. Атрибуты........................................................................................ 726


Применение атрибутов............................................................................... 726
Определение и использование атрибутов . ................................................ 747
Итог........................................................................................................... 753

Глава 15. Файлы и потоки.............................................................................. 755


Класс Stream.............................................................................................. 756
Ориентированные на текст типы................................................................ 768
10    Оглавление

Файлы и каталоги . .................................................................................... 779


Сериализация . .......................................................................................... 794
Итог........................................................................................................... 806

Глава 16. Многопоточность............................................................................ 807


Потоки....................................................................................................... 807
Синхронизация........................................................................................... 828
Задачи ...................................................................................................... 857
Другие асинхронные шаблоны .................................................................. 873
Отмена....................................................................................................... 875
Параллелизм.............................................................................................. 876
Итог........................................................................................................... 878

Глава 17. Асинхронные возможности языка................................................... 880


Ключевые слова async и await.................................................................... 881
Шаблон await............................................................................................. 899
Обработка ошибок .................................................................................... 905
Итог........................................................................................................... 913

Глава 18. Эффективная работа с памятью..................................................... 915


(Не) копируйте это..................................................................................... 916
Представление последовательных элементов с помощью Span<T>............ 920
Представление последовательных элементов с помощью Memory<T>....... 925
ReadOnlySequence<T>................................................................................ 926
Обработка потоков данных с помощью конвейеров ................................... 927
Итог........................................................................................................... 937

Об авторе ................................................................................................. 938

Об обложке.............................................................................................. 939
ГЛАВА 1

Знакомство с языком C#

Язык программирования C# (произносится как «си шарп») широко приме-


няется для разработки во множестве областей, включая веб-сайты, облачные
системы, интернет вещей, машинное обучение, классические приложения,
встроенные контроллеры, мобильные приложения, игры и утилиты ко-
мандной строки. Язык C# наряду со вспомогательной средой выполнения,
библиотеками и инструментами, известными под общим названием .NET,
был в центре внимания разработчиков Windows на протяжении почти
двух десятилетий, но в последние годы язык добрался и до других плат-
форм. В июне 2016 года Microsoft выпустила .NET Core версии 1.0, кросс-
платформенную версию .NET, включающую веб-приложения, микросервисы
и консольные приложения, написанные на C#, для работы в macOS и Linux,
а также в Windows.
Этот шаг в сторону других платформ Microsoft сделала на фоне своего ин-
тереса к разработке с использованием открытого исходного кода. На заре
истории C# Microsoft тщательно следила за всем своим исходным кодом1,
но сегодня почти все, что связано с C#, разрабатывается открыто, причем
приветствуется вклад разработчиков, не имеющих отношения к Microsoft.
Новые предложения по возможностям языка публикуются на GitHub, что
позволяет вовлекать сообщество на самых ранних этапах. В 2014 году для
ускорения разработки проектов с открытым исходным кодом в мире .NET
был создан .NET Foundation (https://dotnetfoundation.org/), и теперь многие из
наиболее важных проектов Microsoft на C# и .NET находятся под управле-
нием этого фонда (в дополнение ко многим проектам, не принадлежащим
Microsoft). В их число входят компилятор Microsoft C# (https://github.com/

1
Все это было верно и в отношении предыдущего кросс-платформенного предложения
Microsoft, а именно .NET. В 2008 году Microsoft выпустила Silverlight 2.0, который
позволял C# работать в браузерах на Windows и macOS. Silverlight проигрывал битву
со все растущими возможностями и универсальным охватом HTML5 и JavaScript,
и его закрытый исходный код никак не помогал его делу.
Почему C#?   17

dotnet/roslyn), .NET Core (https://github.com/dotnet/core), а также среда выпол-


нения, библиотека классов и инструменты для создания .NET-проектов.

Почему C#?
Хотя для C# существует множество сценариев использования, всегда есть
и другие языки программирования. Почему же стоит выбрать среди них
C#? Этот выбор зависит от того, что именно требуется сделать, а также
от того, что вам нравится или не нравится в том или ином языке програм-
мирования. Я считаю, что C# предоставляет значительные возможности,
гибкость и производительность и работает на достаточно высоком уровне
абстракции, из-за чего не приходится тратить огромные усилия на мелкие
детали, не связанные напрямую с задачей, которую призвана решать моя
программа.
Большая часть мощи C# обусловлена методами программирования, которые
поддерживает язык. Например, в нем имеются объектно-ориентированные
функции, обобщения и функциональное программирование. Он поддержи-
вает как динамическую, так и статическую типизацию. Он обеспечивает
мощные функции, ориентированные на списки и множества, благодаря
языку интегрированных запросов (LINQ). Он имеет встроенную поддержку
асинхронного программирования.
В последнее время C# приобрел гибкость в управлении памятью. Среда
выполнения всегда предоставляла сборщик мусора (GC), освобождающий
разработчиков от значительной части работы, связанной с освобождением
памяти, которую программа больше не использует. GC — это распространен-
ная функция в современных языках программирования, и хотя она является
благом для большинства случаев, существуют некоторые особые сценарии,
в которых ее влияние на производительность — это проблема. Поэтому
в C# 7.2 (выпущен в 2017 году) добавлены различные функции, которые
позволяют более явно управлять памятью, что дает возможность обменять
простоту разработки на производительность во время выполнения, но все
это без потери безопасности типов. Это дает возможность создавать на C#
приложения, для которых критична производительность, что годами было
прерогативой менее безопасных языков, таких как С и С++.
Конечно, языки программирования не живут в изоляции, и им нужны
высококачественные библиотеки с широким спектром возможностей. Не-
18    Глава 1. Знакомство с языком C#

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


пока вы не захотите сделать что-то прозаическое, например подключиться
к базе данных или указать, где хранить пользовательские настройки. Не-
зависимо от того, насколько мощный набор идиом предоставляет язык,
он должен обеспечивать полный и удобный доступ к службам базовой
платформы. В данном случае C# оказывается в выигрышном положении
благодаря среде выполнения, библиотеке классов и масштабной поддержке
сторонних библиотек.
.NET включает в себя как среду выполнения, так и библиотеку основных
классов, которые используют программы на C#. Часть среды выполнения
называется общеязыковой средой выполнения (Common Language Runtime,
CLR), потому что она поддерживает не только C#, но и любой другой язык
.NET. Например, Microsoft также предлагает расширения Visual Basic, F#
и .NET для C ++. CLR имеет общую систему типов (CTS), которая допускает
свободное взаимодействие кода, написанного на разных языках, что означает,
что библиотеки .NET могут без проблем использоваться из любого языка
.NET: F# может использовать библиотеки, написанные на C#, C# может
использовать библиотеки Visual Basic и т. д.
Помимо среды выполнения имеется обширная библиотека классов, которая
предоставляет обертки для многих функций базовой операционной системы
(ОС), но также и значительный объем собственного функционала, такого
как классы коллекций или обработка JSON.
Библиотека классов, встроенная в .NET, — это еще не все, ведь многие дру-
гие системы предоставляют свои собственные библиотеки .NET. Например,
существуют объемные библиотеки, которые позволяют программам на C#
использовать популярные облачные сервисы. Как и следовало ожидать,
Microsoft предоставляет всеобъемлющие библиотеки .NET для работы со
службами в рамках своей облачной платформы Azure. Аналогично Amazon
предоставляет полнофункциональный набор разработки для использования
с Amazon Web Services (AWS) из C# и других языков .NET. И библиотекам
нет необходимости быть связанными с фреймворками. Существует большая
экосистема библиотек .NET, как коммерческих, так и бесплатных с открытым
кодом. В их число входят математические утилиты, библиотеки синтакси-
ческого анализа и компоненты пользовательского интерфейса (UI), и это
лишь некоторые из них. Даже если вам не повезло и вам нужна функция
ОС, у которой нет обертки в библиотеке .NET, C# предлагает различные
механизмы для работы с другими видами API, такими как API в стиле C,
Отличительные черты С#   19

доступные в Win32, macOS и Linux, или API на основе объектной модели


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

Отличительные черты С#
Хотя внешне наиболее очевидной особенностью C# является свойственный
семейству C синтаксис, возможно, более примечательно то, что он был
первым языком, разработанным специально для использования в среде
выполнения CLR. Как следует из названия, CLR достаточно гибок для под-
держки множества языков. Однако есть важное различие между языком,
который был расширен для поддержки CLR, и языком, который создан для
работы в CLR. Это можно увидеть на примере расширений .NET в компи-
ляторе C++ Microsoft: синтаксис использования этих функций заметно
отличается от стандартного C++, что показывает четкое различие между
собственно средой C++ и внешней средой CLR. Но даже отсутствие отличий
в синтаксисе2 не гарантирует разногласий, когда две среды будут работать
по-разному. Например, если вам нужна коллекция чисел с динамическим
изменением размера, то какой класс коллекции в C++ следует использовать:
такой как vector<int> или один из .NET, например List<int>? Какой бы из
них вы ни выбрали, он не всегда будет правильным решением: библиотеки
С++ не будут знать, что делать с коллекцией .NET, тогда как API .NET не
смогут использовать тип из C++.
C# поддерживает и среду выполнения .NET, и библиотеку классов, поэтому
таких дилемм не возникает. В только что рассмотренном сценарии List<int>

Первый набор Microsoft.NET-расширений для C++ больше напоминал обычный


2

C++. Оказалось, что использование имеющегося синтаксиса для чего-то совершенно


отличного от обычного C++ создавало путаницу, поэтому Microsoft отказалась от
первой системы (Managed C++) в пользу более нового и самобытного синтаксиса,
который называется C++/CLI.
20    Глава 1. Знакомство с языком C#

вне конкуренции. При использовании библиотеки классов .NET не возни-


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

Управляемый код и CLR


Долгие годы самым распространенным способом работы компилятора были
обработка исходного кода и создание выходных данных в форме, которая
могла бы выполняться непосредственно центральным процессором компью-
тера. Компиляторы генерировали машинный код, т. е. серию инструкций
в некоем двоичном формате, понятном процессору компьютера. Многие
компиляторы до сих пор работают таким образом, а вот компилятор C# —
нет. Вместо этого он использует модель, содержащую управляемый код.
При использовании управляемого кода компилятор не генерирует машин-
ный код, который выполняет процессор. Вместо этого он создает форму
двоичного кода, называемого промежуточным языком (intermediate language,
IL). Сам же исполняемый двоичный файл обычно, хотя и не всегда, создается
позже, во время выполнения. Использование IL позволяет использовать
функции, которые трудно или даже невозможно реализовать в более тра-
диционной модели.
Возможно, наиболее заметным преимуществом управляемой модели яв-
ляется то, что выходные данные компилятора не привязаны к конкретной
архитектуре процессора. Вы можете написать .NET-компонент, который
может работать на 32-битной архитектуре x86, которую ПК использовали
десятилетиями, но будет так же хорошо работать и в более современном
64-битном ее обновлении (x64) и даже на совершенно иных платформах,
таких как ARM. (Например, в .NET Core появилась возможность работы
на устройствах на базе ARM, таких как Raspberry Pi.) С языком, который
компилируется непосредственно в машинный код, вам потребовалось бы
создавать различные бинарные файлы для каждого из них. Но с .NET вы
можете скомпилировать один компонент, который может работать не только
на любой из них, но и на платформах, которые не поддерживались на мо-
Отличительные черты С#   21

мент компиляции, при условии, если подходящая среда выполнения стала


доступна в будущем. В целом любое улучшение в генерации кода CLR —
будь то поддержка новых архитектур процессоров или просто повышение
производительности для уже существующих — мгновенно идет на пользу
всем языкам .NET. Например, более старые версии CLR не использовали
преимущества расширений векторной обработки, доступные на современ-
ных процессорах x86 и x64, но в текущих версиях они часто используются
при генерации кода для циклов. От этого выиграл любой код, исполняемый
в текущих версиях .NET Core, включая тот, который был написан за годы
до того, как было добавлено это усовершенствование.
Точный момент, когда CLR генерирует исполняемый машинный код, мо-
жет варьировать. Как правило, в нем используется подход, называемый
JIT-компиляцией (just-in-time), при котором каждая отдельная функция
компилируется при первом запуске. Тем не менее это не является обяза-
тельным условием. Есть различные способы, которыми код .NET может
быть скомпилирован заранее, ahead oftime (AoT). Есть инструмент под
названием NGen, который способен делать это уже после установки. Прило-
жения магазина Windows, созданные для обобщенной платформы Windows
(UWP), используют инструменты сборки .NET Native, которые делают это
раньше, в рамках сборки. .NET Core 3.0 добавляет новый инструмент под
названием crossgen, который позволяет любому приложению .NET Core
(а не только приложениям UWP) использовать генерацию собственного
кода во время сборки. Тем не менее генерация исполняемого кода все еще
может происходить во время выполнения, даже когда вы используете эти
инструменты3 — многоуровневая функция компиляции во время выполне-
ния может решить динамически перекомпилировать метод, чтобы лучше
оптимизировать его для текущей выполняемой задачи. (Это может быть
сделано независимо от того, используете ли вы JIT или AoT.) Виртуали-
зированная природа управляемого выполнения предназначена для того,
чтобы сделать такие вещи прозрачными для вашего кода, хотя иногда все
это может сказываться не только на производительности. Например, вир-
туализированное выполнение оставляет некоторую свободу в том, когда
и как среда исполнения выполняет определенную работу по инициализации,
и вы иногда можете наблюдать результаты ее оптимизации, приводящие
к удивительным результатам.

3
Исключением является .NET Native: он не поддерживает JIT, поэтому там отсутствует
многоуровневая компиляция.
22    Глава 1. Знакомство с языком C#

Управляемый код обладает единой информацией о типах. Этого требуют


форматы файлов, определяемые интерфейсом командной строки (CLI), по-
тому что только так можно использовать определенные функции времени
выполнения. Например, .NET предлагает различные службы автоматической
сериализации, в которых объекты могут быть преобразованы в двоичные или
текстовые представления их состояния, и эти представления позднее могут
быть превращены в объекты, возможно, уже на другом компьютере. Такая
служба опирается на полное и точное описание структуры объекта, что га-
рантированно присутствует в управляемом коде. Информация о типе может
использоваться и другими способами. К примеру, платформы юнит-теста
могут использовать ее для проверки кода в тестовом проекте и реализации
всех написанных вами юнит-тестов. Опорой для этого служат службы от-
ражения CLR, речь о которых пойдет в главе 13.
Хотя тесная связь C# со средой выполнения является одной из основных
определяющих черт языка, она не единственная. В основе разработки C#
лежит определенная философия.

Универсальность вместо специализации


C# оказывает предпочтение универсальным языковым возможностям,
нежели специализированным. За прошедшие годы Microsoft несколько
раз расширяла C#, и разработчиками языка всегда подразумевались конк­
ретные сценарии для новых возможностей. Однако они всегда старались,
чтобы каждый новый добавляемый элемент был полезен и за пределами
этих основных сценариев.
Например, несколько лет назад Microsoft решила добавить в C# функции,
которые добавляют в него интеграцию с базами данных. Появившаяся в ре-
зультате технология Language Integrated Query (LINQ, описанная в главе 10),
безусловно, на это нацелена, но Microsoft достигла этого, не добавляя в язык
непосредственной поддержки доступа к данным. Вместо этого Microsoft
представила ряд довольно разномастных возможностей. К ним относятся
улучшенная поддержка идиом функционального программирования, воз-
можность добавления новых методов к существующим типам без использо-
вания наследования, поддержка анонимных типов, возможность получения
объектной модели, представляющей структуру выражения, и введение
синтаксиса запроса. Последний пункт имеет очевидную связь с доступом
к данным, но остальные не так просто соотнести с поставленной задачей.
Тем не менее они могут использоваться совместно таким образом, который
Стандарты и реализация C#   23

значительно упрощает некоторые задачи доступа к данным. Но все функции


полезны и сами по себе, поэтому помимо поддержки доступа к данным они
предполагают гораздо более широкий диапазон сценариев. Например, эти
дополнения (появившиеся в C# 3.0) значительно упростили обработку
списков, множеств и других групп объектов, поскольку новые функции
работают для коллекций любого типа, а не только для баз данных.
Примером такой философии универсальности стала языковая особенность,
присутствующая в C# в качестве прототипа, который в конечном итоге не
был реализован разработчиками окончательно. Эта функция позволила бы
добавлять XML непосредственно в исходный код, встраивая выражения для
вычисления значений определенных фрагментов контента прямо во время
выполнения. Прототип компилировал это в код, который генерировал за-
вершенный XML во время исполнения.
Microsoft Research обнародовала эту функцию, но в конечном итоге та не
вошла в C#, хотя позже появилась в другом языке Microsoft .NET — Visual
Basic, который также получил некоторые специализированные функции
запросов для извлечения информации из документов XML. Внедренные
выражения XML — это относительно узкоспециализированное средство,
полезное только при создании документов XML. Что касается запросов
к XML-документам, C# поддерживает эту функциональность с помощью
обобщенных функций LINQ, что не требует каких-либо специфических для
XML функций языка. Звезда XML склонилась к закату, когда эта языковая
концепция была поставлена под вопрос из-за того, что многие ее функции
узурпировал JSON (который, несомненно, будет заменен чем-то новым
в ближайшие годы). Если бы встроенный XML в свое время попал в C#, то
теперь он выглядел бы весьма анахроничной диковинкой.
Новые функции, добавленные в последующих версиях C#, применяющих
то же правило. Например, функции деконструкции и сопоставления с ша-
блоном, добавленные в С# в версиях 7 и 8, направлены на то, чтобы облег-
чить жизнь с помощью едва уловимых, но полезных методов, без привязки
к какой-то конкретной области применения.

Стандарты и реализация C#
Прежде чем приступить к рассмотрению реального кода, нужно выяснить, на
какую реализацию C# и на какую среду выполнения ориентироваться. Есть
спецификации, которые определяют язык и поведение во время выполнения
24    Глава 1. Знакомство с языком C#

для всех реализаций C#, как описано во врезке «C#, CLR и стандарты».
Это сделало возможным появление нескольких реализаций C# и среды
выполнения. На момент написания книги наиболее широко распростране-
ны три из них: .NET Framework, .NET Core и Mono. Путаницу вносит то,
что за всеми тремя стоит Microsoft, но стоит сказать, что изначально все
планировалось не так.

C#, CLR И СТАНДАРТЫ


Орган по стандартизации ECMA опубликовал две ОС-независимые спецификации,
которые эффективно определяют язык C# и среду выполнения: ECMA-334 — это
спецификация языка C#, а ECMA-335 определяет общеязыковую инфраструктуру
(Common Language Infrastructure, CLI), виртуальную среду, в которой работают
программы на C# и  других языках .NET. Версии этих документов также были
опубликованы Международной организацией по стандартизации как ISO/IEC
23270:2018 и  ISO/IEC 23271:2012 соответственно. Из-за числа «2018» может
показаться, что спецификация C# более современна, чем она есть на самом деле:
языковые стандарты ECMA и ISO соответствуют версии 5.0 C#. На момент напи-
сания книги ECMA работает над обновленной спецификацией языка. Но следует
иметь в виду, что эти конкретные стандарты обычно на несколько лет отстают от
современного положения дел. Хотя стандарт IEC CLI имеет еще более старую
дату  — 2012 (как и  ECMA-335)  — спецификации среды выполнения меняются
реже, чем язык, поэтому спецификация CLI намного ближе к текущим реализациям,
несмотря на названия, указывающие на обратное.
ECMA-335 определяет интерфейс командной строки, который включает в себя
все поведение, требуемое от среды выполнения (например, CLR .NET или сре-
ды выполнения Mono), и  многое другое. Он определяет не только поведение
среды выполнения (которое он называет Virtual Execution System, или VES), но
также формат файлов для исполняемых и библиотечных файлов, а также общую
систему типов (Common Type System). Кроме того, он определяет подмножество
CTS, которое языки, как ожидается, должны поддерживать, дабы гарантировать
взаимодействие языков, называемое общеязыковой спецификацией (Common
Language Specification, CLS).

Таким образом, можно сказать, что реализация CLI от Microsoft — это скорее .NET,
нежели просто CLR, хотя .NET включает в себя множество дополнительных функ-
ций, не входящих в спецификацию CLI. (Например, библиотека классов, которую
требует CLI, составляет лишь небольшое подмножество гораздо более крупной
библиотеки .NET.) CLR фактически является VES для .NET, но вы вряд ли когда-
либо увидите термин VES, используемый вне спецификации, почему в этой книге
я в основном и говорю о CLR (или просто среде выполнения). Понятия CTS и CLS
используются более широко, и я еще вернусь к ним в этой книге.
Стандарты и реализация C#   25

Проект Mono был запущен в 2001 году и изначально создавался не Microsoft.


(Вот почему у него отсутствует .NET в названии — он может использовать
название C#, потому что именно так стандарты именуют язык, а .NET — это
торговая марка Microsoft.) Изначально перед Mono стояла задача позволить
разработку приложений для Linux на C#, но позже добавилась поддержка
iOS и Android. Этот важный шаг помог Mono найти свою нишу, поскольку
теперь он в основном используется для создания кросс-платформенных
приложений для мобильных устройств на C#. Это изначально был проект
с открытым исходным кодом, который за время своего существования был
поддержан различным компаниями. С 2011 года и до момента написания
находится под управлением компании Xamarin. Microsoft приобрела Xamarin
в 2016 году и на данный момент сохраняет его как отдельный бренд, по-
зиционируя свою среду выполнения Mono как способ запуска кода C# на
мобильных устройствах.
А что насчет двух других реализаций, в названии которых есть .NET?

Несколько .NET от Microsoft (временных)


В течение примерно семи лет всегда была только одна текущая версия .NET,
но с 2008 года все стало усложняться. Сначала это было связано с профиль-
ными вариантами .NET, предназначенными для появляющихся и исчезающих
платформ пользовательского интерфейса, в том числе Silverlight, несколь-
кими вариантами Windows Phone, а также представленными в Windows 8
приложениями магазина (Store Applications). Хотя некоторые из них еще
поддерживаются, все они тупиковые, за исключением приложений магазина,
которые превратились в обобщенную платформу Windows (UWP). UWP
перешел на .NET Core, из-за чего другие ветви .NET устарели.
Но даже игнорируя эти фактически несуществующие ответвления .NET, на
момент написания этой книги Microsoft все еще выпускает две текущие версии
.NET: .NET Framework (только для Windows, с закрытым исходным кодом)
и .NET Core (кросс-платформенный, с открытым исходным кодом). В мае
2019 года Microsoft объявила, что намерена вернуться к единой текущей вер-
сии в ноябре 2020 года. В долгосрочной перспективе это уменьшит путаницу,
а вот в ближайшей перспективе это лишь еще больше усложнит ситуацию, так
как появится дополнительная версия, которую следует иметь в виду.
Один слегка озадачивающий аспект всего этого — незначительные различия
в именах разных .NET. Первые 15 лет .NET Framework означал сочетание
26    Глава 1. Знакомство с языком C#

двух вещей: среды выполнения и библиотеки классов. Его среда выполнения


называлась CLR. Библиотека классов называлась различными именами,
включая Base Class Library (BCL; вводящее в заблуждение имя, поскольку
спецификации ECMA определяют термин «BCL» как нечто более узкое)
или Framework Class Library.
Сегодня у нас есть еще и .NET Core. Его среда выполнения называется .NET
Core Common Language Runtime (или просто CoreCLR), и это вполне одно-
значное имя: мы можем говорить о .NET Core CLR или .NET Framework CLR,
и всегда очевидно, что мы имеем в виду. И на протяжении всей этой книги,
когда я говорю о CLR или среде выполнения без каких-либо уточнений, это
происходит потому, что речь идет о чем-то, что относится к обеим реализаци-
ям. К сожалению, .NET Core называет свою библиотеку классов .NET Core
Framework (или CoreFX). Это не конструктивно, потому что еще до .NET
Core слово Framework использовалось для обозначения комбинации CLR
и библиотеки. И, чтобы еще больше осложнить ситуацию, многие в Microsoft
теперь называют .NET Framework «desktop», чтобы дать понять, что речь
не идет о .NET Core. (Это всегда сбивало с толку, потому что многие люди
используют эту «desktop»-версию для серверных приложений. Более того,
первый в истории выпуск .NET Core был предназначен для UWP, поддер-
живающего только приложения Windows. Прошел год, прежде чем Microsoft
выпустила версию, которая способна на что-то большее4. И теперь, когда
в .NET Core 3.0 на Windows добавлена поддержка двух платформ пользо-
вательского интерфейса .NET для настольных ПК — Windows Presentation
Foundation (WPF) и Windows Forms, — большинство новых приложений
для настольных компьютеров будут ориентированы на .NET Core. а не так
называемую .NET «desktop».) На всякий случай, если что-то не до конца
ясно, в табл. 1.1 обобщается текущая ситуация.

Таблица 1.1. Названия компонентов .NET


Платформа Среда выполнения Библиотека классов
.NET Framework (она же .NET desktop) .NET CLR .NET Framework Class Library
.NET Core .NET Core CLR .NET Core Framework

Как ни странно, этот самый первый релиз с поддержкой UWP в 2015 году, по-
4

видимому, так и не получил официального номера версии. Выпуск .NET Core 1.0
датируется июнем 2016 года, т. е. примерно через год.
Стандарты и реализация C#   27

В 2020 году, если Microsoft будет придерживаться своего замысла, все


имена снова будут скорректированы, а .NET Core и .NET Framework будут
заменены простым «.NET»5. На момент написания этой книги Microsoft
не определилась с конкретными именами для соответствующих сред вы-
полнения и библиотек.
Но до этого времени у нас есть две «текущие» версии, каждая из которых
способна делать то, что другая не умеет, и именно поэтому обе поставляются
одновременно. Платформа .NET Framework работает только в Windows,
тогда как .NET Core поддерживает Windows, macOS и Linux. Хотя это дела-
ет .NET Framework менее используемым, это же означает, что он способен
поддерживать некоторые специфичные для Windows функции. Например,
есть раздел библиотеки классов .NET Framework, посвященный работе со
службами синтеза и распознавания речи Windows. Это невозможно в .NET
Core, поскольку он может работать в Linux, где эквивалентные функции либо
не существуют, либо слишком различаются, чтобы быть представленными
через один и тот же .NET API.
.NET, который должен появиться в 2020 году, по сути является следующей
версией .NET Core, только с более умным названием. С .NET Core связана
большая часть разработок .NET за последние несколько лет. .NET Framework
по-прежнему полностью поддерживается, но уже начинает отставать. На-
пример, версия 3.0 платформы веб-приложений Microsoft, ASP.NET Core,
будет работать только на .NET Core, но уже не на .NET Framework. Таким
образом, уход со сцены .NET Framework и повышение .NET Core до един-
ственно верного .NET — это неизбежное завершение процесса, который
продолжается уже несколько лет.

Ориентация на различные версии .NET посредством .NET Standard


Разнообразие сред выполнения, каждая из которых имеет свои собственные
версии библиотек классов, представляет собой проблему для тех, кто хочет
сделать свой код доступным для других разработчиков. На http://nuget.org
имеется репозиторий пакетов для компонентов .NET, где Microsoft публи-
кует все создаваемые ею библиотеки .NET, которые не встроены в сам .NET,
и где большинство разработчиков .NET публикуют библиотеки, которыми
хотели бы поделиться. Так какую же версию следует использовать вам?
У этого вопроса две грани: есть не только конкретная реализация (.NET

10 ноября 2020 года действительно была выпущена .NET 5.0. — Примеч. ред.
5
28    Глава 1. Знакомство с языком C#

Core, .NET Framework, Mono), но и версия (например, .NET Core 2.2 или 3.0,
.NET Framework 4.7.2 или 4.8). И есть более старые варианты .NET, такие
как Windows Phone или Silverlight, — Microsoft по-прежнему поддерживает
многие из них, включая постоянную поддержку через различные библиотеки
в NuGet. Многие авторы популярных пакетов с открытым исходным кодом,
распространяемых через NuGet, также поддерживают множество старых
типов и версий платформы.
Первоначально люди справлялись с проблемой версий, создавая несколько
вариантов своих библиотек. Когда вы распространяете библиотеки .NET
через NuGet, то можете встраивать в пакет несколько наборов двоичных
файлов, ориентированных на разные .NET. Тем не менее одна из основных
проблем заключается в том, что по мере появления новых форм .NET суще-
ствующие на протяжении многих лет библиотеки не будут работать во всех
новых средах выполнения. Компонент, написанный для .NET Framework 4.0,
будет работать на всех последующих версиях .NET Framework, но не на .NET
Core. Даже если исходный код компонента полностью совместим с .NET Core,
вам потребуется отдельная версия, скомпилированная для этой платформы.
И если автор используемой вами библиотеки не добавил явную поддержку
.NET Core, это помешает ее использовать. Это плохо для всех. Авторы ком-
понентов встали перед необходимостью создавать новые варианты своих
компонентов, и поскольку это зависит от того, есть ли у авторов желание
и время на эту работу, пользователи сталкиваются с тем, что не все компо-
ненты, которые они хотят применить, доступны на нужной им платформе.
Чтобы избежать этого, Microsoft представила .NET Standard, который
определяет общие подмножества поверхности API библиотеки классов .NET.
Если пакет NuGet нацелен, скажем, на .NET Standard 1.0, это гарантирует, что
он сможет работать на .NET Framework версий 4.5 или новее, .NET Core 1.0
или новее или Mono 4.6 или новее. И что очень важно, в случае появления
очередного варианта .NET существующие компоненты будут работать без
изменений, даже если эта новая платформа еще не существовала на момент
написания. Это истинно до тех пор, пока новый вариант поддерживает .NET
Standard 1.0.
Для обеспечения самого широкого охвата библиотеки .NET, опублико-
ванные в NuGet, должны ориентироваться на самую низкую версию .NET
Standard из возможных. Версии с 1.1 по 1.6 постепенно добавили больше
функциональности в обмен на поддержку меньшего диапазона платформ.
(Например, если вы хотите использовать компонент .NET Standard 1.3

Вам также может понравиться