Главная » C#, Компоненты » Метаинформация, отражение и атрибуты

0

Взаимодействие частей программ между собой подразумевает наличие информации о предоставляемых ими возможностях. Например, к библиотеке DLL прилагается заголовочный файл, описывающий экспортируемые данные, процедуры и структуру типов. Для СОМ-компонента описание хранится в idl-файле или в виде библиотеки типов.

Информация, описывающая компоненты модуля, называется метагтформа- ijiteii. Приставка "мета" подчеркивает, что это "данные о данных", т. е. данные, описывающие другие данные.

Частично технология хранения метаинформации была реализована во многих языках программирования. Например, в Borland Delphi и С++ с помощью методов RTT1 (Run-Time Type Identification) можно было получать информацию о типе данных во время выполнения кода. Но в .NET метаинформация играет не дополнительную, а фактически основную роль. Метаинформация в .NET хранит полную и исчерпывающую информацию обо всех типах, методах, параметрах и возвращаемых значениях, независимо от их области видимости. Другими словами, метаинформация содержит описания всех типов и методов— и public и private и protected… Более того, всю эту информацию можно получить программным путем, и она доступна для расширения.

Получение информации о типе во время выполнения называется отражением (reflection). Классы и методы, предоставляющие доступ к методам отражения, находятся В пространстве имен System.Reflection.

Например, с помощью метода GetTypes о можно получить список всех типов, находящихся в сборке:

using System;

namespace Reflection2 {

class Classl

{

[STAThread]

static void Main(string[] args) {

// Загрузить модуль

System.Reflection.Assembly assembly =

System.Reflection.Assembly.LoadFrom("Reflectionl.exe"); // Получить список всех классов модуля Туре[1 types = assembly.GetTypes(); // Отобразить список классов модуля

foreach (Type t in types) {

Console.WriteLine(t.FullName);

}

// Создание объекта по имени типа object Obj = Activator.Createlnstance(

"Reflectionl", "Reflectionl.TestClass");

}

}

}

Обратите внимание на последние две строки: метод Createlnstance о позволяет создать объект по имени типа!

Каждый управляемый тип в .NET реализует интерфейс iRefiect, с помощью которого возможен доступ к полям, свойствам, методам и другой информации:

// Получение списка свойств

System.Reflection.Propertylnfо[] Props =

listBoxl.GetType().GetProperties();

foreach(System.Reflection.Propertylnfо prop in Props) {

listBoxl.Items.Add(prop.PropertyType.ToString() + ":" + prop.Name);

}

// Получение скрытых полей структуры foreach (Fieldlnfo fi in

typeof(Account).GetFields(BindingFlags.Nonpublic I

BindingFlags.Instance))

f

Console.WriteLine(fi.Name);

}

Причем, с помощью метаинформации можно не только получать, но и устанавливать значения полей и свойств (см. листинг 3.1). И не только открытых

(public), НО И Скрытых (private И protected).

Информацию о конструкторе класса можно получить с помощью метода

GetConstructor():

Constructorlnfo ci =

typeof(LocationClass).GetConstructor(new Type[]{ typeof(Point) });

Как я уже говорил, метаинформация может быть расширена собственными данными с помощью атрибутов (attribute). Атрибут привязывается к объекту с помощью описания в квадратных скобках1:

[CategoryAttribute ("Gradient") ] public Color StartColor 1

get ( return StartColor; } set ( StartColor = value; }

}

По соглашению все имена атрибутов заканчиваются словом Attribute, но для простоты, язык С# позволяет сокращать запись и указывать "чистое" имя:

[Category ("Gradient") ] public Color StartColor

В круглых скобках указываются через запятую параметры атрибута, которые могут быть позиционными или именованными. Позиционные параметры указываются в определенной последовательности, а именованные описываются в виде равенства:

свойство = значение

Например,

[Dlllmport("kernel32.dll", SetLastError= true, CharSet=CharSet.Auto)]

Позиционные параметры указываются в конструкторе, а именованные описываются как свойства, доступные как для чтения, так и для записи. Свойства, доступные только для чтения, не могут быть именованными.

Сточки зрения кода, атрибуты — наследники класса System.Attribute:

public class MyAttribute : Attribute

1 }

