Динамически загружаемые сборки
85C# --- Сборки .NET --- Динамически загружаемые сборки
При поиске внешних сборок, на которые ссылается текущая сборка, CLR-среда заглядывает в манифест сборки. Во многих случаях необходимо, чтобы сборки могли загружаться на лету программно, даже если в манифесте о них не упоминается. Формально процесс загрузки внешних сборок по требованию называется динамической загрузкой.
В пространстве имен System.Reflection поставляется класс по имени Assembly, с применением которого можно динамически загружать сборку, а также просматривать ее собственные свойства. Этот класс позволяет выполнять динамическую загрузку приватных и разделяемых сборок, причем находящихся в произвольных местах. По сути, класс Assembly предоставляет методы (в частности, Load() и LoadFrom(), которые позволяют программно поставлять ту же информацию, которая встречается в клиентских файлах *.config.
Чтобы посмотреть, как обеспечивать динамическую загрузку на практике, создадим новый проект типа ConsoleApplication. Определим в нем метод Main(), который будет запрашивать у пользователя дружественное имя сборки для динамической загрузки. Кроме того, обеспечим передачу ссылки на эту сборку вспомогательному методу, который будет выводить имена всех содержащихся в сборке классов, интерфейсов, структур, перечислений и делегатов. Необходимый код выглядит на удивление просто:
using System;
using System.Reflection;
using System.IO;
namespace ConsoleApplication1
{
class Program
{
static void Types(Assembly ass)
{
Console.WriteLine("Типы сборки {0}: \n",ass.FullName);
Type[] types = ass.GetTypes();
foreach (Type t in types)
Console.WriteLine("--> " + t);
Console.WriteLine();
}
static void Main()
{
Assembly ass = null;
do
{
Console.WriteLine("Введите имя или путь к интересующей сборке или нажмите 'E' для выхода");
string assemblyName = Console.ReadLine();
if (assemblyName.ToUpper() == "E")
{
break;
}
try
{
ass = Assembly.Load(assemblyName);
Types(ass);
}
catch
{
Console.WriteLine("Сборка не найдена");
}
}
while (true);
}
}
}
Обратите внимание, что статическому методу Assembly.Load() передается только дружественное имя сборки, которую требуется загрузить в память. Следовательно, чтобы подвергнуть рефлексии сборку fontinfo.dll с помощью этой программы, понадобится сначала скопировать двоичный файл fontinfo.dll в подкаталог bin\Debug внутри каталога приложения.
Чтобы сделать приложение более гибким, можно модифицировать код так, чтобы загрузка внешней сборки производилась с помощью метода Assembly.LoadFrom(), а не Assembly.Load():
try
{
ass = Assembly.LoadFrom(assemblyName);
Types(ass);
}
После этого пользователь сможет вводить абсолютный путь к интересующей сборке:
Метод Assembly.Load() имеет несколько перегруженных версий. Одна из них позволяет указать значение культуры (для локализуемых сборок), а также номер версии и значение маркера открытого ключа (для разделяемых сборок). Все вместе эти элементы, которые идентифицируют сборку, называются отображаемым именем (display name), формат которого выглядит так: сначала идет дружественное имя сборки, за ней строка разделенных запятыми пар "имя/значение", а потом необязательные спецификаторы (в любом порядке). Ниже приведен образец, которым следует пользоваться (необязательные элементы указаны в круглых скобках):
Имя (, Version = <старший номер>.<младший номер>.<номер сборки>.<номер редакции>) (, Culture = <маркер культуры>) (, PublicKeyToken = <маркер открытого ключа>)
При создании отображаемого имени использование PublicKeyToken=null означает, что связывание и сопоставление должно выполняться со сборкой, не имеющей строгого имени, a Culture="" — что сопоставление должно выполняться с использованием культуры, которая принята на целевой машине по умолчанию:
// Выполнение загрузки fontinfo версии 1.0.0.0
//с использованием принятой по умолчанию культуры
Assembly а =
Assembly.Load(@"CarLibrary, Version=l.0.0.0, PublicKeyToken=null, Culture=""");
Следует также иметь в виду, что в пространстве имен System.Reflection поставляется тип AssemblyName, который позволяет представлять строковую информацию наподобие той, что была показана выше, в виде удобной объектной переменной. Обычно этот класс применяется вместе с классом System.Version, который позволяет упаковывать в объектно-ориентированную оболочку номер версии сборки. После создания отображаемое имя может передаваться перегруженной версии метода Assembly.Load().