Шрифт:
Интервал:
Закладка:
Чтобы определить новые пользовательские атрибуты, следует создать новый класс, унаследовав его от System.Attribute, и реализовать в этом классе public-конструктор:
using System;
[ AttributeUsage(AttributeTargets.All) ]
public class ColorAttribute : Attribute {
public String color;
public ColorAttribute(string c) { color = c;}
}
Атрибут AttributeUsage говорит, к чему будет применим новый атрибут – к классу, методу, свойству и т.п. Если класс атрибута заканчивается на Attribute, то атрибут можно будет использовать с или без этого суффикса. Например, следующие два примера идентичны, хотя последний и реже используется:
[ Color("Red") ]
class MyClass {}
[ ColorAttribute("Red") ]
class MyClass {}
Пользовательские атрибуты доступны в runtime через reflection-механизм. Код, приведенный выше, определяет, какой цвет присвоен данному классу и присвоен ли он вообще:
using System;
String GetColor(Object o) {
Type t = o.GetType();
// Получаем тип объекта
// Получаем тип необходимого атрибута
Type at = typeof(ColorAttribute);
// Получаем все атрибуты этого типа
Attribute[] rga = t.GetCustomAttributes(at);
// Выходим, если атрибуты не заданы
if (rga.Length == 0) return null;
// Иначе извлекаем первый атрибут
ColorAttribute color = (ColorAttribute)rga[0];
return color.color;
}
Атрибуты – это мощный и универсальный механизм расширения метаинформации.
Динамический вызов на халяву. Поскольку информация о типах стандартна и доступна всегда для любого элемента сборки, runtime-сервисы могут динамически вызывать любой метод любого объекта. Это значит, что все объекты CLR могут быть использованы в скриптовых языках программирования. Также можно создавать сервисные визуальные компоненты, непосредственно взаимодействующие с любыми компонентами, и при этом совершенно не нужно иметь их исходных текстов.
Физическое размещение непрозрачно. clr основывается на совершенной информации о типах. Это значит, что ни один тип не остается неописанным, вплоть до типов и имен членов данных типа. Как ни странно, физическая раскладка памяти для данного типа и его экземпляров полностью скрыты. Смещения, размеры и выравнивание членов данных неизвестны. Если нужно экспортировать CLR-объект за пределы среды исполнения, CLR создает COM-Callable Wrapper (CCW), который работает переходником между классической конвенцией вызовов COM, основанной на IUnknown и stdcall во внутреннем формате вызовов CLR-среды.
Очень жалко, что у господ новаторов из Microsoft не хватило смелости попросту расширить стандарт COM и обеспечить поддержку этих улучшений в VS.Net. Так что всем, кто использует COM в своей работе и хочет шагать в ногу со временем, придется изучить еще и CLR. Хотя для разработчиков, чей разум не замутнен тонкостями COM, интеграция CLR и COM будет выглядеть довольно прозрачно. Чтобы использовать COM-объект в своих .Net-приложениях, необходимо будет только зарегистрировать этот объект, примерно так, как это делалось в VB 6. Для низкоуровневых программистов это значит, что для динамической генерации новых типов и перехвата доступа к уже существующим экземплярам нужна новая техника. System.Reflection.Emit дает возможность генерации новых типов; System.Runtime.Remoting дает возможность перехвата вызовов к существующим типам.
Иерархия типов с одним корнем! Почти во всех традиционных языках программирования есть и иерархия , таких, например как int, char и т.п. В COM имеется корневой тип для объектных ссылок – IUnknown. Все остальные типы отражаются в универсальном типе данных – VARIANT. И хотя в принципе VARIANT и может хранить объектные ссылки, но назвать его корнем для иерархии типов язык не поворачивается. Это подтверждается тем, что в VB 6 универсальный параметр можно было описать или как Object (то есть IDispatch), или как Variant. Да и у варианта были некоторые проблемы с информацией о типах при хранении некоторых типов данных. Например, при помещении в вариант значения Enum значение превращалось в целое число, и не было никаких возможностей узнать, к какому типу данных принадлежит значение.
В системе типов CLR нет ни IUnknown, ни VARIANT, ни (по сути) простых типов данных. Вместо этого все типы происходят от System.Object. Да, это значит, что простые типы, такие, как int или double, происходят от System.Object и являются настоящими объектами. Причем это справедливо для всех без исключения языков, поддерживающих .Net. В VB.Net и C# это становится частью языка, а в C++ осуществляется через специальное расширение – (C++ – единственный язык, который не был полностью уничтожен перед реинкарнацией. С его помощью можно будет создавать приложения сразу в бинарном виде и без CLR. По сути, родился новый язык. В новостных группах его уже окрестили MC++.). Функциональность поля vt структуры VARTYPE поглощена методом System.Object.GetType. И теперь она доступна из любого языка. Так, код на C++:
void Process(VARIANT value) {
switch (value.vt) {
case VT_I2:
ProcessAsShort(value.iVal);
break;
case VT_R8:
ProcessAsDouble(value.dblVal);
break;
case VT_BSTR:
ProcessAsString(value.bstrVal);
break;
case VT_UNKNOWN:
case VT_DISPATCH:
{
CComQIPtr<IFoo> spFoo(value.punkVal);
if (spFoo) ProcessAsFoo(spFoo);
else {
CComQIPtr<IBar> spBar(value.punkVal);
if (spBar) ProcessAsBar(spBar);
}
break;
}
}
}
можно заменить на следующий код на C#:
void Process(System.Object value) {
if (value is short) ProcessAsShort((short)value);
else if (value is double) ProcessAsDouble((double)value);
else if(value is System.String) ProcessAsString((System.String)value);
else if(value is IFoo) ProcessAsFoo((IFoo)value);
else if(value is IBar) ProcessAsBar((IBar)value);
}
Заметьте, что C#-код использует специальные операторы для проверки соответствия типов, и что оператор приведения типов выполняет приведение как для объектных ссылок, так и для простых типов.
Еще раз повторюсь, что код, подобный приведенному во втором листинге, можно написать на любом CLR-совместимом языке.
Классы с публичными членами. В com классы были именованными реализациями одного или более интерфейсов. И точка. Классам не позволялось иметь методы или свойства, не принадлежащие какому– либо интерфейсу. В CLR у классов могут быть public-члены. Это позволяет использовать методы как для реализации интерфейсов компонентов, так и в качестве одиноких методов в классах конечных приложений.
Множественное наследование. В clr есть две новации. Одна – реализация множественного наследования для интерфейсов. По всей видимости, это расширение сделано с целью избавиться от QueryInterface. QueryInterface должен быть заменен на операцию приведения типов. С точки зрения идеологии красивое решение, но не приведет ли оно к проблемам интеграции с COM? Вторая новация связана с запретом множественного наследования. Множественное наследование наряду с шаблонами и переопределением операторов были C++. Похоже, времена меняются. Множественное наследование не запрещено в чистом C++. Но его использование ограничено в MC++. VB.Net и C# вообще не поддерживают множественного наследования, но об этом мы еще поговорим при рассмотрении этих языков.
Делегаты. Делегирование – это по сути домысленная и доведенная до совершенства технология обратных (callback) вызовов. В COM все вызовы методов делались по объектным ссылкам, принадлежащим некому типу интерфейса. Чтобы создать обратную связь с объектом, обычно описывался новый событийный интерфейс, и создавалась его реализация. При этом если нужно было создать связь всего лишь по одному методу, все равно приходилось реализовывать весь интерфейс.
CLR-делегаты используются для привязки вызова метода объекта к переменной. Это гибрид указателей на функции в С или, точнее, на функции-члены в C++. Функционально делегаты похожи на интерфейсы с единственным методом, с тем отличием, что целевому типу нужно только иметь метод, чья сигнатура соответствует сигнатуре делегата.
Посмотрите на следующий пример:
public delegate void Hook();
public class MyClassEx {
Hook hook;
public void RegisterHook(Hook h) {
hook = h;
}
public void f() {
if (hook != null) hook(); // callback-вызов
}
}
Заметьте, что вместо интерфейса определяется простой тип-делегат. Заметьте ещё, что вызов перехватчика использует синтаксис, похожий на указатели функций С/C++. Разные языки VS.Net имеют различный синтаксис работы с делегатами, но внутренний механизм един.
Чтобы написать перехватчик MyClassEx, нужно просто реализовать метод, чья сигнатура соответствует сигнатуре делегата Hook:
public class MyHookEx {
public void AnyNameIWant() {
- Программирование - Ирина Козлова - Программирование
- Программирование — вторая грамотность - Андрей Ершов - Программирование
- 97 этюдов для архитекторов программных систем - Нил Форд - Программирование
- Платформа J2Me - Автор неизвестен - Программирование