Класс атрибута может иметь атрибут AttributeUsageAttribute, который позволяет задать область применения атрибута, возможность наследования и возможность указывать несколько атрибутов одновременно:

AttributeUsage(AttributeTargets ValidOn, bool Inherited, bool AllowMultiple)

Здесь:

?        свойство vaiidon имеет тип AttributeTargets и задает область применения атрибута. Например, AttributeTargets.Class, AttributeTargets.Method, AttributeTargets.Property и Т. д.;

?        свойство inherited указывает, будет ли атрибут наследоваться классами, наследниками того, к которому этот атрибут был применен. По умолчанию значение равно true;

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

Например,

[AttributeUsage(AttributeTargets.Class, AllowMultiple= true)]

public class NewAttribute : Attribute {

}

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

[AttributeUsage(AttributeTargets.All)]

public class TypedCollectionAttribute : Attribute {

private Type collectionType; private string fieldName;

public TypedCollectionAttribute(Type collectionType) {

this.collectionType = collectionType;

}

public Type CollectionType {

get { return collectionType; )

}

public string FieldName {

get { return fieldName; }

set { fieldName = value; }

}

}

В этом случае привязка атрибута может выглядеть так:

[TypedCollection(typeof(MyData}, FieldName="Data")]

public class MyCollection : ArrayList, IBindingList, ITypedList

{

}

Добавление новых атрибутов расширяет метаинформацию, позволяя иметь собственные данные в таблицах метаинформации. Разумеется, с помощью методов отражения можно получать список атрибутов и проверять наличие привязки атрибута к объекту.

Получить список атрибутов типа можно с помощью двух методов:

object[] GetCustomAttributes(bool inherit);

object[] GetCustomAttributes(Type attributeType, bool inherit);

Первый метод возвращает все атрибуты, а второй— только атрибуты указанного типа. Параметр inherit указывает, нужно ли просматривать атрибуты в классах-предках.

Проверить, что к типу привязан некий атрибут, можно с помощью метода:

bool IsDefined(Type attributeType, bool inherit);

Сигнатура ЭТОГО метода та же, ЧТО И у метода GetCustomAttributes () .

Кроме этого, можно воспользоваться одним из статических методов класса

Attribute.

Например, вот несколько способов перебора атрибутов:

foreach (Attribute a in this.GetType().GetCustomAttributes( typeof(TypedCollectionAttribute) , false))

{ }

// Получаем атрибут (если он определен один раз) DeveloperAttribute MyAttribute = (DeveloperAttribute)

Attribute.GetCustomAttribute(t, typeof (DeveloperAttribute)); // Если допускается несколько одинаковых атрибутов, то // нужно использовать другой способ

DeveloperAttribute!] MyAttribute = (DeveloperAttribute!])

Attribute.GetCustomAttributes(t, typeof (DeveloperAttribute));

Атрибуты — одна из важнейших составляющих кода времени разработки. С их помощью определяется большинство параметров свойств и классов. Более подробную информацию об атрибутах можно найти в MSDN или в [5].

. Листинг 3.1. Установка значения private- и protected-полей

using System;

using System.Reflection;

namespace Reflection3 {

class Classl {

public class Account {

private long privatevalue = 0; protected long protectedval = 0;

public long GetPrivatevalue() {

return privatevalue;

}

public long GetProtectedval() {

return protectedval;

}

}

[STAThread]

static void Main(string[] args) {

Account a = new Account();

Console.WriteLine("a,privatevalue={0}", a.GetPrivatevalue ()); Console.WriteLine("a.protectedval={0}", a.GetProtectedval());

Fieldlnfo fil = a.GetType().GetField("privatevalue",

BindingFlags.Nonpublic I BindingFlags.Instance);

if (fil != null) {

fi1.SetValue(a, 1);

}

Fieldlnfo fi2 = a.GetType().GetField("protectedval",

BindingFlags.Nonpublic | BindingFlags.Instance);

if (fi2 != null) {

fi2.SetValue(а, 2);

}

Console.WriteLine("a.privatevalue={0}", a.GetPrivatevalue()); Console.WriteLine("a.protectedval={0}", a.GetProtectedval ());

}

}

Литература:

Агуров П. В. C#. Разработка компонентов в MS Visual Studio 2005/2008. – СПб.: БХВ-Петербург, 2008. — 480 е.: ил.

По теме:

  • Комментарии