Декларативное программирование

Главное значение атрибутов в том, что они позволяют изменять поведение программных элементов. Механизм такого изменения прост. Алгоритм, имеющий дело с экземпляром некоторого объекта, может считать значения атрибутов его типа и, в зависимости от считанных значений атрибутов, принять решение о применении того или иного способа обработки этого объекта. Другими словами, судьба объекта задаётся при его декларациидекларативное программирование.

Код, выполняемый во время разработки

Выше говорилось, что атрибуты могут использоваться любыми программами, в том числе компиляторами.

На примере атрибутов мы впервые столкнулись с тем, что часть кода, который пишет программист, выполняется не во время исполнения runtime, а во время разработки (design time)! Важно подчеркнуть, что в этом случае не просто используются описания типов (декларации), а именно программный код! Ведь чтобы получить информацию об атрибутах, надо создать экземпляры объектов-атрибутов, а значит, в этот момент отрабатывают конструкторы атрибутов.

Вот как пишет об использовании атрибутов компилятором Э. Гуннерсон:

Когда компилятор находит атрибут X, установленный для класса, он сначала ищет класс, производный от Attribute, с именем X. Не обнаружив такого класса, он начинает искать класс XAttribute. На этот раз поиск заканчивается успешно. После этого компилятор проверяет, можно ли использовать данный атрибут для классов (AttributeUsage). Затем начинается поиск конструктора, который бы соответствовал параметрам, указанным при установке атрибута, если такой конструктор найден, компилятор создаёт экземпляр объекта вызовом конструктора с заданными параметрами. При передаче именованных параметров компилятор сопоставляет имя параметра с именем переменной класса или свойства, после чего присваивает переменной или свойству указанное значение. Во всяком случае, так должно происходить на логическом уровне.

Существует несколько причин, по которым схема сохранения атрибутов работает не так, как было описано выше. В первую очередь это связано с быстродействием. Чтобы компилятор мог реально создавать объект атрибута, в это время должна работать среда .NET, поэтому компилятор должен был бы работать как управляемый исполняемый файл.

Впрочем, создавать объект в действительности и не требуется, поскольку мы всё равно ограничимся сохранением информации. Поэтому компилятор только убеждается в том, что он может создать объект, вызвать конструктор и присвоить значения всех именованных параметров. Параметры атрибута заносятся в небольшой блок двоичных данных, который сохраняется вместе с метаданными объекта.

Такая схема работы компилятора принципиально ничего не меняет. Дело в том, что другие программные инструменты могут создавать объекты атрибутов "на самом деле", да и сам компилятор в следующих версиях .NET Framework вполне может начать работать по алгоритму, близкому к тому, по которому он работает на логическом уровне. Поэтому нам следует думать об атрибутах как об объектах, которые могут работать в design time.

Использование атрибутов

Как уже отмечалось атрибут - это некоторая дополнительная информация, которая может быть приписана к типам, полям, методам, свойствам и некоторым другим конструкциям языка. Атрибуты помещаются в исполняемый файл и могут оттуда при необходимости извлекаться.

Все атрибуты .NET являются классами - потомками класса System.Attribute.

Атрибуты делятся на предопределенные (встроенные) и пользовательские, которые пишет программист. Таким образом, набор атрибутов .NET открыт для пополнения, т. е. программист может определять собственные атрибуты и применять их к вышеуказанным элементам своего кода.

Класс атрибута всегда должен иметь модификатор доступа public.

Назначаемый атрибут инициализируется вызовом конструктора с соответствующими параметрами. Таким образом, класс атрибута должен иметь хотя бы один public-конструктор.

Атрибут указывается в квадратных скобках перед элементом, которому он назначается. Например

 

[ObsoleteAttribute("Hallow World")]

 

По соглашению, имена всех атрибутов оканчиваются словом Attribute. Но в VisualStudio.NET при назначении атрибута можно не указывать суффикс Attribute. Так, вместо

 

[ObsoleteAttribute("Hallow World")]

 

можно записать

 

[Obsolete ("Hallow World")]

 

Атрибуты в программном коде используются следующим образом:

1. Определяется новый или берется существующий в .NET Framework атрибут;

2. Инициализируется конкретный экземпляр атрибута с помощью вызова конструктора атрибута.