Существует два типа атрибутов: Предопределенные атрибуты (идут в поставке FCL), и пользовательские атрибуты, создаваемые пользователем для добавления в код дополнительных сведений. С точки зрения разработчика оба типа имеют одинаковый синтаксис.
Пользовательский атрибут представляет собой обычный класс, унаследованный от класса Attribute. Такой атрибут может быть использован для любого метода, свойства, класса или сборки при использовании следующего синтаксиса: [ИмяАтрибута(необязательный параметр, дополнительные пары имя=значение)]
Attribute — ключевой класс при работе с атрибутами. Все системные классы-атрибуты наследуются от данного класса. При создании собственных атрибутов, также необходимо наследоваться от данного класса.
Информация, предоставляемая атрибутом, называется также метаданными. Метаданные можно анализировать в приложении во время выполнения, для того чтобы управлять тем, как это приложение осуществляет обработку данных, или до времени выполнения внешними средствами для управления обработкой и выполнением самого приложения. Например, на платформе .NET Framework предопределены и используются типы атрибутов для управления поведением времени выполнения, и некоторые языки программирования используют типы атрибутов для представления языковых функций, не поддерживаемых непосредственно общей системой типов .NET Framework.
Правила создания пользовательского атрибута:
- Имя атрибута должно содержать суффикс Attribute
- Класс-атрибут обязан наследоваться от системного класса System.Attribute
- Класс-атрибут обязан быть декорирован атрибутом [AttributeUsageAttribute]
Системный атрибут AttributeUsage, необходим для создания пользовательских атрибутов.
Позиционные параметры:
- validOn – имеет тип AttributeTargets, указывает, к чему можно применять данный атрибут.
Именованные параметры:
- AllowMultiple – имеет тип bool, разрешает или запрещает множественное применение атрибута (Значение по умолчанию — false).
- Inherited – имеет тип bool, разрешает или запрещает наследование атрибута в производных классах (Значение по умолчанию — true).
Системный атрибут ObsoleteAttribute, позволяет отмечать устаревшие элементы системы.
Позиционные параметры:
- message – имеет тип string, содержит строку сообщения, которая показывается при попытке использования устаревшего элемента.
- error – имеет тип bool, если установлен в true – использование элемента, помеченного атрибутом, будет порождать ошибку на этапе компиляции.
Позиционные параметры указываются в порядке, который определяется списком параметров конструктора атрибута. Позиционные параметры всегда должны быть указаны при назначении атрибута.
Именованные параметры отсутствуют в списке параметров конструктора атрибута. Значения, задаваемые для именованных параметров, используются для инициализации полей и свойств создаваемого экземпляра атрибута. Список именованных параметров указывается через запятую после списка позиционных параметров. Каждый именованный параметр определяется как: Имя_параметра =Значение_параметра.
В C# предопределено много системных (встроенных) атрибутов, но особое значение имеют следующие три: AttributeUsage, Conditional, Obsolete.
AttributeUsage – определяет типы элементов, к которым может быть применен объявляемый атрибут, а также возможность неоднократного применения атрибута и возможность наследования атрибута производными классами. За типы элементов, к которым может быть применен атрибут отвечает позиционный параметр AttributeTargets, допустимые значения для этого перечисления можно детальнее посмотреть на MSDN. Именованный параметр AllowMultiple может принимать значения true или false и отвечает за возможность неоднократного применения атрибута. И, наконец, за возможность наследования атрибутов отвечает именованный параметр Inherited.
Conditional – позволяет создавать условные методы, которые вызываются только в том случае, если с помощью директивы #define определен конкретный идентификатор, а иначе метод пропускается. Следовательно, условный метод служит альтернативной условной компиляции по директиве #if. Для применения данного атрибута в исходный код программы следует включить пространство имен System.Diagnostics. На условные методы накладываются следующие ограничения:
- Они должны возвращать значение типа void.
- Должны быть членами класса или структуры, но не интерфейса.
- Они не могут предшествовать ключевому слову override.
Obsolete – позволяет пометить элемент программы как устаревший. Существует несколько способов применения данного атрибута.
Первый выглядит так:
[Obsolete(“сообщение”)]. Применяется, когда необходимо просто пометить код, как устаревший и вывести соответствующее сообщение.
Вторая форма:
[Obsolete(“сообщение”, ошибка)]. Применяется, когда кроме вывода сообщения также необходимо запретить компиляцию.
Пример применения аттрибута Obsolete:
Пример создания пользовательского аттрибута AttributeUsage :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
// Для создания атрибута необходимо создать класс производный от класса : System.Attribute. // Атрибут - [AttributeUsage] - задает свойства пользовательских атрибутов. // Позиционный параметр - AttributeTargets, определяет элементы программы, для которых атрибут может быть применен. // Параметр - AttributeTargets.All - позволяет использовать атрибут - MyAttribute, для любого элемента. // Именованный параметр - AllowMultiple = false, определяет сколько раз к одному элементу можно применять атрибут. [AttributeUsage(AttributeTargets.All, AllowMultiple = false)] class MyAttribute : System.Attribute { private readonly string name; public string Name { get { return name; } } // Позиционные параметры задаются формальными параметрами - public конструктора, класса атрибута. public MyAttribute(string name) { this.name = name; } // Именованные параметры задаются открытыми нестатическими полями или свойствами, класса атрибута. public int Age { get; set; } } |
Пример использования пользовательского атрибута:
1 2 3 4 5 6 7 8 9 |
[My("Andrey Smolensky", Age = 35)] public class MyClass { [MyAttribute("nookery", Age = 3)] public static void Method() { Console.WriteLine("MyClass.Method()\n"); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
static void Main() { // MemberInfo - абстрактный класс, используется для получения информации о членах класса. //Type type = typeof(MyClass); MemberInfo type = typeof(MyClass); // Метод GetCustomAttributes() - возвращает массив объектов, которые при выполнении приложения // представляют собой эквиваленты атрибутов, созданных в исходном коде. // Извлекаем из элементов массива элементы типа - MyAttribute. object[] attributes = type.GetCustomAttributes(typeof(MyAttribute), true); // Если в массиве есть соответствующие записи, то первый элемент представляет собой атрибут - MyAttribute. if (attributes.GetLength(0) != 0) { // Отображаем полученные значения. foreach (MyAttribute attribute in attributes) { Console.WriteLine(attribute.Name); Console.WriteLine(attribute.Age); } } Console.ReadKey(); } |
// Глобальные атрибуты для всей сборки.
[assembly: AssemblyVersionAttribute(«1.0.1000.0»)] // Версия сборки.
[assembly: AssemblyTitle(«AssemblySmpl»)] // Имя сборки.
[assembly: AssemblyDescription(«»)] // Описание сборки.
[assembly: AssemblyConfiguration(«»)] // Конфигурация, например, Release или Debug.
[assembly: AssemblyCompany(«nookery»)] // Имя компании разработчика.
[assembly: AssemblyProduct(«App»)] // Имя продукта.
[assembly: AssemblyCopyright(«Copyright 2018»)] // Копирайты.
[assembly: AssemblyTrademark(«»)] // Торговая марка.
[assembly: AssemblyCulture(«»)] // Какие культуры поддерживает сборка.
Рассмотрим пример того как можно разграничить уровень доступа к секретному методу, среди персонала:
1 2 3 4 5 6 7 8 9 |
[AttributeUsage(AttributeTargets.All, AllowMultiple = false)] class AccessLevelAttribute : Attribute { private int level; public AccessLevelAttribute(int level) { this.level = level; } } |
Здесь мы создали пользовательский аттрибут который можем применять где угодно, но в одном экземпляре. О чем и свидетельствуют параметры, класс AccessLevelAttribute унаследован от класса Attribute. В классе имеется приватное поле означающее уровень доступа и конструктор с параметром принимающий значения.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
class Logic { public void TopSecret() { Console.WriteLine("Нажал секретную кнопку"); } } [AccessLevel(level: 0)] class Manager : Logic { } [AccessLevel(level: 1)] class Programmer : Logic { } [AccessLevel(level: 2)] class Director : Logic { } class Program { static void Main(string[] args) { Logic[] l = new Logic[3]; Director d = new Director(); Programmer p = new Programmer(); Manager m = new Manager(); l[0] = new Manager(); l[1] = new Programmer(); l[2] = new Director(); for (int i = 0; i < 3; i++) { Console.WriteLine("Проверка доступа Работника: {0} к секретному методу", l[i].GetType().Name); Type type = l[i].GetType(); object[] attributes = type.GetCustomAttributes(false); foreach (AccessLevelAttribute att in attributes) { if (att.level == 0) Console.WriteLine("Отказано"); if (att.level == 1) Console.WriteLine("Неполный доступ"); if (att.level == 2) { Console.WriteLine("Доступ разрешен"); l[i].TopSecret(); } } Console.WriteLine(new string('*', 80)); } Console.ReadKey(); } } |
В классе Logic мы создали метод TopSecret к которому можно обращаться только по уровню доступа персонала. Создали 3 класса персонала и наследовались от класс Logic, так же установили атрибут перед объявлением класса, [AccessLevel(level: 0)] и значение уровня доступа. Далее мы создали массив персонала и запустили в цикле проверку на проверку доступа к методу.