Российский Государственный Технологический Университет им. К.Э. Циолковского
Опубликован: 02.03.2007 | Доступ: свободный | Студентов: 5636 / 790 | Оценка: 3.96 / 3.45 | Длительность: 27:04:00
ISBN: 978-5-9556-0086-4
Лекция 10:

Атрибуты, сборки, рефлексия

Атрибуты

Атрибут – средство добавления ДЕКЛАРАТИВНОЙ информации к элементам программного кода. Назначение атрибутов – внесение всевозможных не предусмотренных обычным ходом выполнения приложения изменений:

  • описание взаимодействия между модулями;
  • дополнительная информация, используемая при работе с данными (управление сериализацией);
  • отладка;
  • и многое другое.

Эта декларативная информация составляет часть метаданных кода. Она может быть использована при помощи механизмов отражения.

Структура атрибута регламентирована. Атрибут – это класс. Общий предок всех атрибутов – класс System.Attribute.

Информация, закодированная с использованием атрибутов, становится доступной в процессе ОТРАЖЕНИЯ (рефлексии типов).

Атрибуты типизированы.

.NET способна прочитать информацию в атрибутах и использовать ее в соответствии с предопределенными правилами или замыслами разработчика. Различаются:

  • предопределенные атрибуты. В .NET реализовано множество атрибутов с предопределенными значениями:

    DllImport – для загрузки .dll-файлов;

    Serializable – означает возможность сериализации свойств объекта – представителя класса;

    NonSerialized – обозначает данные-члены класса как несериализуемые. Карандаши (средство графического представления информации, элемент GDI+) не сериализуются;

  • производные (пользовательские) атрибуты могут определяться и использоваться в соответствии с замыслами разработчика. Возможно создание собственных (пользовательских) атрибутов. Главные условия:
    • соблюдение синтаксиса;
    • соблюдение принципа наследования.

В основе пользовательских атрибутов – все та же система типов с наследованием от базового класса System.Attribute.

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

Добавлять атрибуты можно к:

  • сборкам;
  • классам;
  • элементам класса;
  • структурам;
  • элементам структур;
  • параметрам;
  • возвращаемым значениям.

Ниже приводится описание членов класса Attribute.

Открытые свойства
TypeId При реализации в производном классе получает уникальный идентификатор для этого атрибута Attribute
Открытые методы
Equals Переопределен (см. Object.Equals )
GetCustomAttribute Перегружен. Извлекает пользовательский атрибут указанного типа, который применен к заданному члену класса
GetCustomAttributes Перегружен. Извлекает массив пользовательских атрибутов указанного типа, которые применены к заданному члену класса
GetHashCode Переопределен. Возвращает хэш-код для этого экземпляра
GetType (унаследовано от Object ) Возвращает Type текущего экземпляра
IsDefaultAttribute При переопределении в производном классе возвращает значение, показывающее, является ли значение этого производного экземпляра значением по умолчанию для производного класса
IsDefined Перегружен. Определяет, применены ли какие-либо пользовательские атрибуты заданного типа к указанному члену класса
Match При переопределении в производном классе возвращает значение, указывающее, является ли этот экземпляр эквивалентным заданному объекту
ToString (унаследовано от Object ) Возвращает String, который представляет текущий Object
Защищенные конструкторы
Attribute -конструктор Инициализирует новый экземпляр класса Attribute
Защищенные методы
Finalize (унаследовано от Object ) Переопределен. Позволяет объекту Object попытаться освободить ресурсы и выполнить другие завершающие операции, перед тем как объект Object будет уничтожен в процессе сборки мусора.

В C# для функций финализации используется синтаксис деструктора

MemberwiseClone (унаследовано от Object ) Создает неполную копию текущего Object

Следующий пример является демонстрацией объявления и применения производных атрибутов:

using System;
 using System.Reflection;

