Основы архитектуры .NET: CLR, CLI, CTS, CLS и процесс выполнения кода в C#

Представьте, что вы строите дом. У вас есть чертежи (исходный код), различные материалы (типы данных), строители (компиляторы) и бригада, которая следит за качеством, безопасностью и эффективностью всего процесса (среда выполнения). Платформа .NET Framework в мире разработки программного обеспечения играет роль целой строительной компании, а Common Language Runtime (CLR) — её главного прораба, который обеспечивает бесперебойное и качественное выполнение работ.

Понимание архитектуры .NET — это не просто академическое упражнение. Это фундамент, на котором строится любое серьёзное приложение на C#. Для студента, осваивающего основы программирования и C#, это знание критически важно. Оно позволяет не только писать код, но и понимать, *как* он работает «под капотом», почему возникают те или иные ошибки, как оптимизировать производительность и создавать надёжные, масштабируемые системы.

Цели данного материала:

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

Этот конспект призван стать вашим надёжным путеводителем в мир архитектуры .NET, раскрывая не только «что», но и «почему» и «как» работают его ключевые компоненты.

Common Language Runtime (CLR): Сердце выполнения .NET-приложений

Common Language Runtime (CLR) — это не просто абстрактная концепция, а живое, динамичное сердце любой .NET-программы. Можно сказать, что CLR — это среда выполнения, которая не просто запускает ваш код, но и предоставляет целый комплекс критически важных сервисов, значительно упрощающих разработку, повышающих надёжность и обеспечивающих безопасность приложений. Она выступает в роли своеобразного «управляющего» для всего кода, написанного на .NET-совместимых языках, обеспечивая тем самым его унифицированное и предсказуемое поведение на различных платформах.

Основные функции и службы CLR

Чтобы понять истинную мощь CLR, необходимо рассмотреть её ключевые функции и службы, каждая из которых играет свою незаменимую роль в жизненном цикле приложения:

  • Автоматическое управление памятью (Automatic Memory Management): Это, пожалуй, одна из самых заметных и ценных функций CLR, реализуемая через механизм сборки мусора (Garbage Collection). Разработчику не нужно вручную выделять и освобождать память, как, например, в C++. CLR автоматически отслеживает объекты, которые больше не используются, и освобождает занимаемую ими память, предотвращая утечки и снижая вероятность ошибок, связанных с управлением памятью.
  • Управление потоками (Thread Management): CLR предоставляет средства для создания, запуска, приостановки, возобновления и завершения потоков выполнения, а также для синхронизации доступа к общим ресурсам. Это позволяет создавать многопоточные приложения, эффективно использующие ресурсы современных многоядерных процессоров.
  • Обработка исключений (Exception Handling): CLR предлагает унифицированную модель обработки исключений, которая работает прозрачно для всех .NET-совместимых языков. Это означает, что исключение, возникшее в коде на C#, может быть перехвачено и обработано в коде на F# или VB.NET, что значительно повышает надёжность и предсказуемость поведения приложения.
  • Безопасность (Security): CLR включает мощную систему безопасности, которая контролирует, какие действия может выполнять код. Это не только стандартная безопасность на основе ролей, но и более глубокие механизмы, такие как контроль доступа кода (Code Access Security – CAS), который ограничивает возможности кода в зависимости от его происхождения или цифровой подписи.
  • Отладка (Debugging): CLR предоставляет обширные возможности для отладки приложений. Специализированные API позволяют отладчикам подключаться к процессу выполнения, останавливать его в точках останова, инспектировать переменные и стек вызовов, значительно упрощая поиск и исправление ошибок.
  • Удаленное взаимодействие (Remoting): Эта функция позволяет объектам, находящимся в разных доменах приложений, процессах или даже на разных машинах, взаимодействовать друг с другом так, как если бы они находились в одном адресном пространстве. Хотя в современных .NET-приложениях эта технология постепенно заменяется более новыми подходами (например, gRPC, REST), она была краеугольным камнем для распределённых систем в ранних версиях .NET Framework.

Концепция управляемого кода и его преимущества

В основе всего, что делает CLR, лежит концепция управляемого кода (managed code). Это код, который компилируется в Common Intermediate Language (CIL) и выполняется под полным контролем CLR. В отличие от неуправляемого кода (например, приложений на C++), управляемый код не взаимодействует напрямую с операционной системой или аппаратным обеспечением, а делегирует эти задачи CLR, которая выступает посредником.

