
- •1. Понятие модуля. Принципы модульного программирования. Понятие объекта как динамического модуля.
- •2. Понятие класса. Понятие метода. Представление метода в виде обычной процедуры. Понятие конструктора и деструктора.
- •3. Понятие свойства. Методы получения и установки значений свойств. Свойства-массивы (в некоторых языках программирования). Индексаторы (в некоторых языках программирования).
- •Информация о типе времени выполнения программы
- •5. Классы в программных модулях. Атрибуты доступа к элементам объектов. Термин «инкапсуляция».
- •Термин «инкапсуляция»
- •Virtual – в базовом классе
- •7. Понятие ссылки на метод объекта (или делегата – в зависимости от языка программирования). Понятие события. Применение ссылок на методы для расширения объектов.
- •8. Понятие метакласса (в некоторых языках программирования). Методы, применяемые к классам. Виртуальные конструкторы (в некоторых языках).
- •Понятие метакласса (в некоторых языках программирования)
- •Виртуальные конструкторы (в некоторых языках)
- •3) Finally – вызвать free.
- •Если глобально-уникальный идентификатор назначается интерфейсу, то он записывается после ключевого слова interface и заключается в квадратные скобки, например:
- •Microsoft Visual Studio
- •Import Network;
- •Var Stat: SocketStat;
- •Var SocketStatCollection: … ;
- •Конструкторы и деструкторы
- •Стандартные конструкторы
- •Создание объектов по значению (на стеке) и по ссылке (в динамической памяти)
- •Операторы new и delete
- •Размещающий оператор new
- •Порядок конструирования и разрушения объектов
- •Вложенные определения классов
- •«Друзья» класса
- •Статические члены класса
- •19. Перегрузка бинарных операторов. Перегрузка унарных операторов. Перегрузка операторов преобразования типа.
- •Индексаторы
- •Механизм вызова событий
- •Создание пользовательских обобщенных коллекций
- •Создание обобщенных интерфейсов
- •Несколько слов о вложенных делегатах
- •25. Понятие итератора в языке c#. Оператор foreach. Оператор yield.
- •И напоследок... Блок finally
- •26. Понятие атрибутов в языке c#. Создание пользовательских атрибутов. Анализ атрибутов во время выполнения программы. Понятие рефлексии (reflection) в языке c#. Сериализация объектов.
- •Что такое метаданные и зачем они нужны?
- •1. Метаданные в .Net обязательны и универсальны.
- •2. Метаданные в .Net общедоступны.
- •3. Метаданные в .Net исчерпывающи.
- •4. Метаданные в .Net расширяемы.
- •5. Метаданные в .Net конструируемы программно.
- •Получение экземпляра класса Type
- •Динамическая загрузка сборок
- •Динамическая загрузка типов
- •Исследование типа
- •Характеристики типа как целого
- •Члены класса
- •Исследование объекта
- •Динамическое создание объекта и вызов методов
- •Создание объекта по его типу
- •Декларативное программирование
- •Новые механизмы абстракции?
- •Динамическое создание типов
- •Роль графов объектов
- •Formatter сериализации
- •XmlSerializer
- •Интерфейсы iFormatter и iRemotingFormatter
- •Точность типов среди форматеров
- •28*. Ооп в языке программирования Smalltalk. Достоинства и недостатки этого языка в сравнениии с языком программирования c#.
Декларативное программирование
Но главное значение атрибутов в том, что они позволяют изменять поведение программных элементов. Механизм такого изменения прост. Алгоритм, имеющий дело с экземпляром некоторого объекта, может считать значения атрибутов его типа и, в зависимости от считанных значений атрибутов, принять решение о применении того или иного способа обработки.
Давайте рассмотрим этот механизм на примере работы нашей процедуры протоколирования. Сейчас она выводит абсолютно все поля объекта без разбора. С помощью атрибутов можно создать механизм управления выводом в лог тех или иных полей класса (фильтрации). Для этого достаточно ввести новый атрибут, обозначающий, что протоколирование данного поля не требуется. Ниже приведено описание атрибута NotTrace:
[ AttributeUsage(AttributeTargets.Field) ] public class NotTrace : Attribute { } |
Чтобы упростить использование этого атрибута, напишем простую вспомогательную функцию:
static bool NeedTrace(FieldInfo fi) { return ! Attribute.IsDefined(fi, typeof(NotTrace)); } |
Теперь можно модифицировать функцию протоколирования, чтобы она выводила только те поля, у которых не задан атрибут NotTrace:
foreach (FieldInfo fi in type.GetFields(...)) { if (! NeedTrace(fi)) continue; ... } |
Заметьте, что теперь управление выводом полей производится не изменением кода функции протоколирования, а изменением описаний полей – заданием их атрибутов. Другими словами, судьба объекта задаётся при его декларации. Отсюда название данного метода программирования – декларативное программирование.
Код, исполняющийся в design-time
На примере атрибутов мы впервые (я, по крайней мере, впервые) столкнулись с тем, что часть кода, который пишет программист, выполняется в runtime (это как обычно), а часть кода выполняется в момент разработки (design time)! Не просто используются описания типов (декларации), а именно код! Ведь чтобы получить информацию об атрибутах, надо создать экземпляры объектов-атрибутов, а значит, в этот момент отрабатывают конструкторы атрибутов.
Вот как пишет об использовании атрибутов компилятором Э. Гуннерсон [2]:
Когда компилятор находит атрибут X, установленный для класса, он сначала ищет класс, производный от Attribute, с именем X. Не обнаружив такого класса, он начинает искать класс XAttribute. На этот раз поиск заканчивается успешно. После этого компилятор проверяет, можно ли использовать данный атрибут для классов (AttrubuteUsage). Затем начинается поиск конструктора, который бы соответствовал параметрам, указанным при установке атрибута, если такой конструктор найден, компилятор создаёт экземпляр объекта вызовом конструктора с заданными параметрами. При передаче именованных параметров компилятор сопоставляет имя параметра с именем переменной класса или свойства, после чего присваивает переменной или свойству указанное значение. Во всяком случае, так должно происходить на логическом уровне. <...> Существует несколько причин, по которым схема сохранения атрибутов работает не так, как было описано выше. В первую очередь это связано с быстродействием. Чтобы компилятор мог реально создавать объект атрибута, в это время должна работать среда .Net, поэтому компилятор должен был бы работать как управляемый исполняемый файл. Впрочем, создавать объект в действительности и не требуется, поскольку мы всё равно ограничимся сохранением информации. Поэтому компилятор только убеждается в том, что он может создать объект, вызвать конструктор и присвоить значения всех именованных параметров. Параметры атрибута заносятся в небольшой блок двоичных данных, который сохраняется вместе с метаданными объекта. |
От себя же добавлю, что такая схема работы компилятора принципиально ничего не меняет. Дело в том, что другие программные инструменты могут создавать объекты атрибутов "на самом деле", да и сам компилятор в следующих версиях .Net Framework вполне может начать работать по алгоритму, близкому к тому, по которому он работает на логическом уровне. Потому нам следует думать об атрибутах как об объектах, которые могут работать в design time.