Доступность метаданных во время исполнения. Из постулата единого формата метаданных вытекает доступность метаданных в runtime, даже если во время разработки метаданные доступны не были. Причем можно не только читать, но и писать метаданные (создавать новое описание). Это дает возможность создавать динамически расширяемые приложения, позволяющие использовать информацию о типах для подключения внешних модулей или динамического вызова методов и установки свойств. Одним словом предполагается, что такие сложные приложения, как контейнеры объектов (дизайнеры форм, менеджеры транзакций и т.п.) можно будет писать на любом языке программирования, даже на VB. Более того, описание, сделанное на одном языке, можно будет использовать в другом без каких либо дополнительных действий. Еще более того, можно будет наследовать классы одного языка от классов, описанных на другом языке.
Например, рассмотрим следующий COM IDL:
[ uuid(ABBAABBA-ABBA-ABBA-ABBA-ABBAABBAABBA) ]
library MyLib {
importlib("stdole32.tlb");
[ uuid(87653090-D0D0-D0D0-D0D0-18121962FADE) ]
interface ICalculator : IUnknown {
HRESULT Add([in] double n, [in, out] VARIANT_BOOL *round, [out] VARIANT_BOOL *overflow, [out, retval] double *sum);
}
}
Эквивалентный тип CLR на C# (это новый язык программирования, который претендует стать основным языком VS.Net, о нем мы еще подробно поговорим позже) будет выглядеть так:
namespace MyLib {
interface ICalculator {
double Add(double n, ref bool round, out bool overflow);
}
}
Если поместить это описание в файл, то его можно будет скомпилировать с помощью компилятора C#, следующей командной строкой:
csc.exe /t:library /out:mylib.dll mylib.cs
Полученное бинарное описание можно импортировать, например, в VB, используя ключ компилятора "/r":
vbc.exe /r:mylib.dll program.vb
CLR не снимает необходимости определения типов, он позволяет разработчику делать это на любом языке, совместимом с CLR.
CLR предоставляет библиотеку, позволяющую в runtime-е читать и/или создавать сборку, содержащую описание типов. Нижеприведенный листинг демонстрирует создание сборки, содержащей описание следующего интерфейса:
namespace MyLib {
public interface ICalculator {
double Add(double n, ref double round, out double overflow);
}
}
Код, создающий сборку, реализован на C#, языке, похожем на C++ или Java. Мы надеемся, что у вас не возникнет проблем с пониманием кода:
using System;
using System.Reflection;
using System.Reflection.Emit;
public class emititf {
// Точка входа программы (объектно-ориентированный аналог функции main в С/C++)
public static int Main(String[] argv) {
// Создаем новую сборку AssemblyBuilder ab = DefineNewAssembly();
// Создаем определение нового интерфейса ICalculator внутри новой сборки
TypeBuilder tb = DefineICalculator(ab);
// Добавляем описание метода "Add" к описанию интерфейса
ICalculator MethodBuilder method = DefineAddMethod(tb);
// Добавляем описание параметров
DefineAddParameters(method);
// Создаем тип
Type t = tb.CreateType();
// Записываем сборку в файл "mylib.dll"
ab.Save("mylib.dll");
return 0;
}
// Создает сборку с именем "mylib"
static AssemblyBuilder DefineNewAssembly() {
// Новая сборка создается в рамках текущего AppDomain-а
AppDomain current = AppDomain.CurrentDomain;
// Новая сборка нуждается в имени. Назначаем ей не строгое имя!
AssemblyName an = new AssemblyName();
an.Name = "mylib";
// DefineDynamicAssembly завершает работу по созданию сборки
return current.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
}
// Создает новое описание интерфейса с именем "MyLib.ICalculator"
static TypeBuilder DefineICalculator(AssemblyBuilder ab) {
// Все описания типов находятся в модуле, определенном для нашей сборки
ModuleBuilder mb = ab.DefineDynamicModule("mylib.dll", "mylib.dll");
// Все описания интерфейсов должны быть помечены как Interface и Abstract
TypeAttributes attrs = TypeAttributes.Interface | TypeAttributes.Abstract;
// public-интерфейсы должны быть также помечены как
Public attrs |= TypeAttributes.Public;
// DefineType завершает работу по созданию описания для интерфейса
return mb.DefineType("MyLib.ICalculator", attrs);
}
// Создает новое описание методов "double Add(double, ref double, out double)"
static MethodBuilder DefineAddMethod(TypeBuilder itf) {
// Методы интерфейса должны быть помечены как abstract, virtual и public
MethodAttributes attrs = MethodAttributes.Public | MethodAttributes.Abstract | MethodAttributes.Virtual;
// Метод определяется по имени и описанию (его параметрам)
// Создаем описание возвращаемого значения
Type resultType = typeof(double);
Читать дальше