Преимущества такого подхода многочисленны и значительны:

  • Межъязыковая интеграция (Interoperability): Поскольку все .NET-совместимые языки (C#, VB.NET, F#, C++/CLI) компилируются в один и тот же CIL, они могут беспрепятственно взаимодействовать друг с другом. Класс, написанный на C#, может быть унаследован и использоваться в VB.NET, а исключение, выброшенное в F#, обработано в C#. Это открывает возможности для создания сложных систем, где каждый компонент написан на языке, наиболее подходящем для конкретной задачи.
  • Кросс-языковая обработка исключений (Cross-Language Exception Handling): Как упоминалось выше, унифицированная модель обработки исключений CLR позволяет легко обрабатывать исключения, возникающие в коде, написанном на разных языках, что делает приложения более устойчивыми.
  • Улучшенная безопасность (Enhanced Security): CLR проводит строгую проверку типобезопасности и валидацию кода перед его выполнением, что помогает предотвратить множество распространённых уязвимостей, таких как переполнение буфера. Кроме того, управляемые сборки по умолчанию используют функции предотвращения выполнения данных (DEP) и рандомизации адресного пространства (ASLR), которые значительно повышают устойчивость к эксплойтам.
  • Поддержка управления версиями и развертывания (Version and Deployment Support): CLR упрощает развертывание приложений и управление версиями библиотек (сборок) благодаря механизму строгих имён (Strong-Named Assemblies) и GAC (Global Assembly Cache), что позволяет избежать «DLL Hell» — распространённой проблемы в неуправляемых средах, где конфликты версий библиотек приводили к неработоспособности приложений.
  • Упрощенная модель взаимодействия компонентов (Simplified Component Interoperability): Одной из наиболее сложных задач в традиционной разработке было взаимодействие с существующими неуправляемыми компонентами (например, COM-объектами или функциями Windows API). CLR предоставляет механизмы, такие как Platform Invoke (P/Invoke), которые значительно упрощают этот процесс. P/Invoke позволяет управляемому коду вызывать экспортированные функции из неуправляемых динамически подключаемых библиотек (DLL). Разработчик объявляет управляемый прототип неуправляемой функции, а CLR автоматически выполняет маршалинг данных (преобразование форматов данных между управляемым и неуправляемым кодом), позволяя безопасно и эффективно взаимодействовать с нативными API. Это скрывает сложные низкоуровневые детали, такие как управление указателями и выравнивание структур, делая взаимодействие с неуправляемым миром гораздо более доступным.

Управление памятью: Сборка мусора (Garbage Collection) в CLR

Автоматическая сборка мусора (GC) — это краеугольный камень управления памятью в .NET. Она освобождает разработчика от утомительной и подверженной ошибкам задачи ручного выделения и освобождения памяти, тем самым предотвращая утечки и повышая стабильность приложения. GC в CLR работает по принципу отслеживания объектов: она определяет, какие объекты всё ещё доступны из корневых ссылок (например, локальных переменных, статических полей), и помечает все остальные объекты как «мусор», подлежащий удалению.

CLR предоставляет несколько типов сборки мусора, оптимизированных для различных сценариев:

  • Сборка мусора рабочей станции (Workstation GC): Этот тип GC оптимизирован для клиентских приложений и рабочих станций. Он работает «внутри» процесса приложения, используя его потоки, и стремится обеспечить минимальное влияние на пользовательский интерфейс.
    • Непараллельная (Nonconcurrent) Workstation GC: При выполнении сборки мусора все потоки приложения приостанавливаются до её завершения. Это просто, но может вызывать «замирания» (freezes) пользовательского интерфейса.
    • Параллельная (Concurrent) Workstation GC (или фоновая GC): Это основной режим для клиентских приложений. Она позволяет управляемым потокам продолжать работу во время сборки мусора для поколения 2 (Gen 2). CLR использует дополнительный фоновый поток для выполнения большей части работы по сборке Gen 2, что значительно снижает «замирания» и улучшает отзывчивость приложения. Только в определённые моменты (например, во время «корневого» сканирования) все потоки приложения приостанавливаются на очень короткое время.
  • Сборка мусора сервера (Server GC): Этот тип GC разработан для серверных приложений, которые требуют высокой пропускной способности и масштабируемости. В отличие от Workstation GC, Server GC создает отдельный, выделенный поток сборки мусора для каждого логического процессора на машине. Это позволяет распределить нагрузку GC между ядрами CPU, значительно ускоряя процесс и обеспечивая более агрессивное управление кучей. Server GC выделяет большие сегменты кучи и работает с большими объёмами памяти, что идеально подходит для высоконагруженных серверных служб и микросервисов. Она также быстрее расширяет кучу при нехватке памяти, что критично для приложений с динамически меняющимися потребностями в ресурсах.

Выбор между Workstation GC и Server GC обычно делается через конфигурационный файл приложения (.runtimeconfig.json в .NET Core/.NET 5+ или .config в .NET Framework).

Улучшенная безопасность управляемого кода

Безопасность в .NET — это многоуровневая система, где CLR играет центральную роль, обеспечивая защищённое выполнение управляемого кода. Помимо традиционных механизмов безопасности, таких как аутентификация и авторизация, CLR предлагает фундаментальные средства защиты:

  • Контроль доступа кода (Code Access Security – CAS): Хотя CAS в значительной степени устарел в современных версиях .NET (начиная с .NET Framework 4.0), он был ключевым элементом безопасности в ранних версиях. CAS позволял разработчикам или администраторам ограничивать, какие части кода могут выполнять определённые действия (например, доступ к файловой системе, сетевым ресурсам, реестру). Разрешения выдавались на основе «доказательств» (evidence) — атрибутов сборки, таких как её происхождение (локальный диск, интранет, интернет). Например, код, загруженный из Интернета, мог иметь ограниченные права по сравнению с кодом, установленным локально.
  • Проверка типобезопасности и валидация кода: Перед выполнением JIT-компилятор CLR проводит строгую проверку CIL-кода и метаданных на типобезопасность. Это гарантирует, что код не будет выполнять небезопасные операции, такие как прямой доступ к произвольным областям памяти, приведение типов, которые несовместимы, или вызовы методов на объектах неверного типа. Этот процесс критически важен для предотвращения таких атак, как переполнение буфера, и обеспечивает целостность данных в приложении. Если код не проходит проверку, он не будет скомпилирован и выполнен.
  • Предотвращение выполнения данных (Data Execution Prevention – DEP): Управляемые сборки .NET по умолчанию используют DEP. Это технология безопасности, которая помечает области памяти как «неисполняемые», предотвращая выполнение кода из этих областей. Это значительно усложняет жизнь злоумышленникам, пытающимся внедрить и выполнить вредоносный код в областях данных.
  • Рандомизация адресного пространства (Address Space Layout Randomization – ASLR): Аналогично DEP, ASLR также включена по умолчанию для управляемых сборок. Эта технология рандомизирует адреса в памяти, по которым загружаются исполняемые файлы и библиотеки, а также другие важные структуры данных. Это делает практически невозможным для злоумышленников предсказать точное расположение определённых функций или данных в памяти, что является распространённым методом для проведения атак, таких как атаки с использованием возвратно-ориентированного программирования (ROP).

Таким образом, CLR не просто запускает код, но и создаёт вокруг него многоуровневый защитный барьер, делая .NET-приложения одними из самых безопасных и надёжных в современной разработке.

Процесс компиляции и выполнения кода C# в .NET

Путь кода C# от написания до исполнения — это сложный, но тщательно продуманный двухэтапный процесс. Он разработан для достижения максимальной платформенной независимости, эффективности и безопасности.

От исходного кода C# к Common Intermediate Language (CIL/MSIL)

Первый этап — это преобразование человекочитаемого исходного кода C# в промежуточный язык, который понятен CLR. Этот язык называется Common Intermediate Language (CIL), или иногда Microsoft Intermediate Language (MSIL).

Когда вы пишете программу на C# и компилируете её (например, с помощью компилятора csc.exe или в среде Visual Studio), компилятор C# (точнее, Roslyn — компилятор C# как сервис) не создаёт машинный код напрямую для конкретного процессора (например, Intel x86 или ARM). Вместо этого он генерирует CIL.

Почему CIL?
CIL — это ключевой элемент, обеспечивающий машинную и платформенную независимость. Это язык ассемблера высокого уровня, который содержит инструкции для загрузки, сохранения, инициализации и вызова методов объектов, а также для арифметических и логических операций, управления потоком и обработки исключений. CIL не привязан к конкретной архитектуре CPU или операционной системе. Это означает, что один и тот же CIL-код может быть выполнен на любой платформе, где установлен CLR (будь то Windows, Linux или macOS), без необходимости перекомпиляции для каждой из них.

Пример:

Рассмотрим простейший фрагмент кода C#:

public class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }
}

