Главная » C# » Динамическая загрузка сборки в Visual C# (Sharp)

0

В терминах .NET, чтобы использовать конфигурационный файл с динамически зружаемым приложением, необходимо загрузить сборку и из сборки создать эемпляр типа. К ссылкам можно обращаться локально или с кэша GAC.

Динамическое создание экземпляра типа

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

Класс данных является заполнителем, который требуется только для кода, примяемого для динамической загрузки типа. Это позволяет определить класс данных частным, т. к. частный класс подразумевает, что только родительский класс может создавать его экземпляры.

Определив класс configurationLoader как класс для динамического создания эемпляров других типов, объявляем частный класс следующим образом (добавляем в сборку Definitions):

namespace Definitions

{

public class ConfigurationLoader { private class Configyrationlnfo {

public string AssemblyName; public string TypeName,- public string EasyName;

}

Dictionary<string, ConfigurationInfo> _availableTypes;

}

}

Класс  Configurationlnfo содержит  три  члена  данных:  AssemblyName, TypeName и EasyName. С классом применяется модификатор private, который указывает, что ничто вне класса conf igurationLoader не может создать экземпляр конфигурации. Если бы  класс Configurationinfo был объявлен с модификатором public, то слующий код был бы легитимным:

ConfigurationLoader.Configurationinfo els = new ConfigurationLoader.Configurationinfo!);

Класс Configurationinfo применяется для хранения информации, полученной из конфигурационного файла. Элементы конфигурационной информации  связаны между собой перекрестными ссылками с помощью типа Dictionary, ключом в кором служит член данных EasyName.

Для анализа конфигурационной информации и создания отдельных экземпляров Configurationinfo применяется следующий код (являющийся частью Configur- tionLoader):

public void Load() {

string value = ConfigurationManager.AppSettings["assemblies"]; string[] values = value.Split(‘/);

for (int cl =0; cl < values.Length; cl += 3) {

_availableTypes.Add( values! cl],

new Configurationinfo { EasyName = values[cl],

TypeName = values[cl + 1], AssemblyName = values[cl + 2] });

}

}

Конфигурация  считывается  с. помощью  AppSettings, после  чего  преобразуется в  массив  строковых  элементов.   Элементы   массива  обрабатываются   в   цикле по три элемента за раз. С каждой итерацией цикла создается экземпляр configurationinfo. После обработки конфигурационной строки, можно динически создавать экземпляр типа с помощью метода instantiate класса ConfigurationLoader:

using System.Reflection;

public RequestedType Instantiate<RequestedType>(string identifier) {

if (_availableTypes.ContainsKey(identifier)) { Configurationlnfo info = _availableTypes[identifier]; AssemblyName assemblyName =

AssemblyName.GetAssemblyName(info.AssemblyName);

Assembly assembly = Assembly.Load(assemblyName);

object obj = assembly.Createlnstance(info.TypeName);

return (RequestedType)obj;

}

else {

throw new ArgumentException("identifier (" +

identifier + ") is not a listed type");

}

}

Посмотрев на объявление метода instantiate о, можно увидеть, что он является методом обобщения .NET, которые рассматриваются в главе 11. Целью является создать экземпляр типа и выполнить автоматическое преобразование в запрошеый тип. Таким образом, нет надобности определять метод instantiate () с помью типа object. Параметр identifier является строкой, которая используется для поиска идентификатора в члене данных _availabieTypes коллекции Dictionary. Если _avaiiabieTypes содержит идентификатор, то извлекается эемпляр configurationlnfo, с помощью которого создается экземпляр данного та. При отсутствии экземпляра выдается исключение.

Фрагмент кода был выделен жирным шрифтом, потому что подобного кода мы еще не рассматривали. В этих трех строчках кода демонстрируется встроенная в .NET возможность динамического исполнения кода. В первой выделенной жирным шрифтом строке кода разрешается возможность отражения, а во второй загружаея сборка. Так, например, если бы параметр identifier был impii, то первая жиая строчка кода ссылалась бы на сборку Implementationsl.dll и динамически загрила бы эту сборку. Но здесь имеется загвоздка, заключающаяся в том, что сборку Implementationsl.dll можно загрузить динамически, только если она имеется в лальной папке или в кэше GAC. В качестве альтернативного решения можно было бы указать полный путь в определении сборки. Но данная стратегия имеет недосток, заключающийся в том, что сборка всегда должна находиться в одном и том же месте на разных компьютерах.

Когда вторая жирная строка кода присваивает значение переменной сборки assembly, то этой сборкой является загруженная сборка .NET. Сборка будет проанизирована, после чего можно будет создать экземпляр типа. В третьей выделенной жирным строчке кода вызывается метод Createisntanceo, которому в параметрах передается имя типа, экземпляр которого нужно  создать,  включающее  простратво имен.  Таким  образом,  например, если загружена сборка Implementationsl.dll,

то можно будет создать экземпляр типа implementation!.implementation. Создание экземпляра будет успешным, несмотря на ТО, что Implementationsl. Implementation является частным классом, т. к. мы применяем принципы динамического програирования.

Но создание экземпляра типа не означает, что этот экземпляр можно использовать. Для этого необходимо выполнить приведение типа к типу, который был объявлен общим (public).

