Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Базовые технологии платформы .NET.docx
Скачиваний:
13
Добавлен:
03.11.2018
Размер:
614.46 Кб
Скачать

21. Состав и взаимодействие сборок

В платформе .NET сборка (assembly) – это единица развёртывания и контроля версий. Сборка состоит из одного или нескольких программных модулей и, возможно, данных ресурсов. Эти компоненты могут размещаться в отдельных файлах, либо содержаться в одном файле. В любом случае, сборка содержит в некотором из своих файлов манифест, описывающий состав сборки. Будем называть сборку однофайловой, если она состоит из одного файла. В противном случае сборку будем называть многофайловой. Тот файл, который содержит манифест сборки, будем называть главным файлом сборки.

Рис. 3. Однофайловая и многофайловая сборки.

Простые приложения обычно представлены однофайловыми сборками. При разработке сложных приложений переход к многофайловым сборкам даёт следующие преимущества:

  1. Ресурсы (текстовые строки, изображения и т. д.) можно хранить вне приложения, что позволяет при необходимости изменять ресурсы без перекомпиляции приложения.

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

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

Рассмотрим пример создания и использования многофайловых сборок1. Пусть требуется построить консольное приложение, в котором функция Main() печатает на экране строку. Предположим, что эту строку возвращает метод GetText() класса TextClass.

public static class TextClass

{

public static string GetText()

{

return "message";

}

}

Файл TextClass.cs с исходным кодом класса TextClass скомпилируем в виде модуля (обратите внимание на ключ компилятора):

csc.exe /t:module TextClass.cs

После компиляции получим файл-модуль TextClass.netmodule. Далее, создадим консольное приложение (файл MainClass.cs):

using System;

public class MainClass

{

public static void Main()

{

Console.WriteLine("Text from resource");

Console.WriteLine(TextClass.GetText());

}

}

Теперь соберём многофайловую сборку. Ключ компилятора /addmodule позволяет добавить к сборке ссылку на внешний модуль. Он должен применяться для каждого подключаемого модуля.

csc.exe /addmodule:textclass.netmodule MainClass.cs

В итоге получим многофайловую сборку, состоящую из двух файлов: главного файла mainclass.exe и файла-модуля textclass.netmodule. Мы можем создать новую сборку, в которой используется код из модуля textclass.netmodule, то есть сделать этот модуль разделяемым между несколькими сборками. Важное замечание: предполагается, что все файлы, составляющие нашу многофайловую сборку, размещены в одном каталоге.

Рассмотрим вопрос взаимодействия сборок. Как правило, крупные программные проекты состоят из нескольких сборок, связанных ссылками. Среди этих сборок имеется некая основная (обычно оформленная как исполняемый файл *.exe), а другие сборки играют роль подключаемых библиотек с кодом необходимых типов (обычно такие сборки – это файлы с расширением *.dll).

Представим пример, который будет использоваться в дальнейшем. Пусть имеется класс (в файле UL.cs), содержащий «полезную» функцию:

namespace UsefulLibrary

{

public class UsefulClass

{

public void Print()

{

System.Console.WriteLine("Useful function");

}

}

}

Скомпилируем данный класс как библиотеку типов (расширение *.dll):

csc.exe /t:library UL.cs

Пусть основное приложение (файл main.cs) собирается использовать код из сборки UL.dll:

using System;

using UsefulLibrary;

public class MainClass

{

public static void Main()

{

// используем класс из другой сборки

UsefulClass a = new UsefulClass();

a.Print();

}

}

Ключ компилятора /r (или /reference) позволяет установить ссылку на требуемую сборку, указав путь к ней. Скомпилируем приложение main.cs:

csc.exe /r:UL.dll main.cs

Платформа .NET разделяет сборки на локальные (или сборки со слабыми именами) и глобальные (или сборки с сильными именами). Если UL.dll рассматривается как локальная сборка, то при выполнении приложения она должна находиться в том же каталоге, что и main.exe1. Локальные сборки обеспечивают простоту развёртывания приложения (все его компоненты сосредоточены в одном месте) и изолированность компонентов. Имя локальной сборки – слабое имя – это имя файла сборки без расширения.

Хотя использование локальных сборок имеет свои преимущества, иногда необходимо сделать сборку общедоступной. До появления платформы .NET доминировал подход, при котором код общих библиотек помещался в системный каталог простым копированием фалов при установке. Такой подход привёл к проблеме, известной как «ад DLL» (DLL Hell). Инсталлируемое приложение могло заменить общую библиотеку новой версией, при этом другие приложения, ориентированные на старую версию библиотеки, переставали работать. Для устранения «ада DLL» в платформе .NET используется специальное защищённое хранилище сборок (Global Assembly Cache, GAC).

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

Сборка, помещённая в GAC, получает сильное имя. Как раз использование сильного имени является тем признаком, по которому среда исполнения понимает, что речь идёт не о локальной сборке, а о сборке из GAC. Сильное имя включает: имя главного файла сборки (без расширения), версию сборки, указание о региональной принадлежности сборки и маркер открытого ключа сборки:

Name, Version=1.2.0.0, Culture=neutral, PublicKeyToken=1234567812345678

Рассмотрим процесс создания сборки с сильным именем на примере сборки UL.dll. Первое: необходимо создать пару криптографических ключей для цифровой подписи сборки. Для этих целей служит утилита sn.exe, входящая в состав Microsoft .NET Framework SDK.

sn.exe -k keys.snk

Параметр -k указывает на создание ключей, keys.snk – это файл с ключами. Просмотреть полученные ключи можно, используя команду sn.exe -tp.

Далее необходимо подписать сборку полученными ключами. Для этого используется специальный атрибут1 уровня сборки [AssemblyKeyFile] или ключ компилятора командной строки /keyfile:

using System;

using System.Reflection;

[assembly: AssemblyKeyFile("keys.snk")]

namespace UsefulLibrary { . . . }

Обратите внимание: для использования атрибута необходимо подключить пространство имён System.Reflection, а в качестве параметра атрибута указывается полное имя файла с ключами.

После подписания сборку можно поместить в GAC. Простейший вариант сделать это – использовать утилиту gacutil.exe, входящую в состав .NET Framework SDK. При использовании ключа /i сборка помещается в GAC, а ключ /u удаляет сборку из GAC:

gacutil.exe /i ul.dll

Теперь сборка UL.dll помещена в GAC. Её сильное имя (для ссылки в программах) имеет вид:

UL, Version=0.0.0.0, Culture=neutral, PublicKeyToken= ff824814c57facfe

Компонентом сильного имени является версия сборки. Если программист желает указать версию, то для этого используется атрибут [AssemblyVersion]. Номер версии имеет формат Major.Minor.Build.Revision. Часть Major является обязательной. Любая другая часть может быть опущена (в этом случае она полагается равной нулю). Часть Revision можно задать как *, тогда компилятор генерирует её как количество секунд, прошедших с полуночи, делённое на два. Часть Build также можно задать как *. Тогда для неё будет использовано количество дней, прошедших с 1 февраля 2000 года.