После компиляции этот код не превратится сразу в команды вида MOV EAX, ... или ADD RBX, .... Вместо этого, метод Add будет представлен на CIL примерно так (упрощённо):

.method public hidebysig instance int32 Add(int32 a, int32 b) cil managed
{
  // Code size       11 (0xb)
  .maxstack  2
  .locals init ([0] int32 CS$1$0000)
  IL_0000:  nop
  IL_0001:  ldarg.1      // Загрузить аргумент 'a' в стек
  IL_0002:  ldarg.2      // Загрузить аргумент 'b' в стек
  IL_0003:  add          // Сложить два верхних элемента стека
  IL_0004:  stloc.0      // Сохранить результат в локальную переменную 0
  IL_0005:  ldloc.0      // Загрузить локальную переменную 0 в стек
  IL_0006:  ret          // Вернуть значение из стека
}

Этот CIL-код хранится внутри исполняемого файла (обычно .exe или .dll), который фактически является переносимым исполняемым файлом (PE-файл), аналогичным стандартным исполняемым файлам Windows, но содержащим не машинный код, а CIL и метаданные.

Метаданные: Информационное сопровождение кода

Просто CIL-кода недостаточно для полноценного выполнения. Чтобы CLR могла корректно работать с кодом, ей необходима исчерпывающая информация о структуре этого кода. Эту информацию предоставляют метаданные.

