.NET Native и компиляция

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

Создание собственных двоичных файлов

Приложение, которое предназначено для платформа .NET Framework и не компилируется с помощью цепочки средств .NET Native, состоит из сборки приложения, которая включает в себя следующее:

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

  • Код реализации. Он состоит из кодов операций промежуточного языка (IL). Во время выполнения JIT-компилятор преобразует его в машинный код для целевой платформы.

В дополнение к основной сборке приложения для приложения требуется следующее:

  • Все дополнительные библиотеки классов и сборки сторонних производителей, необходимые приложению. Эти сборки точно так же включают метаданные, описывающие сборку, ее типы и члены, а также IL-код, который реализует все члены типов.

  • Библиотека классов .NET Framework. Это коллекция сборок, устанавливаемая на локальном компьютере при установке платформы .NET Framework. Сборки, включенные в библиотеку классов .NET Framework, содержат полный набор метаданных и кода реализации.

  • Среда CLR. Это коллекция библиотек DLL, которые выполняют такие операции, как загрузка сборок, управление памятью, сбор мусора, обработка исключений, JIT-компиляция, удаленное и локальное взаимодействие. Как и библиотека классов, среда выполнения устанавливается на локальном компьютере в процессе установки .NET Framework.

Обратите внимание, для успешного выполнения приложения должны присутствовать: вся среда CLR, метаданные, IL-код для всех типов в сборках, специфичных для приложения, сторонние сборки и системные сборки.

JIT-компиляция

Входными данными для цепочки инструментов .NET Native является приложение UWP, созданное компилятором C# или Visual Basic. Иными словами, цепочка инструментов .NET Native начинает выполнение, когда компилятор языка завершает компиляцию приложения UWP.

Совет

Так как входные данные .NET Native — промежуточный язык (IL) и метаданные, записанные в сборки управляемого кода, то вы по-прежнему можете выполнять создание собственного кода или другие пользовательские операции посредством событий "перед сборкой" и "после сборки" или путем изменения файла проекта MSBuild.

Однако не поддерживаются инструменты, изменяющие IL-код и тем самым препятствующие анализу приложения IL цепочкой инструментов .NET. Средства запутывания являются наиболее важными средствами этого типа.

Во время преобразования приложения из промежуточного языка в машинный код цепочка инструментов .NET Native выполняет следующие операции:

  • Для некоторых ветвей кода заменяется код, основанный на отражении и метаданных, на статический машинный код. Например, если тип значения не переопределяет метод <xref:System.ValueType.Equals%2A?displayProperty=nameWithType>, то стандартный тест на эквивалентность использует отражение для получения объектов <xref:System.Reflection.FieldInfo>, представляющих поля этого типа значения, а затем сравнивает значения полей двух экземпляров. При компиляции в машинный код цепочка инструментов .NET Native заменяет код отражения и метаданные на статическое сравнение значений полей.

  • Везде, где возможно, делается попытка исключить все метаданные.

  • .NET Native включает в заключительные сборки приложения только тот код реализации, который фактически вызывается приложением. Особенно это касается кода сторонних библиотек и кода в библиотеке классов .NET Framework. В результате приложение больше не зависит от сторонних библиотек или всей библиотеки классов .NET Framework; вместо этого код сторонних библиотек и библиотек классов .NET Framework теперь является локальным для приложения.

  • .NET Native заменяет полную среду CLR на оптимизированную среды выполнения, которая в первую очередь содержит сборщика мусора. Оптимизированная среда выполнения находится в библиотеке mrt100_app.dll, которая является локальной для приложения и имеет размер только несколько сотен килобайт. Это возможно потому, что статическое связывание устраняет необходимость во многих операциях, реализуемых средой CLR.

    Примечание

    .NET Native использует тот же сборщик мусора, что и стандартная среда CLR. В сборщике мусора .NET Native фоновая сборка мусора включена по умолчанию. Дополнительные сведения о сборке мусора см. в разделе Основы сборки мусора.

Важно!

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

Итоговое приложение, созданное цепочкой инструментов .NET Native, записывается в каталог ilc.out в каталоге отладки или выпуска каталога проекта. Итоговое приложение состоит из следующих файлов:

  • <appName> exe — исполняемый файл-заглушка, который просто передает управление Специальному Main экспорту в <appName> DLL.

  • <appName>. DLL — библиотека динамической компоновки Windows, которая содержит весь код приложения, а также код из библиотеки классов платформа .NET Framework и любых сторонних библиотек, от которых зависит зависимость. Итоговое приложение также содержит код поддержки, например код, необходимый для взаимодействия с Windows и сериализации объектов в приложении.

  • mrt100_app.dll — оптимизированная среда выполнения, которая предоставляет сервисы реального времени, такие как сборка мусора.

APPX-манифестом приложения регистрируются все зависимости. Помимо EXE-файла приложения, библиотеки DLL и среды выполнения mrt100_app.dll, которые входят непосредственно в APPX-пакет, пакет включает два дополнительных файла:

  • msvcr140_app.dll — библиотека языка C времени выполнения (CRT), используемая mrt100_app.dll. Она включается по ссылке платформы в пакете.

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

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

  • Отражение.

  • динамический вызов или вызов посредством позднего связывания;

  • сериализация и десериализация;

  • COM-взаимодействие.

Если во время выполнения метаданные или необходимый код отсутствуют, среда выполнения .NET Native вызывает исключение. Чтобы предотвратить эти исключения, убедитесь, что цепочка инструментов .NET Native включает необходимые метаданные и код реализации, с помощью файла директив времени выполнения. Это XML-файл, указывающий элементы программы, чьи метаданные или код реализации должны быть доступны во время выполнения, и назначающий им политику времени выполнения. Ниже приведен файл директив среды выполнения по умолчанию, который добавляется в проект UWP, скомпилированный цепочкой инструментов .NET Native:

<Directives xmlns="http://schemas.microsoft.com/netfx/2013/01/metadata">
  <Application>
    <Assembly Name="*Application*" Dynamic="Required All" />
  </Application>
</Directives>

Благодаря этому становятся доступными для отражения и динамического вызова все типы и их члены во всех сборках в пакете приложения. Однако это не позволяет выполнять отражение или динамическую активацию типов в сборках библиотеки классов .NET Framework. В большинстве случаев этого достаточно.

См. также раздел