namespace CustomAttrCS 
 {
 // Перечисление of animals.
 // Start at 1 (0 = uninitialized).
 public enum Animal 
 {
 // Pets.
 Dog = 1,
 Cat,
 Bool,
 }

 // Перечисление of colors.
 // Start at 1 (0 = uninitialized).
 public enum Color 
 {
 // Colors.
 Red = 1,
 Brown,
 White,
 }


 // Класс пользовательских атрибутов.
 public class AnimalTypeAttribute : Attribute 
 {//==============================================================
 // Данное - член типа "перечисление". 
protected Animal thePet;
protected string WhoIs(Animal keyPet)
 { 
string retStr = "";
 switch (keyPet)
 {
 case Animal.Dog: retStr = "This is the Dog!"; break; 
case Animal.Cat: retStr = "This is the Cat!"; break; 
case Animal.Bull: retStr = "This is the Bool!"; break; 
default: retStr = "Unknown animal!"; break;
 }

return retStr;
 }

 // Конструктор вызывается при установке атрибута.
 public AnimalTypeAttribute(Animal pet) 
 {
 thePet = pet;
 Console.WriteLine("{0}", WhoIs(pet));
 }

 // Свойство, демонстрирующее значение атрибута.
 public Animal Pet 
 {
 get
 {
 return thePet;
 }
 set
 {
 thePet = value;
 }
 }
 }//==============================================================

 // Еще один класс пользовательских атрибутов.
 public class ColorTypeAttribute : Attribute 
 {//==============================================================
 // Данное - член типа "перечисление". 
protected Color theColor;

 // Конструктор вызывается при установке атрибута.
 public ColorTypeAttribute(Color keyColor) 
 {
 theColor = keyColor;
 }

 // Свойство, демонстрирующее значение атрибута.
 public Color ColorIs 
 {
 get
 {
 return theColor;
 }
 set
 {
 theColor = ColorIs;
 }
 }
 }//==============================================================


 // A test class where each method has its own pet.
 class AnimalTypeTestClass 
 {//==============================================================
 // Содержит объявления трех методов, каждый из которых
 // предваряется соответствующим ПОЛЬЗОВАТЕЛЬСКИМ атрибутом.
 // У метода может быть не более одного атрибута данного типа.
 [AnimalType(Animal.Dog)]
 [ColorType(Color.Brown)]
 public void DogMethod() 
 {
 Console.WriteLine("This is DogMethod()...");
 }

 [AnimalType(Animal.Cat)]
 public void CatMethod() 	
 {
 Console.WriteLine("This is CatMethod()...");
 }


 [AnimalType(Animal.Bool)]
 [ColorType(Color.Red)]
 public void BoolMethod(int n, string voice)
 {
 int i;
 Console.WriteLine("This is BoolMethod!");
 	
 if (n > 0) for (i = 0; i < n; i++)
  {
 Console.WriteLine(voice);
  }
 }
 }//==============================================================

 class DemoClass 
 {//==============================================================
 static void Main(string[] args) 
 {
 int invokeFlag;
 int i;

 // И вот ради чего вся эта накрутка производилась...
 // Объект класса AnimalTypeTestClass под именем testClass
 // представляет собой КОЛЛЕКЦИЮ методов, каждый из которых
 // снабжен соответствующим ранее определенным пользовательским
 // СТАНДАРТНЫМ атрибутом. У класса атрибута AnimalTypeAttribute есть все,
 // что положено иметь классу, включая конструктор.
 AnimalTypeTestClass testClass = new AnimalTypeTestClass();

 // Так вот создали соответствующий объект - представитель класса...
 // Объект - представитель класса сам может служить источником 
 // информации о собственном классе. Информация о классе представляется 
 // методом GetType() в виде
 // объекта - представителя класса Type. Информационная капсула! 
Type type = testClass.GetType();

 // Из этой капсулы можно извлечь множество всякой "полезной" информации...
 // Например, можно получить коллекцию (массив) элементов типа MethodInfo
 // (описателей методов), которая содержит список описателей методов, 
 // объявленных в данном классе. В список будет включена информация 
 // о ВСЕХ методах класса: о тех, которые были определены явным 
 // образом, и о тех, которые были унаследованы
 // от базовых классов. И по этому списку описателей методов мы 
 // пройдем победным маршем ("Ha-Ha-Ha") оператором foreach.
 i = 0;

foreach(MethodInfo mInfo 
in
type.GetMethods()) 
 {
 invokeFlag = 0;
 Console.WriteLine("#####{0}#####{1}#####", i, mInfo.Name);
 // И у каждого из методов мы спросим относительно множества атрибутов,
 // которыми метод был снабжен при объявлении класса.
 foreach (Attribute attr
in 
Attribute.GetCustomAttributes(mInfo)) 
 {
 Console.WriteLine("~~~~~~~~~~");
 // Check for the AnimalType attribute.
 if (attr.GetType() == typeof(AnimalTypeAttribute))
 {
 Console.WriteLine("Method {0} has a pet {1} attribute.", 
mInfo.Name,
 ((AnimalTypeAttribute)attr).Pet);
 // Посмотрели значение атрибута – и если это Animal.Bool – подняли флажок.
 if (((AnimalTypeAttribute)attr).Pet.CompareTo(Animal.Bool) == 0)
                                                 invokeFlag++;
 }

if (attr.GetType() == typeof(ColorTypeAttribute))
 {
 Console.WriteLine("Method {0} has a color {1} attribute.", 
 mInfo.Name,
 ((ColorTypeAttribute)attr).ColorIs);

 // Посмотрели значение атрибута – и если это Color.Red –
 // подняли флажок второй раз.
 if (((ColorTypeAttribute)attr).ColorIs.CompareTo(Color.Red) == 0)
 invokeFlag++;
 }

 // И если случилось счастливое совпадение значений атрибутов метода
 // (Красный Бычок), то метод выполняется. 
 // Метод Invoke в варианте с двумя параметрами:
 // объект - представитель исследуемого класса
 // (в данном случае AnimalTypeTestClass), и массив объектов-параметров. 
if (invokeFlag == 2)
 {
 object[] param = {5,"Mmmuuu–uu–uu!!! Mmm..."};
 mInfo.Invoke(new AnimalTypeTestClass(),param);
 }

Console.WriteLine("~~~~~~~~~~");
 }

Console.WriteLine("#####{0}#####", i);
 i++;
 }
 }
 }//==============================================================
 }
Листинг 10.2.

Эта программа демонстрирует одну замечательную особенность синтаксиса объявления атрибутов. При их применении можно использовать сокращенные имена ранее объявленных классов атрибутов.

В программе объявлено два класса атрибутов – наследников класса Attribute:

public class AnimalTypeAttribute : Attribute...
public class ColorTypeAttribute : Attribute...

При объявлении соответствующих экземпляров атрибутов последняя часть имени класса атрибута ( ...Attribute ) была опущена.

Вместо

[AnimalTypeAttribute(Animal.Dog)]

используется имя

[AnimalType(Animal.Dog)],

вместо

[ColorTypeAttribute(Color.Red)]

используется

[ColorType(Color.Red)]

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

kewezok kewezok
kewezok kewezok
Елена Шляхт
Елена Шляхт
Объясните плиз в чем отличие а++ от ++а
Почему результат разный?
int a=0, b=0;
Console.WriteLine(a++); //0
Console.WriteLine(++b); //1
a++;
++b;
Console.WriteLine(a); //2
Console.WriteLine(b); //2