Метаданные — это данные о данных. В контексте .NET они описывают все типы, члены, ссылки, атрибуты и другие элементы, определённые в коде. Они хранятся вместе с CIL-кодом внутри того же PE-файла. Каждый загруженный CLR-файл содержит метаданные.

Что содержат метаданные?

  • Описания типов: Имена классов, интерфейсов, структур, перечислений, делегатов.
  • Описания членов: Методы, поля, свойства, события с их сигнатурами и модификаторами доступа.
  • Информация о ссылках: Ссылки на другие сборки (DLL), которые использует текущий код.
  • Атрибуты: Дополнительная декларативная информация, применённая к элементам кода (например, [Serializable], [Obsolete]).

Как среда выполнения использует метаданные?
CLR активно использует метаданные на протяжении всего жизненного цикла приложения:

  • Поиск и загрузка классов: CLR использует метаданные для идентификации и загрузки необходимых классов и их членов по мере их использования.
  • Размещение экземпляров в памяти: Метаданные предоставляют CLR информацию о размере и структуре объектов, что позволяет корректно выделять для них память.
  • Разрешение вызовов методов: CLR использует сигнатуры методов из метаданных для правильного разрешения вызовов и передачи аргументов.
  • Принудительное применение безопасности: Метаданные могут содержать информацию о разрешениях, необходимых коду, что позволяет CLR применять политики безопасности (например, CAS).
  • Настройка границ контекста времени выполнения: Метаданные помогают CLR управлять различными аспектами выполнения, такими как контексты активации и удалённое взаимодействие.
  • Отладка и профилирование: Отладчики и профилировщики используют метаданные для получения подробной информации о структуре кода и его поведении во время выполнения.

Метада��ные делают .NET-сборки самодостаточными и облегчают динамическую загрузку и выполнение кода, а также межъязыковое взаимодействие.

JIT-компиляция: Преобразование CIL в машинный код «на лету»

Если CIL — это универсальный язык, то для его выполнения на конкретном процессоре необходимо преобразование в специфический для этого процессора машинный код. Эту задачу выполняет Just-In-Time (JIT) компилятор — ещё один критически важный компонент CLR.

Принцип работы JIT-компилятора:

  1. Компиляция «по требованию»: JIT-компилятор не компилирует весь CIL-код приложения сразу. Он работает «на лету» (just-in-time), компилируя CIL-код в машинный только тогда, когда метод впервые вызывается.
  2. Кэширование: При первом обращении к методу CLR вызывает JIT-компилятор. JIT компилирует соответствующий блок CIL-кода в машинные команды и сохраняет их в динамически выделенном блоке памяти (так называемый «код-кэш»).
  3. Повторное использование: Последующие вызовы этого же метода напрямую используют уже скомпилированный машинный код, что значительно ускоряет выполнение. Это означает, что хотя первый вызов метода может быть немного медленнее из-за накладных расходов на JIT-компиляцию, все последующие вызовы будут выполняться с нативной скоростью.

