Жанр: Учеба
Си шарп: создание приложений для windows
...xj (где idx -
это индекс университета), конкретизируемся с выбором нужного трехмерного
массива. Указав индекс второго порядка arr[idx][idx2j, выбираем в обозначенном
трехмерном массиве двумерный массив со студентами всех курсов
данного факультета. Указав индекс третьего порядка, можно выбрать необходимый
вам курс факультета arr[idx][idx2][idx3]. Для выбора конкретного
студента используйте индекс четверого порядка arr[idx][idx2][idx3][idx4]. Это
на первый взгляд сложное описание является простой задачей.
Давайте рассмотрим пример использования многомерного массива.
Возьмем двумерный массив строк для хранения данных.
using System;
namespace Multidimensional
t
class MainApp
(
static void Main(string[] args)
{
string[,] arr;
const int firstldx = 2;
const int secondldx = 3;
//создаем массив
144 Раздел I I . Фундаментальные понятия
arr = new string[firstldx, secondldx];
// заполняем двумерный массив
for(int i = 0; i " firstldx; i++)
for (int j = 0; j " secondldx; j
arr[i,j] = "arr " + (i) + "-" + (j);
}
// выводим значения из массива
for(int i = 0; i " firstldx; i++)
for (int j = 0; j " secondldx; j++)
Console.WriteLine("Поле " + i + "-" + j)
Двумерный массив объявляется с использованием одной разделительной
запятой внутри квадратных скобок.
string[,] arr;
Объявляем две переменные, которые будут хранить размерность массива.
const int firstldx = 2;
const int secondldx - 3;
В данном случае размерность массива будет составлять 2*3. Далее
следует создание массива.
arr = new string[firstldx, secondldx];
Создание массива тесно связано с его объявлением. Нельзя объявить
двумерный массив, а при создании указать три параметра размерности.
Теперь массив создан, под него выделена память, и он может быть заполнен
данными. Следующий код заполняет массив строками, которые
хранят значения номеров строк и столбцов элементов.
for (int i =* 0; i " firstldx; i + + )
for (int j ^ 0; j " secondldx; j + + )
{
arr[i, j] = "arr " + (i) + "-" + (j);
}
Так же, как и заполнение массива, выполняется вывод его значений
на экран.
forfint i = 0; i " firstldx; i++)
{
for(int j = 0; j " secondldx; j++)
{
Console.WriteLine("Поле " + i + "-" + j) ;
Массивы 145
Результат работы программы будет следующий:
Поле 0-0
Поле 0-1
Поле 0-2
Поле 1-0
Поле 1-1
Поле 1-2
РАЗМЕР И РАНГ МАССИВА
Как уже говорилось, любой массив является объектом класса, производным
от System.Array. Как и любой другой класс, System.Array имеет свои
методы и свойства. Одним из важнейших методов класса System.Array является
метод Getl_enght(). Он позволяет получить размер определенного
измерения многомерного массива. Другое важное достоинство класса
System.Array - свойство Rank, которое позволяет программным путем определить
ранг массива, то есть количество измерений массива. Кроме того,
класс System.Array имеет свойство Length, которое возвращает общее количество
элементов в массиве. Давайте рассмотрим использование этих возможностей,
видоизменив предыдущий пример.
using System;
namespace Multidimensional
{
class MainApp
{
static void Main(string[] args]
{
string[,] arr;
const int firstldx = 2;
const int secondldx = 3;
//создаем массив
arr = new string[firstldx, secondldx];
// получаем значение ранга массива
int Rank = arr.Rank;
Console-WriteLine("Массив arr имеет ранг {0}", Rank);
// определяем общее количество элементов массива
int Length = arr.Length;
Console.WriteLine("Количество элементов в массиве: (0)", Length);
// заполняем двумерный массив
146 Раздел I I . Фундаментальные понятия
rorfint i - 0; i " arr .GetLength (0) ; i-i-t)
for(int j - 0; j " arr.GetLength(1]; j++)
(
arr[i,j] = "arr " -r (i) + "-" + (j);
// выводим значения из массива
foriint i =• 0; i " arr .GetLength (0) ; i + +)
for(int j - 0; j " arr .GetLength (1 ] ; j-r + )
{
Console.WriteLine("Поле " 4 i +• "-" + j
В этом примере вычисляется ранг массива и выводится его значение
на экран. При обходе элементов массива нет необходимости запоминать
размер каждого измерения массива. Размер соответствующего измерения
можно получить при помощи функции Getl_ength(). Результат работы программы:
Массив arr имеет ранг 2
Количество элементов в массиве: 6
Поле 0-0
Поле 0-1
Поле 0-2
Поле 1-0
Поле 1-1
Поле 1-2
НЕВЫРОВНЕННЫЕ МАССИВЫ
Многомерные массивы могут иметь как одинаковый размер всех вложенных
массивов, так и различный. Если используемый в программе
массив будет иметь различную длину вложенных массивов, то об этом
следует позаботиться заранее. Объявлять такой массив следует как:
int [ ] [] arr;
Такое объявление будет означать, что объявляется массив массивов
значений типа int. Приведу пример использования невыровненного массива.
Допустим, у вас есть массив для хранения фамилий мальчиков и
девочек в учебном классе. Для большей управляемости, вам лучше разделить
мальчиков и девочек на различные массивы. Объявив невыровненный
массив, вы можете создать родительский массив, что позволит с
легкостью обрабатывать всех учеников в классе.
using System;
namespace Multidimensional
Массивы 147
class Pupil
virtual public void Hello ()
Console.WriteLine("Базовый класс");
class Boy: Pupil
override public void Hello()
Console.WriteLine("Класс Boy");
}
)
class Girl: Pupil
override public void Hello ()
Console.WriteLine("Класс Girl");
}
class MainApp
static void Main(string[] args)
Pupil[][] pupils;
// создаем родительский массив
pupils = new Pupil[2][];
// создаем массив для мальчиков
pupils[0] = new Pupil[5];
// заполняем массив
forlint i = 0; i " pupils[0].Length; i++)
• pupils [0] [i] = new BoyO;
i
// создаем массив для девочек
pupils[lj = new Pupil[8];
// заполняем массив
for(int i = 0; i " pupils[1].Length; i++)
pupils[1][i] = new Girl();
for(int i = 0; i " pupils.Length; i++)
148 Раздел I I . Фундаментальные понятия
forfint j = 0; j " pupils [i] .Length,- j+ + )
{
Pupil pupil = pupils[i][jj;
Console.Write ""{0 "-"1}: ", i, j) ;
pupil.Hello();
J
Здесь я создал базовый класс Pupil. Он содержит всего одну виртуальную
функцию Hello(), которая выводит на экран сообщение. Классы Boy и
Girl являются производными от Pupil. Они перегружают функцию Hello и
выводят собственное сообщение на экран.
В функции Main создается невыровненный массив объектов Pupil. При
этом по нулевому индексу содержится массив для мальчиков, а по первому-
массив для девочек. В итоге мы создали массив на 5 элементов
для мальчиков и массив на 8 элементов для девочек. Каждый элемент
массива был проинициализирован своим типом объекта. В итоге у нас в
массиве оказалось 5 объектов типа Boy и 8 объектов типа Girl.
Для перебора всех элементов массива используются вложенные циклы.
Поскольку массив pupils является невыровненным, то его свойство Length
вернет количество элементов в первом измерении. Таким образом, цикл
forfint i - 0; 1 " pupils.Length; if+)
сделает 2 итерации. Вложенный цикл
forfint i = 0; j " pupils[i].Length; j++)
будет выполняться число раз, соответствующее количеству элементов
в массиве. Первый раз он выполнится 5 раз, а второй - 8.
Благодаря полиморфизму, через базовый класс можно работать с производными
классами. Что и делается с присвоением объекту pupil класса
Pupil различных элементов массива. Результат работы программы будет
следующий:
0-0: Класс Boy
0-1: Класс Boy
0-2: Класс Boy
0-3: Класс Boy
0-4: Класс Boy
1-0: Класс Girl
1-1: Класс Girl
1-2: Класс Girl
1-3: Класс Girl
1-4: Класс Girl
1-5: Класс Girl
1-6: Класс Girl
1-7: Класс Girl
Массивы 149
ОПЕРАТОР foreach
В Visual Basic уже давно встроен оператор для итерирования массивов
и коллекций. Поэтому разработчики С# сочли полезным наделить такой
возможностью и язык С#. Синтаксис оператора foreach следующий:
foreach (тип in выражение)
Рассмотрим следующий код программы:
using System;
using System.Collections;
namespace ForEachApp
{
class MyArray
f
public ArrayList peoples;
public MyArray()
{
peoples = new ArrayList();
peoples.Add("Иванов");
peoples.Add("Петров");
peoples.Add("Сидоров");
}
static void Main(string[] args)
!
MyArray arr = new MyArray();
for(int i = 0,- i " arr .peoples .Count; il+)
i
Console.WriteLine("{0)", arr.peoples[i]);
}
Для перебора всех элементов массива используется цикл for. Такой
способ наиболее прижился среди программистов на C++. Однако такой
способ имеет ряд проблем: необходимость инициализации переменной
цикла, проверки булева значения, наращивания переменной цикла, использования
переменной определенного типа для выделения элемента
массива. Использование оператора foreach позволяет избежать всех этих
неудобств. Вот как можно переписать предыдущий пример с помощью
оператора foreach.
foreach(string people in arr.peoples)
{
Console.WriteLine("(0}"- people);
150 Раздел I I . Фундаментальные понятия
Насколько проще и понятнее использование оператора foreach! Вам
гарантирован обход всех элементов массива. Вам не нужно вручную устанавливать
значение переменной для инициализации и проверять границы
массива. Кроме того, foreach сам поместит необходимое значение в
переменную указанного типа. Если вам нужно прервать цикл foreach, вы
можете воспользоваться операторами break и return.
СОРТИРОВКА
Одной из наиболее распространенных операций при работе с массивами
является сортировка. Существует множество способов сортировки, которые
отличаются быстродействием и количеством используемой памяти.
Я расскажу лишь о наиболее простом методе сортировки - сортировке
пузырьком.
Задача сортировки заключается в следующем: имеется список целых
чисел, необходимо упорядочить элементы в списке по возрастанию, то
есть расположить их так, чтобы самый первый элемент был самым маленьким,
второй больше первого, но меньше третьего и т.д.
При пузырьковой сортировке упорядоченный список получается из исходного
путем систематического обмена пары рядом стоящих элементов, не
отвечающих требуемому порядку; до тех пор, пока такие пары существуют.
Наиболее простой метод систематического обмена соседних элементов
с неправильным порядком при просмотре всего списка слева направо
определяет пузырьковую сортировку: максимальные элементы как бы
всплывают в конце списка.
Пример:
В (20, -5, 10, 8, 7}, исходный список
В1{-5, 10, 8, 7, 20}, первый просмотр
В2{-5, 8, 7, 10, 20}, второй просмотр
ВЗ{-5, 7, 8, 10, 20}, третий просмотр
При первом просмотре меняются местами 20 и -5, 20 и -10, 20 и 8, 20
и 7. При втором просмотре 10 и 8, 10 и 7. При третьем просмотре 8 и 7.
В итоге самое большое число оказывается с правой стороны, а самое
маленькое - с левой. Количество итераций п2, где п--количество элементов
в массиве. Ниже приведен пример программы, использующей
пузырьковую сортировку.
using System;
namespace Sorting
{
class TestApp
{
public static void Main()
{
int[] arr;
Массивы 151
arr = new intflQj;
Console.WriteLine("Исходный массив:
forlint i = 0; i " arr.Length; i++)
(
arr[i! - 10 - i;
Console.Write("(0} ", arr fi]);
bool bSort = false;
do
{
bSott ^ false;
forlint. i = 1; i " arr.Length;
if (arr [i] " arr[i-Ij)
{
int с = arr[i];
arrfi] - a r r [ i -1 ] ;
arrfi-1] = c;
bSort = true;
} while(bSort);
Console.WriteLine("Отсортированный массив:")
for(int i = 0; i " arr.Length; i++)
"
Console.Write{"{0} ", arr[i]);
Здесь сортировка выполняется до тех пор, пока хотя бы одна пара элементов
массива будет переставлена. О перестановке сигнализирует флаг
bSort, который устанавливается в false при начале обхода массива и изменяется
на true, если хотя бы одна пара элементов была переставлена.
13. ИНДЕКСАТОРЫ
В предыдущей главе подробно рассказывалось о том, как использовать
массивы, которые представляют собой очень удобный способ хранения
однотипной информации. В С# существует особая возможность, позволяющая
работать с объектами так, как если бы они были массивами,-
это механизм индексаторов.
ПРЕИМУЩЕСТВО ИСПОЛЬЗОВАНИЯ ИНДЕКСАТОРОВ
Вы можете спросить: "Зачем нужны индексаторы, в чем преимущество
их использования?" Ответ такой: "Процесс написания приложений
становится интуитивно более понятным". Индексаторы можно сравнить
со свойствами. Последние позволяют обращаться к полям класса без использования
методов аксессоров. При использовании свойств программисту
нет необходимости задумываться о формате метода-установщика или
метода-получателя. Программист выбирает простую инструкцию типа
объект.поле. То же можно сказать и об индексаторах. Они позволяют обращаться
к полям объекта так, как если бы это был массив.
Рассмотрим пример. Тем, кто хорошо знаком с программированием на
Win32API, известно, каким образом происходит работа с элементом управления
ListBox. Для того чтобы добавить в него строку, следует послать
окну соответствующее сообщение с необходимыми параметрами.
Этот метод имеет свои преимущества. Он поддерживается ядром Windows
и является самым быстрым способом работы со списком. Однако со временем
стали более широко использоваться такие языки программирования,
как C++, Object Pascal и др., которые применяли интуитивно более
понятные интерфейсы. Библиотека классов MFC использовала тонкую
надстройку в виде классов, которые лишь отсылали соответствующие
сообщения при работе с окнами. И программисты выбирали удобство
использования программного кода, даже если приходилось жертвовать
размером программы или ее быстродействием.
Все это было принято во внимание разработчиками языка С#. В стремлении
разработать максимально интуитивно понятный язык программирования
они задумались над вопросом возможности обработки объекта,
который по своей сути является массивом, как массива. Ведь обычное окно
со списком является тем же массивом с дополнительной возможностью
отображения данных. В связи с этим и родилась концепция индексаторов.
Пример использования индексаторов можно найти и в языке C++. Там
это называлось перегрузкой оператора ( [ ] ).
Индексаторы 153
ОПРЕДЕЛЕНИЕ ИНДЕКСАТОРОВ
Как уже было сказано выше, индексаторы очень похожи на свойства.
Формально синтаксис определения индексатора таков:
[атрибуты; [модификаторы! тип this [список-формальных-параметров){sec get)
Или, если это индексатор в интерфейсе, таков:
[атрибуты] [модификаторы] тип интерфейс, this [список формальных
параметров]{set get}
Где:
атрибуты - дополнительная информация об индексаторе. Наиболее
значимым для индексаторов является атрибут Name. Задание Name позволяет
дать имя индексатору для того, чтобы его могли использовать другие
языки, не поддерживающие индексаторы. По умолчанию все индексаторы
вашего класса имеют Name, равный Item;
модификаторы - модификаторы доступа и директивы. К индексатору
применимы почти все стандартные директивы С#. Он может быть скрыт,
перегружен, сделан виртуальным, но есть одно исключение, индексатор
не может быть static;
список формальных параметров - указывает параметры, посредством
которых осуществляется индексация. Передается в get и set, которые
используются в индексаторе так же, как в свойствах, get применяется для
вычисления индексатора по заданному списку формальных параметров,
a set - для изменения индексатора, set получает в качестве дополнительного
параметра value того же типа, что и индексатор.
Следует отметить, что доступ к индексатору осуществляется посредством
сигнатуры, в отличие от свойства, доступ к которому осуществляется
посредством имени. Сигнатурой индексатора считаются число и тип
формальных параметров. Тип самого индексатора и имена параметров в
сигнатуру не входят. Естественно, в классе не может быть двух индексаторов
с одинаковой сигнатурой. К тому же, индексатор не считается переменной
и не может быть передан в качестве ref или out параметра.
Вот пример простейшего класса, использующего индексатор.
class MyArray
(
public object this fint idx]
(
get
// установка нужных значений
// возврат нужных значений
Здесь не приводится реализация индексатора. Здесь лишь описываются
общие правила синтаксиса, используемого при создании индексаторов.
По этому коду вы уже можете определить, что индексатор сродни
вызову функции с переданным ей параметром и возвращающей значение.
Однако индексаторы позволяют использовать в программе примеры
кода подобно следующему:
МуЛггау obj = new MyArrayO;
obj[0] = someObj;
Console.WriteLine{"{0}", obj[0]);
Клиентская часть программы не заботится о том, что возвращает индексатор.
Все, что делается внутри индексатора, не имеет значения для
клиентского кода. Клиент лишь ожидает от индексатора такого значения,
как если бы он обращался к элементу массива.
Давайте рассмотрим пример использования индексатора:
using System;
using System.Collections;
namespace Indexers
{
class ListBox
{
protected ArrayList data - new ArrayListO;
public object this [int idx]
{
get
{
// установка нужных значений
if(idx " -1 &s idx " data.Count)
{
return data[idx];
}
else
{
// запрещенная ситуация
// возникновение исключения
return null;
set
Посмотри в окно!
Чтобы сохранить великий дар природы — зрение,
врачи рекомендуют читать непрерывно не более 45–50 минут,
а потом делать перерыв для ослабления мышц глаза.
В перерывах между чтением полезны
гимнастические упражнения: переключение зрения с ближней точки на более дальнюю.
1
// установка нужных значений
iffidx " -1 && idx " data.Count)
{
data[idx] = value;
Индексаторы 155
else iffidx == data.Count)
{
data.Add(value);
}
else
{
// запрещенная ситуация
// возникновение исключения
class ListBoxApp
{
static void Main(string[] args)
{
L i s t B o x l i s t - new L i s t B o x ( ) ;
l i s " [ O j =- " к р а с н ы й " ;
l i s ~ [ l ] = " з е л е н ы й " ;
l i s t : [ 2 ] = " с и н и й " ;
C o n s o l e . W r i t e L i n e ( " ( O ) {1} ( 2 } ' 1 , l i s t [ 0 ] , l i s t [ l ] , l i s t [ 2 ] "
Созданный нами класс ListBox имеет индексатор. Сам класс предназначен
для хранения списка объектов. Класс ArrayList библиотеки классов .NET
Framework предназначен для хранения совокупности объектов. Объект data
типа ArrayList является членом класса ListBox. В моей реализации класса
ListBox выполняется лишь проверка на выход индекса за границы допустимых
значений и генерация исключения в случае необходимости. Вы можете
расширить реализацию индексатора любыми дополнительными функциями.
Например, добавить функцию отображения добавленного в список
элемента на экране. От этого не изменится код клиентской части программы.
Вам лишь необходимо будет изменить внутреннюю реализацию индексатора.
14. АТРИБУТЫ
Большинство языков программирования разрабатываются с учетом
набора необходимых возможностей. Так, в начале создания компилятора
вы думаете, какова будет структура приложений на новом языке, как
один фрагмент кода будет вызывать другой, как распределить функциональность,
и о многих других проблемах, решение которых сделает язык
продуктивным средством создания ПО. Обычно разработчикам компиляторов
приходится иметь дело со статическими сущностями. Например,
вы определяете класс на С#, помещая перед его именем ключевое слово
class. После этого вы определяете производный класс, вставляя двоеточие
после его имени, за которым следует название базового класса. Это может
служить примером решения, которое принимается разработчиком
языка однажды и навсегда.
Сейчас те, кто пишет компиляторы, семь раз отмерят, прежде чем
отрежут. Но даже они не могут предвидеть все будущие усовершенствования
в нашей области и то, как они повлияют на способы выражения
программистами типов на данном языке. Скажем, как создать связь между
классом на C++ и URL документации для данного класса? Или как вы
будете ассоциировать члены классов C++ с полями XML в новом решении
вашей компании в области электронной коммерции? Поскольку C++
разрабатывался задолго до прихода в нашу жизнь Интернета и протоколов,
таких как XML, обе эти задачи выполнить довольно трудно.
До сих пор решения подобных проблем предполагают хранение дополнительной
информации в отдельном файле (DEF, IDL и т. д.), которая затем
связывается с тем или иным типом или членом. Так как компилятор не
обладает сведениями о каком-то файле или связи между вашим классом и
этим файлом, такой подход обычно называется разрывным решением
(disconnected solution). Главная проблема в том, что класс больше не является
"самоописывающимся", т. е. теперь пользователь не может сказать о
классе все, лишь взглянув на его определение. Одно из преимуществ самоописывающегося
компонента - гарантия соблюдения при компиляции и в
период выполнения правил, ассоциированных с компонентом. Кроме того,
сопровождение самоописывающегося компонента проще, поскольку разработчик
может найти всю связанную с ним информацию в одном месте.
Так все и было многие десятилетня. Разработчики языка пытаются
определить, что вы хотите от языка, создают компилятор с этими возможностями,
и, к счастью или к сожалению, вы остаетесь с этими возможностями
до прихода следующего компилятора. Так это и было вплоть
до сегодняшнего дня. С# предлагает иную парадигму, берущую начало
от атрибутов (attributes).
Атрибуты 157
НАЗНАЧЕНИЕ АТРИБУТОВ
Атрибуты предоставляют универсальные средства связи данных (в виде
аннотаций) с типами, определенными на С#. Вы можете применять их
для определения информации периода разработки (например, документации),
периода выполнения (например, имя столбца БД) или даже характеристик
поведения периода выполнения (например, может ли данный
член участвовать в транзакции). Возможности атрибутов бесконечны.
Лучше объяснить использование атрибутов на примере. Допустим, у
вас есть приложение, хранящее некоторые данные в реестре. Одна из
проблем разработки связана с выбором места хранения информации о
разделе реестра. В большинстве сред разработки она, как правило, хранится
в файле ресурсов, в константах или даже жестко запрограммирована
в вызовах API реестра. Однако мы снова имеем ситуацию, когда
неотъемлемая часть класса хранится отдельно от определения остальной
части класса. Атрибуты позволяют "прикреплять" эту информацию к
членам класса, получая полностью самоописывающийся компонент. Вот
пример, иллюстрирующий, как это может выглядеть, если предположить,
что атрибут RegistryKey уже определен:
class MyClass
(
[RegistryKey (HKEYCURRENTUSER, "foo")]
public int Foo;
)
Чтобы прикрепить определенный атрибут к типу или члену С#, нужно
просто задать данные атрибута в скобках перед целевым типом или
членом. В нашем примере мы прикрепили атрибут RegistryKey к полю
MyClass.Foo. Как вы вскоре увидите, все, что нам надо сделать в период
выполнения,-это запросить значение поля, связанное с разделом реестра,
и использовать его, чтобы сохранить данные в реестре.
ОПРЕДЕЛЕНИЕ АТРИБУТОВ
В предыдущем примере синтаксис прикрепления атрибута к типу или
члену похож на тот, что применяется при создании экземпляра класса.
Дело в том, что атрибут на самом деле является классом, производным
от базового класса System.Attribute.
А теперь немного расширим атрибут RegistryKey:
public enum RegistryHives
HKEY_CLAS SES_ROOT,
HKEY_CURRENT_USER,
HKEY_LOCAL_MACHINE,
HKE YCURRENT С ON FIG
158 Раздел I I . Фундаментальные понятия
public class RegistryKeyAttribute : Attribute
{
public RegistryKeyAttribute ( RegistryHives Hive, String ValueName)
{
this.Hive = Hive;
this.ValueName = ValueName;
)
protected RegistryHives hive;
public RegistryHives Hive get
return
sen
hive =
hive;
value;
protected String valueName;
public String ValueName
{
get
return valueName e;
set
{
valueName *- value;
В этом примере были добавлены enum для различных типов разделов
реестра, конструктор для класса-атрибута (который принимает тип и имя
раздела реестра), а также два свойства для имени улья реестра и имени
значения. Теперь мы видим более широкие возможности атрибутов, так
что посмотрим, как запросить атрибут в период выполнения. Мы будем
работать с полностью функциональным примером. Перейдем к рассмотрению
некоторых более сложных вопросов, связанных с определением и
прикреплением атрибутов.
В приведенных примерах к именам классов атрибутов добавлено
слово Attribute. Однако, прикрепляя атрибут к типу или члену, можно
отбросить этот суффикс. Это одна из возможностей сокращения, изысканная
разработчиками С#, и дается она нам даром. Обнаружив атрибут,
прикрепленный к типу или члену, компилятор будет искать класс
с именем заданного атрибута, производный от System.Attribute. Если
класс найти не удастся, компилятор добавит к имени заданного атрибута
слово Attribute и будет продолжать поиск получившегося имени.
Поэтому в повседневной практике имена классов атрибутов при определении
оканчиваются словом Attribute. Впоследствии эта часть имени
опускается.
Атрибуты 159
ЗАПРОС ИНФОРМАЦИИ ОБ АТРИБУТАХ
Мы знаем, как определять атрибуты в виде производных от System.
Attribute и как прикреплять их к типу или члену. А что теперь? Как
использовать атрибуты при программировании? Короче, как производится
запрос типа или члена (как прикрепленных к ним атрибутов, так и их
параметров)?
Чтобы запросить тип или член о прикрепленных к ним атрибутах,
нужно применить отражение (reflection).
Отражение - это набор функций, позволяющих в период выполнения
динамически определять характеристики типов в приложении. Например,
с помощью Reflection API из состава .NET Framework можно циклически
запрашивать метаданные всего приложения и создавать списки
определенных для него классов, типов и методов. Рассмотрим несколько
примеров атрибутов и способов их запроса с помощью отражения.
Атрибуты класса
Способ получения атрибута зависит от типа члена, к которому производится
запрос. Допустим, вам нужно узнать атрибут, определяющий
удаленный сервер, на котором должен быть создан объект. Без атрибутов
вам бы пришлось сохранять эту информацию в константе или файле
ресурсов приложения. Используя же их, можно просто создать аннотацию
для класса с именем удаленного сервера, например, так:
using System;
public enum RemoteServers
{
DC,
BIND,
COOKER
j
public class RemoteObjectAttribute: Attribute
{
public RemoteObjectAttribute(RemoteServers Server)
i
this.server = Server;
}
protected RemoteServers server;
public string Server
{
get
(
return RemoteServers .GetName (typeof (RemoteSeirvers) , this . server! ;
160 Раздел I I . Фундаментальные понятия
[RemoteOb}ect(RemoteServers.COOKER)
class MyRemotableClass
Сервер, на котором необходимо создавать объект, можно определить так:
class Cla
...Закладка в соц.сетях