Атрибуты элементов программы
В языке Перфолента.Net можно привязать к элементам программы дополнительную информацию, которая придаст им дополнительный смысл, особое назначение или особые правила использования.
«Атрибут - это как наклейка на коробке,
на которой написана дополнительная информация.»
Современные языки программирования имеют очень много возможностей, а современные программы стали очень большими. Вводить все новые и новые сущности в язык программирования не хочется, т.к. это ведет к трудностям в его изучении и понимании, но все же иногда хочется придать новый смысл каким-либо конструкциям языка или сопроводить их дополнительной информацией об их использовании или поведении. Дополнительная информация об элементах программы может понадобиться многим видам пользователей, среди которых могут быть и компиляторы, и инструменты разработки, исследования кода, отладки, рефакторинга, документирования и другие. Все эти задачи в .Net языках решаются с помощью атрибутов.
Атрибут - это как наклейка на коробке, на которой написана дополнительная информация.
Думаете я зря это повторил? Нет! В этом вся суть атрибутов – они дают дополнительную информацию о классе, структуре, поле, методе, параметре метода и о любом другом элементе программы к которому прикреплены.
В языке Перфолента, как и в других .Net языках, атрибуты элементов программы доступны как на стадии компиляции, так и на стадии выполнения. Это значит, что как компилятор может внести изменения в генерируемый машинный код основываясь на атрибутах, так и выполняющаяся программа может менять своё поведение в зависимости от атрибутов выполняемого кода.
Процесс получения атрибутов сборки и других элементов программы называется рефлексией (reflection).
Виды атрибутов.
Часть используемых вами атрибутов определена прямо в языке Перфолента. Это атрибуты видимости, наследования, переопределения методов и другие. С каждым из них мы познакомимся чуть позже.
Некоторые атрибуты определены в библиотеке Net Framework и могут использоваться компиляторами любых .Net языков. Другие определены в стандартной библиотеке Перфоленты и могут использоваться компилятором Перфоленты и программами, использующими эту библиотеку. А некоторые атрибуты будут определены Вами в вашей программе и использовать их будете только Вы.
Умнику на заметку: в языке 1С, как и в некоторых других языках программирования, атрибуты называют аннотациями. В разных языках синтаксис записи атрибутов (аннотаций) различен, как и способы их использования, но в большинстве современных языков они в том или ином виде есть.
Устройство атрибута.
Технически, атрибуты — это специальные классы, унаследованные от класса Атрибут, который в .Net соответствует классу System.Attribute.
Атрибуты могут что-то значить сами по себе или иметь конструктор, принимающий на хранение дополнительную информацию в виде параметров. Типы параметров конструктора атрибута, а также полей и свойств, ограничены в .Net следующими типами: Цел8, Цел32, Цел64, Байт, Бит32, Бит64, Вещ, ДВещ, Строка, Символ, Булево, Тип, а также это может быть Перечисление одного из указанных типов.
К сожалению, платформа .Net не поддерживает тип Дата в качестве параметра атрибута, но при необходимости дату (как и другие сериализуемые типы) можно представлять строкой.
Определим простейший атрибут:
&ВидноВсем
Класс АнглийскийСиноним
Родитель Атрибут
Поле _Синоним тип Строка
&ВидноВсем
Конструктор(Синоним
тип Строка)
_Синоним
= Синоним
КонецКонструктора
&ВидноВсем
Функция ПолучитьСиноним()
тип Строка
Возврат _Синоним
КонецФункции
КонецКласса
Как видим, кроме наследования от типа Атрибут, других особенностей у класса-атрибута нет.
Используем созданный нами атрибут для одного из методов программы:
&АнглийскийСиноним("Start"), ВидноВсем
Процедура Старт
//тут какой-то
код
КонецПроцедуры
Мы задали для процедуры Старт два атрибута, один созданный нами - АнглийскийСиноним, а другой встроенный - ВидноВсем. Английский синоним процедуры Старт сможет узнать любая программа (в том числе и эта), которая захочет исследовать сборку программы содержащей процедуру.
Во время компиляции создается экземпляр объекта указанного класса-атрибута и сохраняется в сборке в области метаданных вместе с элементом программы, который отмечен этим атрибутом. Любой код, который использует сборку может восстановить экземпляр объекта-атрибута и узнать значения его полей и свойств, получая необходимую дополнительную информацию об элементе программы, который был отмечен этим атрибутом.
Синтаксис атрибута.
Элемент программы может иметь один или несколько атрибутов.
Формальный синтаксис атрибута выглядит так:
&[Новый] ИмяАтрибута[(ПараметрКонструктора1,
…, ПараметрКонструктораН)][{ .СвойствоАтрибута1=ЗначениеАтрибута1,
…, . СвойствоАтрибутаН=ЗначениеАтрибутаН}]
В квадратных скобках указаны части объявления атрибута, которые могут отсутствовать. В круглых скобках задаются параметры конструктора атрибута, а в фигурных скобках происходит инициализация свойств атрибута, если они у него есть.
Например, в объявлении процедуры Старт, которое было показано выше, у атрибута ВидноВсем в наличии только имя, а у атрибута АнглийскийСиноним в наличии один фактический параметр конструктора.
Синтаксис списка атрибутов.
Список атрибутов располагается перед элементом программы, к которому он принадлежит, начинается с символа & и разделяется запятыми, если символ & не повторяется, либо пробелами или переносами строки, если символ & повторяется:
&Атрибут1, Атрибут2, АтрибутН ЭлементПрограммы...
&Атрибут1 &Атрибут2 &АтрибутН ЭлементПрограммы...
Каждый следующий атрибут и сам элемент программы можно записывать с новой строки, не забывая про запятые между атрибутами:
&Атрибут1,
Атрибут2,
АтрибутН
ЭлементПрограммы...
&Атрибут1
&Атрибут2
&АтрибутН
ЭлементПрограммы...
Каждый отдельный атрибут — это экземпляр объекта указанного класса, созданный оператором Новый, с последующим заполнением полей и свойств созданного объекта инициализатором, если необходимо.
В конструктор атрибута можно передать необходимое количество параметров (в данном случае один):
&Новый АнглийскийСиноним ("MyClass")
Ключевое слово Новый при создании атрибутов не обязательно, просто помните, что оно там присутствует неявно:
&АнглийскийСиноним ("MyClass")
После вызова конструктора можно заполнить значения полей и свойств атрибута с помощью инициализатора (в фигурных скобках):
&Новый
ИмпортМетода("kernel32.dll"){
.EntryPoint="SetConsoleTitleW", .CharSet=CharSet.Auto,
.SetLastError=True}
Значениями параметров конструктора, полей и свойств могут быть только константные выражения, вычисляемые при компиляции.
//в этом атрибуте проверим
вычисление константных выражений в параметрах
&EnumerationType("Виды"+"Операци"+"и"с,
"Operation"+"Types",
Не Ложь)
Для встроенных атрибутов, например, таких, как атрибуты видимости членов, можно использовать стандартный синтаксис, но смысла в этом нет:
&Новый ВидноВсем(){}, Новый ВидноСборке(){}
Пустые круглые и фигурные скобки, как и ключевое слово Новый не обязательны:
&ВидноВсем, ВидноСборке
Встроенные атрибуты языка Перфолента.
В язык перфолента встроены следующие атрибуты:
ВидноВсем, ВидноСборке, ВидноНаследникам, ВидноНаследникамСборки - атрибуты видимости классов и их членов;
ПространствоИмен("МоёПространство "), ПространствоИмён("МоёПространство") (синонимы) - атрибут класса, определяющий пространство имен, к которому принадлежит класс;
ОднопоточнаяМодель, МногопоточнаяМодель - атрибуты метода Старт, определяющие модель взаимодействия для COM объектов;
ОбщийДляКласса - атрибут члена класса (поля, метода, свойства или события) делающий его общим для всех созданных экземпляров этого класса;
Глобальный - атрибут модуля, определяющий доступ ко всем видимым членам модуля из всех методов программы;
Сериализуемый, НеСериализуемый – атрибуты класса, указывающие компилятору необходимость создать код поддержки сериализации данных класса или не возможность его сериализации;
ДолженНаследоваться, МожетНаследоваться, НеМожетНаследоваться - атрибуты класса, определяющие возможность или необходимость его наследования;
ДолженПереопределяться, МожетПереопределяться, НеМожетПереопределяться - атрибуты члена класса, определяющие возможность или необходимость его переопределения;
Переопределение - атрибут переопределения метода родителя в классе-наследнике;
Перекрытие - атрибут перекрытия метода родителя в классе-наследнике;
Перегрузка - атрибут перегрузки методов с одинаковыми именами;
ТолькоЧтение, ТолькоЗапись - атрибуты ограничения доступа к полям и свойствам;
Обработчик("МоёСобытие") - атрибут метода класса (процедуры), определяющий, что метод является обработчиком указанного события;
ИмпортМетода – специальный атрибут метода, указывающий компилятору, что реализация метода находится во внешней библиотеке и имеет указанную сигнатуру;
Устаревший("Этот элемент устарел!") – специальный атрибут, указывающий пользователям программы, что этот элемент устарел;
Небезопасно – специальный атрибут, указывающий компилятору, что этот элемент программы содержит небезопасный код, например, прямые указатели на область памяти;
РасширяетТип, СужаетТип– атрибуты оператора ЗнчКТипу, указывающие компилятору, будет ли в процессе преобразования значения из одного типа в другой происходить потеря данных или точности значения;
Флаги – специальный атрибут перечисления, указывающий компилятору, что каждое значение перечисления интерпретируется как набор битовых флагов. Например, в значении 0b0010 установлен флаг во втором разряде (единица);
Реализует("ПолноеИмяЧленаИнтерфейса") – специальный атрибут члена класса, указывающий компилятору, что этот член реализует член указанного интерфейса;
Назначение и особенности использования встроенных атрибутов подробнее рассматриваются в тех статьях, где описаны сущности языка, к которым применяются конкретные атрибуты
Использование особых видов атрибутов.
Атрибуты сборки.
В .Net определены разнообразные атрибуты, которые есть смысл применять к сборке в определенных ситуациях. Этот вид атрибутов желательно задавать в начале программы на уровне классов.
АтрибутСборки System.Runtime.InteropServices.ComVisible(Ложь)
С оператором АтрибутСборки можно использовать не только атрибуты, определенные в Net, но и созданные вами атрибуты, если в дальнейшем Вы захотите получать дополнительную информацию о сборке.
Атрибуты модуля сборки.
В .Net определены несколько атрибутов, которые есть смысл применять к модулю сборки в определенных ситуациях (не путайте модуль сборки с модулями программы). Этот вид атрибутов желательно задавать в начале программы на уровне классов.
АтрибутМодуля System.CLSCompliant(Истина)
С оператором АтрибутМодуля можно использовать не только атрибуты, определенные в Net, но и созданные вами атрибуты, если в дальнейшем Вы захотите получать дополнительную информацию о модуле сборки.
Атрибуты параметров метода.
Не только сами методы могут иметь атрибуты, но и их параметры. Для параметров используется тот же синтаксис атрибутов, как и для других элементов программы.
&ВидноВсем
Функция СобратьБит32(
&АнглСиноним("HighDigits") Знач СтаршиеРазряды тип Бит16,
&АнглСиноним("LowDigits")
Знач МладшиеРазряды
тип Бит16
) &АнглСиноним("ReturnValue")
тип Бит32 //к возвращаемому параметру тоже можно применить атрибуты
Возврат (Бит32(СтаршиеРазряды) << 16) БитИли
МладшиеРазряды
КонецФункции
Определение особого порядка и правил передачи полей структуры.
Структуры, которые необходимо передавать коду, написанному на языках подобных C, C++, Rust, чаще всего необходимо отметить атрибутом StructLayout заставляющем среду исполнения сформировать структуру пригодную к передаче. Также необходимо убедиться, что правильно передаются поля имеющие сложный тип, такие как массивы или другие структуры. Для задания правил передачи используется атрибут MarshalAs. Ознакомьтесь с документацией .Net по этим атрибутам, прежде, чем передавать структуры в код, написанный на указанных языках.
&StructLayout(LayoutKind.Sequential){
.Pack=1, .CharSet=CharSet.Auto }
Структура UDT
&ВидноВсем Поле
rxStatus тип Цел16
&ВидноВсем Поле
datalen тип Байт
&ВидноВсем, MarshalAs(UnmanagedType.ByValArray){
.SizeConst=255 }
Поле array1 тип Байт[]
&ВидноВсем
Процедура Инициализировать()
//код инициализации
КонецПроцедуры
КонецСтруктуры
Некоторые случаи применения атрибутов в Net.
1. Пометить метод атрибутом System.Web.Services.WebMethod в Web сервисах, для указания, что метод должен вызываться через SOAP протокол.
2. Описание способа передачи параметров метода в не управляемый код с помощью атрибута System.Runtime.InteropServices.MarshalAs.
3. Описание свойств COM для классов, методов и интерфейсов с помощью группы атрибутов подобных System.Runtime.InteropServices.ComVisible или System.Runtime.InteropServices.DispId.
4. Вызов методов неуправляемого кода с помощью атрибута System.Runtime.InteropServices.DllImport (синоним ИмпортМетода).
5. Описание свойств сборки, таких как, версия, заголовок, копирайт и т.д., с помощью группы атрибутов подобных System.Reflection.AssemblyTitle.
6. Пометка членов класса, которые могут быть сериализованы с помощью атрибута System.Serializable (синоним Сериализуемый).
7. Описание сопоставления между членами класса и узлами XML для сериализации объекта в XML.
8. Описание требований безопасности для методов.
9. Указание характеристик, используемых для обеспечения безопасности.
10. Установка параметров JIT компиляции, например, для сохранения удобства отладки.
11. Получение информации о вызывающем методе.
12. Указание на то, что метод или другой элемент программы устарел с помощью атрибута System.Obsolete (синоним Устаревший).
Использование атрибутов в указанных случаях описано в документации .Net.
Вы можете придумать любые другие случаи, когда будет удобно использовать атрибуты в вашей программе.
Вывод: Атрибуты нужны, чтобы привязать к сборке, к модулю сборки, к элементам программы, к членам классов или параметрам методов дополнительную информацию, которую можно будет использовать на этапе компиляции или на этапе выполнения программы. Атрибут — это класс, унаследованный от типа Атрибут. Информация, заданная через конструктор атрибута, сохраняется в метаданных сборки и может быть извлечена в любое время вашей программой или любой другой программой, которая использует или исследует сборку.
Сергей Рогаткин
К началу статьи
Предыдущая статья:
Оператор Попытка. Структурная обработка ошибок.
Следующая статья:
Оператор Использовать - гарантированное завершение объекта
Вернуться в раздел:
Синтаксис языка Перфолента