Процесс верификации типобезопасности:
Перед JIT-компиляцией код должен пройти процесс верификации типобезопасности (type safety verification). Это крайне важный шаг для безопасности и стабильности .NET-приложений. Верификатор CLR проверяет CIL-код и метаданные, чтобы убедиться, что код не будет выполнять небезопасные операции, такие как:

  • Прямой доступ к памяти (например, запись по произвольному адресу).
  • Вызовы методов на объектах несовместимых типов.
  • Переполнение буфера или другие нарушения границ памяти.
  • Некорректная работа с указателями.

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

Оптимизации JIT-компилятора:
Одно из значительных преимуществ JIT-компиляции — это возможность оптимизации кода, учитывая конкретный процессор, на котором выполняется приложение. В отличие от традиционного компилятора, который компилирует для «общей» архитектуры, JIT может:

  • Использовать специализированные инструкции CPU (например, SSE, AVX), если они доступны на целевом процессоре.
  • Применять оптимизации, специфичные для микроархитектуры, для повышения эффективности.
  • Проводить более агрессивные оптимизации, основываясь на данных, доступных только во время выполнения (например, профилирование кода).

AOT-компиляция (Ahead-Of-Time) как альтернатива JIT

Хотя JIT-компиляция является стандартным подходом в .NET, существуют сценарии, когда предварительная компиляция в машинный код (Ahead-Of-Time, AOT) может быть предпочтительнее. AOT-компиляция — это процесс преобразования CIL в машинный код до запуска приложения.

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

Реализации AOT в .NET:

  • NGen (Native Image Generator) в .NET Framework: Это утилита, которая компилирует сборки (DLL и EXE) в нативные образы (машинный код) и устанавливает их в кэш нативных образов (Native Image Cache). При запуске приложения CLR сначала проверяет наличие нативного образа. Если он найден и действителен, используется нативный код, минуя JIT-компиляцию. NGen часто используется для системных библиотек и критически важных приложений для ускорения их запуска.
  • AOT-компиляция в .NET Core/.NET 5+: В современных версиях .NET концепция AOT получила новое развитие.
    • Publish ReadyToRun: Это опция публикации, которая частично AOT-компилирует приложение. Она упаковывает нативные образы для некоторых частей приложения вместе с CIL-кодом. Это улучшает время запуска, но не исключает JIT полностью, так как некоторые части кода всё равно могут быть скомпилированы JIT.
    • .NET Native (для UWP): Более старая, но полная AOT-компиляция, использовавшаяся для приложений Universal Windows Platform (UWP), где весь код компилировался в нативный перед развёртыванием.
    • Native AOT (в .NET 7+): Это наиболее современный и амбициозный подход, позволяющий компилировать всё приложение в один самостоятельный исполняемый файл, содержащий только машинный код и минимальный необходимый рантайм. Это устраняет зависимость от установленного .NET Runtime на целевой машине, значительно сокращает размер приложения и обеспечивает наилучшее время запуска. Native AOT идеально подходит для контейнерных приложений, бессерверных функций и CLI-утилит.

Единство компиляции и компоновки в C#

В традиционных языках, таких как C++, процессы компиляции и компоновки (линковки) чётко разделены: компилятор преобразует исходные файлы в объектные, а компоновщик затем собирает эти объектные файлы вместе с библиотеками в один исполняемый файл.

В C# и .NET эти термины часто используются взаимозаменяемо, и это не случайно. Компилятор C# способен обрабатывать все файлы исходного кода проекта как один блок. В результате он генерирует один или несколько PE-файлов (сборок), которые уже содержат CIL-код и метаданные. Все необходимые ссылки на другие сборки (DLL) разрешаются во время выполнения с помощью метаданных и загрузчика сборок CLR. Таким образом, явного этапа «компоновки» в традиционном смысле, как правило, не требуется, поскольку вся необходимая информация для связи компонентов уже встроена в метаданды сборок.

Эта унифицированная модель значительно упрощает процесс сборки и развертывания .NET-приложений.

