Настраиваемые атрибуты в .NET

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

Настраиваемые атрибуты применяются в большинстве технологий .NET Framework (WPF, Windows Forms, WCF…), однако, довольно длительное время, я использовал только стандартные атрибуты, которых, к слову, огромное множество, но всё же иногда приходится задавать свои собственные…

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

Для задания атрибуты, нужно создать класс, унаследованный от System.Attribure имя класса, желательно, должно быть в виде NameAtrtribure, хотя использование суффикса Attribute не обязательно, но соответствует стандарту именования.

 
    internal class AuthorNameAttribute : Attribute
    {
        private readonly String _name;
        public AuthorNameAttribute(string name)
        {
            _name = name;
        }

        public String Name
        {
            get { return _name; }
        }
    }

Чтобы ограничить область действия нашего атрибута, необходимо воспользоваться атрибутом [AttributeUsage]. Область действия задаётся любой комбинацией из перечисления AttributeTargets, посредством операции OR. По умолчанию равен AttributeTargets.All.

Значения AttributeTargets:
 
    public enum AttributeTargets
    {
        Assembly = 0x0001,
        Module = 0x0002,
        Class = 0x0004,
        Struct = 0x0008,
        Enum = 0x0010,
        Constructor = 0x0020,
        Method = 0x0040,
        Property = 0x0080,
        Field = 0x0100,
        Event = 0x0200,
        Interface = 0x0400,
        Parameter = 0x0800,
        Delegate = 0x1000,
        ReturnValue = 0x2000,
        GenericParameter = 0x4000,

        All = Assembly | Module | Class | Struct | Enum | Constructor |
                        Method | Property | Field | Event | Interface | Parameter |
                        Delegate | ReturnValue | GenericParameter,
    }

Помимо области действия еще можно задать 2 параметра, AllowMultiple и Inherited.

AllowMultiple — указывает, что атрибут можно применять несколько раз, по умолчанию равен false.

Inherited — указывает, будет ли атрибут, применяемый к базовому классу, применяться также к производным классам, по умолчанию равен true.

Для нашего атрибута установим область действия только для класса и укажем, что он может применяться несколько раз.

 
    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)]
    internal class AuthorNameAttribute : Attribute
    {
        private readonly String _name;
        public AuthorNameAttribute(string name)
        {
            _name = name;
        }

        public String Name
        {
            get { return _name; }
        }
    }

Теперь создадим класс MyProgram и определим функцию, которая будет выводить в консоль всех авторов данного класса.

 
    [AuthorName("Василий Пупкин")]
    [AuthorName("Дмитрий Иванов")]
    public static class MyProgram
    {
        public static void PrintAuthors()
        {
            var thisType = typeof (MyProgram);
            if (thisType.IsDefined(typeof (AuthorNameAttribute), false))
            {
                var authors = thisType.GetCustomAttributes(typeof(AuthorNameAttribute), false);
                foreach (var author in authors)
                {
                    var z = (AuthorNameAttribute) author;
                   Console.WriteLine(z.Name);
                }
            }
        }
    }

К классу MyProgram мы применили 2 атрибута AuthorName с пользователями Василий и Дмитрий. Метод PrintAuthors работает следующим образом:

var thisType = typeof (MyProgram); — Получает объект Type для класса MyProgram.

thisType.IsDefined(typeof (AuthorNameAttribute), false) — проверяет наличие хотябы одного экземпляра атрибута AuthorNameAttribute.

var authors = thisType.GetCustomAttributes(typeof(AuthorNameAttribute), false); - получает все атрибуты с типом AuthorNameAttribute.

foreach (var author in authors) { var z = (AuthorNameAttribute) author; Console.WriteLine(z.Name); }

Перебираем все атрибуты и выводим значение свойства Name в консоль.


Результат работы MyProgram.PrintAuthors(); будут следующий:

GetCustomAttributes — возвращает массив, каждый элемент которого является экземпляром указанного класса атрибута.

GetCustomAttribute — возвращает экземпляр указанного класса атрибута.p>

IsDefined — возвращает значение true при наличии хотя бы одного экземпляра указанного класса, производного от Attribute, связанного с целью.

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

Добавить комментарий