Динамически загружаемые сборки

85

При поиске внешних сборок, на которые ссылается текущая сборка, 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().

Пройди тесты
Лучший чат для C# программистов