Common Language Infrastructure (CLI): Стандарт для кросс-платформенности

Если .NET Framework — это конкретная реализация, то Common Language Infrastructure (CLI) — это абстрактный, всеобъемлющий стандарт, лежащий в её основе. CLI — это не просто набор правил, это манифест кросс-платформенной разработки, разработанный Microsoft, чтобы позволить множеству языков высокого уровня работать на разных компьютерных платформах без необходимости переписывать код для каждой конкретной архитектуры.

Стандартизация CLI и C#

Значение CLI и C# выходит далеко за рамки продуктов Microsoft благодаря их стандартизации. В 2001 году Microsoft представила C# и CLI в Ecma International®, международной организации по стандартизации информационных и коммуникационных систем.

  • Ecma-334 определяет спецификацию языка C#.
  • Ecma-335 определяет спецификацию Common Language Infrastructure (CLI).

Эти стандарты впоследствии были ратифицированы как международные стандарты ISO/IEC 23270 для C# и ISO/IEC 23271 для CLI.

Почему это важно?
Международная стандартизация обеспечивает:

  • Открытость и прозрачность: Спецификации являются общедоступными, что позволяет любому желающему создавать свои реализации.
  • Межплатформенную совместимость: Стандарт гарантирует, что программа, написанная на CIL, будет вести себя одинаково на любой платформе, где реализован CLR, соответствующий стандарту CLI.
  • Широкое принятие: Независимость от одного поставщика стимулирует более широкое распространение языка и инфраструктуры.
  • Влияние на реализации: Стандартизация позволила появиться таким открытым реализациям, как Mono (ранняя кросс-платформенная реализация .NET, разработанная Miguel de Icaza), которая предоставила возможность запускать .NET-приложения на Linux, macOS и других Unix-подобных системах задолго до появления .NET Core. Mono в значительной степени опиралась на стандарты Ecma-334 и Ecma-335 для построения своей собственной среды выполнения и компиляторов.

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

Основные компоненты CLI

CLI — это не монолитная сущность, а набор взаимосвязанных спецификаций, которые работают вместе для создания платформенно-нейтральной среды. Она состоит из четырёх основных компонентов:

  1. Common Type System (CTS): Это фундаментальная часть CLI, которая определяет, как типы данных объявляются, используются и управляются в среде выполнения. CTS обеспечивает единообразие типов между всеми языками, соответствующими CLI. Мы подробно рассмотрим её ниже.
  2. Common Language Specification (CLS): Являясь подмножеством CTS, CLS устанавливает минимальный набор правил и ограничений, которым должен соответствовать любой язык, чтобы считаться «CLS-совместимым» и обеспечивать беспрепятственное взаимодействие с другими CLS-совместимыми языками. Подробнее об этом также будет ниже.
  3. Метаданные (Metadata): Как уже упоминалось, метаданные — это декларативная информация о структуре кода (типах, членах, атрибутах), которая хранится вместе с CIL-кодом и используется средой выполнения для различных целей, от загрузки классов до обеспечения безопасности.
  4. Virtual Execution System (VES): Это, по сути, абстрактная машина, описанная в стандарте CLI, которая обеспечивает среду для выполнения управляемого кода. VES определяет:
    • Поддержку набора встроенных типов данных.
    • Гипотетическую машину с связанной моделью состояния и выполнения.
    • Набор инструкций CIL и правила их выполнения.
    • Механизмы управления потоками и обработки исключений.

Важная взаимосвязь: Microsoft .NET Framework реализует VES в виде Common Language Runtime (CLR). То есть, CLR является конкретной, коммерческой реализацией стандарта VES. Это как если бы VES был чертежом двигателя, а CLR — реальным, работающим двигателем, построенным по этим чертежам.

Компонент CLI Описание
Common Type System (CTS) Определяет, как типы данных объявляются, используются и управляются в среде выполнения, обеспечивая единообразие между языками.
Common Language Specification (CLS) Подмножество CTS, устанавливающее правила для межъязыковой совместимости.
Метаданные (Metadata) Декларативная информация о структуре кода, хранящаяся вместе с CIL и используемая CLR.
Virtual Execution System (VES) Абстрактная машина, описанная в стандарте CLI, обеспечивающая среду для выполнения управляемого кода (реализуется CLR).

Похожие записи