Шрифт:
Интервал:
Закладка:
Таким образом, механизм рефлексии предоставляет ту информацию, которая в рамках СОМ хранилась в IDL-файлах, в заголовочных файлах, в библиотеке типов.
Теперь рассмотрим вопросы компиляции и развертывания построенного приложения.
Важнейшее новшество в .NET — понятие сборки, которую можно рассматривать как логическую DLL. Сборки позволяют различным клиентам работать с различными версиями одного и того же компонента. Сборка содержит метаданные (манифест), используя которые система может еще до запуска клиента узнать — имеются ли все нужные клиенту компоненты в доступных сборках.
Предположим, что код клиента содержится в файле MуАрр. cs рабочего каталога, а код сервера в подкаталоге MyServer в файле MyServer.cs. Следующие команды обеспечивают компиляцию и сборку клиентского и серверного приложений:
>csc /t: library MyServer.cs
>csc /r: MyServerMyServer.dll MyApp.cs
В первой строке вызывается компилятор с C# и код сервера MyServer.cs компилируется в сборку MyServer.dll. Ключ /t: library говорит именно о том, что нужна сборка типа DLL, т. е. ее можно загружать в домен клиентского приложения.
Во второй строке компилируется код клиента MуАрр. cs. По умолчанию получаем сборку МуАрр. ехе. Ключ /r: MyServer MyServer.dll дает статическую ссылку на сборку, содержащую компонент Account.
Отметим, что в данном случае сборка с сервером находится в одном каталоге с клиентом (точнее, в его подкаталоге, имя которого совпадает с именем сборки). Такой способ развертывания является самым простым. Он допускает перенос приложения в другой каталог, на другую машину простым копированием. Нет никакой регистрации.
Однако данной сборкой может пользоваться только данный клиент. Другим клиентам, находящимся вне рабочего каталога, данная библиотечная сборка недоступна.
Теперь запустим клиентское приложение
>csc МуАрр. ехе
Server AppDomain = МуАрр. ехе
Total = 8
Client AppDomain = МуАрр. ехе
Int32 Total()
Void Add(Int32)
Int32 GetHashCode()
Boolean Equals(System.Object)
System.String ToString()
System.Type GetType()
Мы видим, что и сервер и клиент выполняются в одном домене приложения. Его имя совпадает с именем клиентского приложения. Это связано с тем, что клиентская сборка загружается при запуске в домен приложения по умолчанию, который потом и получает свое имя, совпадающее с именем загруженной сборки. Серверная сборка загружается туда же.
Ниже выводятся сигнатуры всех методов, доступных через ссылку на любой интерфейс класса Account. Это и оба метода, реализованные в классе Account, и методы, унаследованные от System.Object.
Регистрация сборки в Global Assembly Cache
GAC — Global Assembly Cache, это то место, где можно развертывать сборки, которые должны быть доступны многим клиентам. Для развертывания сборки в GAC ее нужно подписать. Точнее, сборке присваивается версия, ее содержимое хешируется и хешированное значение шифруется личным ключом разработчика. В сборку (в ее манифест), включаются версия сборки, шифрованный хеш — электронная подпись, публичный ключ. В результате, благодаря наличию публичного ключа, можно проверить сохранность сборки, т. е. перед запуском клиентского приложения удостовериться, что именно с этой сборкой данное клиентское приложение было откомпилировано.
Ниже приведены все необходимые команды, обеспечивающие подписание сборки и ее регистрацию в GAC:
>csc /t: module MyServer.cs
>sn — k my.snk
>al /out: MyServer.dll MyServer.netmodule /keyfile: my.snk
>gacutil — i MyServer.dll
>sn — Tp MyServer.dll
Public key is
002400000480….
Public key token is 047772996d01a6d4
Для получения подписанной сборки (сборки со строгим именем) нужно откомпилировать код серверас параметром /t: module. В результате получим модуль MyServer.netmodule, содержащий (как и сборка) код на MSIL, но не содержащий (в отличие от сборки) манифеста.
Далее генерируется (если не была сгенерирована ранее) пара ключей — личный и публичный ключи, с помощью утилиты sn (strong name). Эта пара записывается в файл my. snk.
На следующем этапе компоновщик сборки al (assembly linker) формирует подписанную сборку MyServer. dll, используя модуль MyServer.netmodule и файл с ключами my.snk.
Для регистрации сборки в GAC можно использовать утилиту gacutil.
Для работы с подписанной сборкой клиент должен знать ее версию и публичный ключ (или его хешированное значение — токен публичного ключа). Последнее можно получить с помощью утилиты sn с параметром —Tр, указав подписанную сборку.
В следующем примере демонстрируется клиент, который может воспользоваться зарегистрированной в GAC сборкой. Кроме этого, в этом примере демонстрируется так называемая динамическая ссылка на сборку.
В случае продемонстрированной в предыдущем примере статической ссылки, которую можно было бы применить и в этом случае (в случае сборки зарегистрированной в GAC), клиент связывается с нужной сборкой на этапе компиляции. В этом случае в манифест сборки клиента записывается вся нужная информация об используемых им сборках. Это позволяет еще до запуска клиента узнать — имеются ли в наличии нужные клиенту компоненты. Благодаря использованию подписанной сборки при ее наличии можно гарантировать ее совместимость с клиентом.
Но иногда нужно связаться со сборкой динамически, в процессе исполнения клиента. В этом случае на этапе компиляции наличие нужной сборки (и нужного типа в данной сборке) не проверяется.
using System;
using System.Reflection;
public class MyApp {
public static void Main() {
Assembly assem = Assembly.Load("MyServer, " +
"Version=0.0.0.0, " +
"Culture=neutral, " +
"PublicKeyToken=047772996d01a6d4");
Type accountType = assem.GetType("MyServer.Account");
if (accountType!= null) {
MethodInfо addMethod = accountType.GetMethod("Add");
MethodInfo totalMethod =
accountType.GetMethod("Total");
Object obj = Activator.CreateInstance(accountType);
Object [] args = new Object[1];
args [0] = 3;
if (addMethod!= null){
addMethod.Invoke(obj, args);
args [0] = 5;
addMethod.Invoke(obj, args);
}
if (totalMethod!= null)
Console.WriteLine("Total = {0}",
totalMethod.Invoke(obj, null));
}
}
}
В данном примере статический метод Load класса Assembly загружает из GAC сборку с заданным именем, версией (по умолчанию 0.0.0.0), культурой (по умолчанию neutral) и токеном публичного ключа. Далее, используя механизм рефлексии, получаем ссылку на объект типа Tуре, содержащий всю информацию о классе Account из данной сборки. Если информация об этом типе имеется в загруженной сборке, получаем информацию о методах Add и Total. Потом создаем экземпляр класса Account и формируем массив аргументов. В данном случае