Атрибуты, сборки, рефлексия
Класс сборки в действии
Исследование свойств и областей применения класса Assembly начинаем с создания тестовой однофайловой сборки AssemblyForStart в рамках проекта Class Library.
Первая сборка Operators00.exe:
using System;
namespace Operators00
{
public class xPoint
{
float x, y;
xPoint()
{
x = 0.0F;
y = 0.0F;
}
public xPoint(float xKey, float yKey):this()
{
x = xKey;
y = yKey;
}
public static bool operator true(xPoint xp)
{
if (xp.x != 0.0F && xp.y != 0.0F) return true;
else return false;
}
public static bool operator false(xPoint xp)
{
if (xp.x == 0.0F || xp.y == 0.0F) return false;
else return true;
}
public static xPoint operator | (xPoint key1, xPoint key2)
{
if (key1) return key1;
if (key2) return key2;
return new xPoint();
}
public static xPoint operator & (xPoint key1, xPoint key2)
{
if (key1 && key2) return new xPoint(1.0F, 1.0F);
return new xPoint();
}
public void Hello()
{
Console.WriteLine("Hello! Point {0},{1} is here!",this.x,this.y);
}
}
class Class1
{
// The main entry Point for the application.
static void Main() // У точки входа пустой список параметров.
// Я пока не сумел ей передать через метод Invoke массива строк.
{
xPoint xp0 = new xPoint(1.0F, 1.0F);
xPoint xp1 = new xPoint(1.0F, 1.0F);
if (xp0 || xp1) Console.WriteLine("xp0 || xp1 is true!");
else Console.WriteLine("xp0 || xp1 is false!");
}
}
}
Листинг
10.3.
Вторая сборка AssemblyForStart.dll. В примере она так и не запускалась. Используется только для тестирования стандартных средств загрузки сборок – библиотек классов:
using System;
namespace AssemblyForStart
{
// Class1 : первая компонента сборки AssemblyForStart.
public class Class1
{
public Class1()
{
Console.WriteLine("This is constructor Class1()");
}
public void fC1()
{
Console.WriteLine("This is fC1()");
}
}
}А вот полигон AssemblyStarter.exe. В примере демонстрируется техника ПОЗДНЕГО СВЯЗЫВАНИЯ. Именно поэтому код, который выполняется после загрузки сборки, не содержит в явном виде информации об используемых в приложении типах:
using System;
using System.Reflection;
using System.IO;
namespace AssemblyStarter
{
// Приложение обеспечивает запуск сборки.
class Class1
{
static void Main(string[] args)
{
// Сборка может быть вызвана непосредственно по имени
// (строковый литерал с дружественным именем сборки).
// Ничего особенного. Просто имя без всяких там расширений.
// Главная проблема заключается в том, что сборки должны
// предварительно включаться в раздел References (Ссылки).
// Кроме того, информация о загружаемой сборке может быть представлена
// в виде объекта - представителя класса AssemblyName, ссылка
// на который также может быть передана в качестве аргумента методу
// Assembly.Load().
// Вот здесь как раз и происходит формирование этого самого объекта.
AssemblyName asmName = new AssemblyName();
asmName.Name = "AssemblyForStart";
// Версию подсмотрели в манифесте сборки с помощью IlDasm.exe.
Version v = new Version("1.0.1790.25124");
// Можно было бы для пущей крутизны кода поле Version
// проинициализировать непосредственно (дело хозяйское):
// asmName.Version = new Version("1:0:1790:25124");
asmName.Version = v;
// Ссылка на объект - представитель класса Assembly.
//
Assembly asm = null;
try
{
// Загрузка сборки по "дружественному имени".
//asm = Assembly.Load("AssemblyForStart");
// Путь и полное имя при загрузке частной сборки не имеют значения.
// Соответствующие файлы должны располагаться непосредственно
// в каталоге приложения.
//asm = Assembly.Load
//(@"D:\Users\WORK\Cs\AssemblyTest\AssemblyForStart\bin\Debug\ AssemblyForStart.dll");
//asm = Assembly.Load(asmName);
// Если сборку организовать в виде исполняемого модуля и
// "запихнуть" в каталог вручную – загрузится и такая сборка.
asm = Assembly.Load("Operators00");
}
catch(FileNotFoundException e)
{
Console.WriteLine("We have a problem:" + e.Message);
}
// Итак, решено. Загрузили сборку, содержащую объявление класса xPoint.
// Если сборка загрузилась – с ней надо что-то делать. Ясное дело,
// надо выполнять программный код сборки.
// Первый вариант выполнения. В сборке содержатся объявления двух классов:
// класса xPoint и класса Class1. Мы воспользуемся объявлением класса
// xPoint, построим соответствующий объект - представитель этого класса,
// после чего будем вызывать нестатические методы - члены этого класса.
// Все происходит в режиме ПОЗДНЕГО связывания.
// Поэтому ни о каких ЯВНЫХ упоминаниях
// имен типов не может быть речи.
// Единственное явное упоминание – это упоминание
// имени метода.
object[] ps = {25,25};
Type[] types = asm.GetTypes();
// Здесь используется класс Activator!
object obj = Activator.CreateInstance(types[0],ps);
MethodInfo mi = types[0].GetMethod("Hello");
mi.Invoke(obj,null);
// Второй вариант выполнения. Воспользуемся тем обстоятельством,
// что загружаемая сборка не является библиотекой классов, а является
// обычной выполнимой сборкой с явным образом обозначенной точкой
// входа – СТАТИЧЕСКОЙ функцией Main(), которая является членом
// класса Class1.
mi = asm.EntryPoint;
// Вот, все получилось! Единственное, что я не сделал,
// так это не смог передать в точку входа загруженной
// сборки массива строк-параметров (смотреть на точку входа данной
// сборки). Ну не удалось. Потому и в сборке
// Operators точку входа сборки объявил без параметров.
mi.Invoke(null,null);
}
}
}
Листинг
10.4.
Собрали, запустили. Получилось. Стало быть, ВСЕ ХОРОШО.
В ходе выполнения приложения класс Assembly позволяет:
- получать информацию о самой сборке;
- обращаться к членам класса, входящим в сборку;
- загружать другие сборки.
Разбор полетов
В приведенном выше примере демонстрируется техника ПОЗДНЕГО СВЯЗЫВАНИЯ. Именно поэтому код, который выполняется после загрузки сборки, не содержит в явном виде информации об используемых в приложении типах. Транслятор действует в строгом соответствии с синтаксисом языка C# и просто не поймет пожелания "создать объект – представитель класса ..., который будет объявлен в сборке, которую предполагается загрузить в ходе выполнения приложения".
Класс System.Activator
Класс Activator – главное средство, обеспечивающее позднее связывание.
Содержит методы, позволяющие создавать объекты на основе информации о типах, получаемой непосредственно в ходе выполнения приложения, а также получать ссылки на существующие объекты. Далее приводится список перегруженных статических методов класса:
- CreateComInstanceFrom. Создает экземпляр COM-объекта;
-
CreateInstance. Создает объект – представитель specified-типа, используя при этом наиболее подходящий по списку параметров конструктор (best matches the specified parameters).
Пример:
ObjectHandle hdlSample; IMyExtenderInterface myExtenderInterface; string argOne = "Value of argOne"; int argTwo = 7; object[] args = {argOne, argTwo}; // Uses the UrlAttribute to create a remote object. object[] activationAttributes = {new UrlAttribute("http://localhost:9000/MySampleService")}; // Activates an object for this client. // You must supply a valid fully qualified assembly name here. hdlSample = Activator.CreateInstance( "Assembly text name, Version, Culture, PublicKeyToken", "samplenamespace.sampleclass", true, BindingFlags.Instance|BindingFlags.Public, null, args, null, activationAttributes, null); myExtenderInterface = (IMyExtenderInterface)hdlSample.Unwrap(); Console.WriteLine(myExtenderInterface.SampleMethod("Bill")); -
CreateInstanceFrom. Создает объект – представитель типа, специфицированного по имени. Имя специфицируется на основе имени сборки. При этом используется подходящий по списку параметров конструктор (the constructor that best matches the specified parameters).
Пример:
ObjectHandle hdlSample; IMyExtenderInterface myExtenderInterface; object[] activationAttributes = {new SynchronizationAttribute()}; // Assumes that SampleAssembly.dll exists in the same directory as this assembly. hdlSample = Activator.CreateInstanceFrom( "SampleAssembly.dll", "SampleNamespace.SampleClass", activationAttributes); // Assumes that the SampleClass implements an interface provided by // this application. myExtenderInterface = (IMyExtenderInterface)hdlSample.Unwrap(); Console.WriteLine(myExtenderInterface.SampleMethod("Bill")); - GetObject – Overloaded. Создает прокси (заместителя, представителя) для непосредственного запуска активизированного сервером объекта или XML-сервиса.
Сборка может быть вызвана непосредственно по имени (строковый литерал с дружественным именем сборки).
Ничего особенного. Просто имя без всяких там расширений. Главная проблема заключается в том, что частные сборки должны предварительно включаться в раздел References (Ссылки).
Кроме того, информация о загружаемой сборке может быть представлена в виде объекта – представителя класса AssemblyName, ссылка на который также может быть передана в качестве аргумента методу Assembly.Load().