Мы будем продолжать рассматривать пример Implementations! . Implementation, пытаясь вычислить, как создать его экземпляр и манипулировать этим экземплом. Но прежде чем заниматься этим кодом, нам необходимо рассмотреть еще одну возможность программирования: синглетоны (singletons).

Использование синглетонов

Так как класс ConfigurationManager является статическим, то с него нельзя создать экземпляры. Статический класс со своими статическими методами и стическими членами данных является классом одноцелевого назначения. Такой же эффект можно получить, создав синглетон (singleton). Синглетон подобен статичкому классу, только с него можно создавать экземпляры. Преимущество синглонов в том, что можно создавать множественные синглетоны одного типа при ничии одного статического класса.

Статический класс можно сравнить с единственным ключом к замку. Если это зок на личном сейфе, то, скорее всего, вы не захотите, чтобы к нему было несколо ключей у разных людей. Но что если это замок от входной двери вашего дома или квартиры? Если только вы не холостяк, то тогда к замку будет несколько клей, но не у всякого, а только у членов вашей семьи. Ситуацию с ключом к сейфу можно сравнить со статическим классом, а с ключами к семейному дому — с синетоном. Можно спорить, что для сейфа можно также применить синглетон, но только одну его копию.  Но такой аргумент не принимает во внимание то обстоельство., что с помощью должным образом написанного исходного кода можно создать и второй, и третий и т. д. экземпляр синглетона. А создать другой экземяр статического класса невозможно, что обеспечивает определенный стиль прраммирования.

Определим класс ConfigurationLoader как синглетон. Это подразумевает следуие два обстоятельства:

•    создание  свойства  instance, ссылающегося  на  единичный  экземпляр  класса

ConfigurationLoader;

•    определение класса ConfigurationLoader как private, что подразумевает, что только он сам может создавать свои экземпляры. Таким образом, обеспечиваея поведение класса ConfigurationLoader подобное поведению статического класса в том смысле, что его клиент не сможет создавать экземпляры данного класса.

Далее   приводится   КОД синглетона   ConfigurationLoader (находится   в   сборке

Definitions):

static ConfigurationLoader „instance; static ConfigurationLoader() {

„instance = new ConfigurationLoader();

ConfigurationLoader!) {

„availableTypes = new Dictionary<string, ConfigurationInfo>();

}

public static ConfigurationLoader Instance { get {

return „instance;

}

}

Свойство синглетона instance объявлено static, что позволяет обращение к ConfigurationLoader. instance. Реализация статического свойства должна ссаться на статический член данных, которым в данном примере является член даых „instance. Экземпляр члена данных „instance создается статическим консуктором static ConfigurationLoaderо. Конструктор экземпляра создаст экземпляр „availableTypes типа Dictionary. Обратите внимание на отсутствие модификатора public в объявлении, что подразумевает частную область видимости.

Использование созданного экземпляра

Теперь,   когда  мы  познакомились  с  понятием  сингелтона,  можем  возвратиться к первоначальной проблеме создания кода для динамического создания экземпляра типа. Экземпляр impii создается с помощью следующего кода:

ConfigurationLoader.Instance.Load(); IDefinition definition =

ConfigurationLoader.Instance.Instantiate<IDefinition>("Impll");

Console.WriteLine(definition.TranslateiWord("hello"));

ПРИМЕЧАНИЕ

Может случиться, что при  исполнении  этого  кода  появятся  ошибки.  Если  у  вас возниут такие затруднения, то это потому, что  сборки  не  находятся  в  одном  каталоге  и  моды динамическо й загрузки не могут найти их. Проблема решается  помещение м  всех сборок  в  один  общи й  каталог.

В   первой  строчке   кода  из  конфигурационного  файла  извлекается  информация о сборке и типе. Во второй строчке создается экземпляр impii, после чего выпояется  приведение  Implementationsi.Implementation К типу  IDefinition. Предение  будет  успешным,  потому  что  интерфейс   IDefinition был  реализован

С ПОМОЩЬЮ Implementationsi.Implementation. Переменная definition будет ссылаться на действительный экземпляр  объекта, реализующий iDefinition и  мод  TranslateWord().

Здесь  нам  нужно остановиться  и  подумать  над тем, что  произошло.  Мы  определили в конфигурационном файле местонахождение типа. Исходный код в приложении прочитал конфигурационный файл во время  исполнения  и  сохранил  имеющиеся типы. Вызывающий код знал только об абстрактном типе impii и то, что он  реалует интерфейс iDefinition. Потом с помощью динамических методов .NET была загружена сборка типа и создан его экземпляр.  Данный  процесс уникальный  в том, что   предоставляет   администратору   возможность   модифицирования   информации о типе, чей экземпляр необходимо создать, и о его местонахождении, не требуя при этом рекомпиляции или обновления инфраструктуры. Такая гибкость значительно упрощает обновление функциональности, не требуя при этом обновления клиента функциональности.

ПРИМЕЧАНИЕ

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

Источник: Гросс  К. С# 2008:  Пер. с англ. — СПб.:  БХВ-Петербург, 2009. — 576 е.:  ил. — (Самоучитель)

По теме:

  • Комментарии