Выбрать главу

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

Роль CIL

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

// Calc.cs

using System;

namespace CalculatorExample {

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

 public class CalcApp {

  static void Main() {

   Calc с = new Calc();

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

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

   // Ждать, пока пользователь не нажмет клавишу ввода.

   Console.ReadLine();

  }

 }

 // C#-калькулятор.

 public class Calc {

  public int Add(int x, int y) {return x + y;}

 }

}

После того как компилятор C# (csc.exe) скомпилирует этот файл исходного кода, вы получите состоящий из одного файла компоновочный блок *.exe, который содержит манифест, CIL-инструкции и метаданные, описывающие каждый аспект классов Calc и CalcApp. Например, если вы откроете этот компоновочный блок с помощью ildasm.exe (мы рассмотрим ildasm.exe немного позже в этой же главе), вы увидите, что метод Add () в терминах CIL представляется так.

.method public hidebysig instance int32 Add(int32 x, int32 y) cil managed

{

 // Code size 8 (0x8)

 .maxstack 2

 .locals init ([0] int32 CS$l$0000)

 IL_0000: ldarg.1

 IL_0001: ldarg.2

 IL_0002: add

 IL_0003: stloc.0

 IL_0004: br.s IL_0006

 IL_0006: ldloc.0

 IL_0007: ret

} // end of method Calc::Add

Не беспокойтесь, если вы пока не в состоянии понять CIL-код для этого метода – в главе 15 будут описаны основы языка программирования CIL. Здесь следует сконцентрироваться на том, что компилятор C# генерирует CIL-код, а не специфические для платформы инструкции.

Напомним теперь, что это верно для всех .NET-компиляторов. Для иллюстрации предположим, что вы создали аналогичное приложение с помощью Visual Basic .NET (VB .NET), а не с помощью C#.

' Calc.vb

Imports System

Namespace CalculatorExample

 ' VB .NET 'Модуль' – это класс, содержащий только ' статические члены.

 Module CalcApp

  Sub Main()

   Dim ans As Integer

   Dim с As New Calc

   ans = c.Add(10, 84)

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

   Console.ReadLine()

  End Sub

 End Module

 Class Calc

  Public Function Add(ByVal x As Integer, ByVal у As Integer) As Integer

   Return x + у

  End Function

 End Class

End Namespace

Если теперь проверить CIL-код для метода Add(), вы обнаружите подобные инструкции (слегка "подправленные" компилятором VB .NET).

.method public instance int32 Add(int32 x, int32 y) cil managed

{

 // Code size 9 (0x9)

 .maxstack 2

 .locals init ([0] int32 Add)

 IL_0000: nop

 IL_0001: ldarg.1

 IL_0002: ldarg.2

 IL_0003: add.ovf

 IL_0004: stloc.0

 IL_0005: br.s IL_0007

 IL_0007: ldloc.0

 IL_0008: ret

} // end of method Calc::Add

Преимущества CIL

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

Кроме того, поскольку CIL демонстрирует независимость от платформы, каркас .NET Framework тоже оказывается независимым от платформы, обеспечивая то, к чему так привыкли разработчики Java (единую базу программного кода, способного работать во многих операционных системах). Фактически уже имеется международный стандарт для языка C#, а значительная часть платформы .NET реализована для множества операционных систем, отличных от Windows (более подробная информация об этом имеется в конце главы). Но, в отличие от Java, .NET позволяет строить приложения, используя язык вашего предпочтения.

Преобразование CIL-кода в набор инструкций, соответствующих платформе

Ввиду того, что компоновочные блоки содержат CIL-инструкции, а не инструкции для конкретной платформы, программный код CIL перед использованием приходится в фоновом режиме компилировать. Объект, который компилирует программный код CIL в инструкции, понятные процессору машины, называется JIT-компилятором (just-in-time – точно к нужному моменту), который иногда "по-дружески" также называют Jitter. Среда выполнения .NET использует JIT-компилятор, соответствующий конкретному процессору и оптимизированный для соответствующей платформы.