ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 11.12.2025

Просмотров: 3252

Скачиваний: 1

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.

Обращение к элементам массива очень простое и понятное: команда c o n t a i n e r [п] обеспечивает обращение к и-му элементу массива c o n t a i n e r . Было бы хорошо, если бы так же просто можно было обращаться и к другим типам коллекций.

С# позволяет написать вам свою собственную реализацию операции индексирова­ ния. Вы можете обеспечить возможностью обращения через индекс коллекции, кото­ рые таким свойством изначально не обладают. Кроме того, вы можете индексировать с использованием в качестве индексов не только типа i n t , но и других типов, напри­ мер, s t r i n g .

Формат индексатора

Индексатор выглядит очень похоже на свойство, за тем исключением, что в нем вме­ сто имени свойства появляются ключевое слово t h i s и оператор индекса [ ] :

c l a s s MyArray

{

p u b l i c s t r i n g t h i s [ i n t i n d e x ] / / О б р а т и т е в н и м а н и е н а

{

/ / к л ю ч е в о е с л о в о " t h i s "

g e t

 

{

 

r e t u r n a r r a y [ i n d e x ] ;

 

}

 

s e t

 

{

 

a r r a y [ i n d e x ] = v a l u e ;

 

}

 

За сценой выражение s = m y A r r a y [ i ] ;

вызывает функцию доступа g e t , переда­

вая ей значение индекса i. Выражение myAr r a y [ i ] = " с т р о к а " ; приводит к вызову функции доступа s e t , которой передаются индекс i и строка " с т р о к а " .

Пример программы с использованием индексатора

Индексы не ограничены типом i n t . Например, вы можете использовать для индек­ сирования коллекции домов имена их владельцев или адреса. Кроме того, свойство ин­ дексатора может быть перегружено для различных типов индекса.

Приведенная далее демонстрационная программа I n d e x e r генерирует класс виртуального массива K e y e d A r r a y , который выглядит и функциони­ рует точно так же, как и обычный массив, с тем исключением, что в качестве его индексов применяется значение типа s t r i n g .

/ / I n d e x e r - д а н н а я д е м о н с т р а ц и о н н а я п р о г р а м м а и л л ю с т р и р у е т / / и с п о л ь з о в а н и е о п е р а т о р а и н д е к с а д л я о б е с п е ч е н и я д о с т у п а к / / м а с с и в у с и с п о л ь з о в а н и е м с т р о к в к а ч е с т в е и н д е к с о в

Глава 20. Работа с коллекциями

465


u s i n g

S y s t e m ;

 

 

n a m e s p a c e

I n d e x e r

 

{

 

 

 

 

p u b l i c

c l a s s

K e y e d A r r a y

/ / С л е д у ю щ а я с т р о к а о б е с п е ч и в а е т " к л ю ч " к м а с с и в у — э т о

/ / с т р о к а ,

к о т о р а я и д е н т и ф и ц и р у е т э л е м е н т

p r i v a t e s t r i n g [ ]

s K e y s ;

/ / o b j e c t п р е д с т а в л я е т с о б о й р е а л ь н ы е д а н н ы е , с в я з а н н ы е

//с к л ю ч о м

p r i v a t e

o b j e c t [ ]

o A r r a y E l e m e n t s ;

 

I I K e y e d A r r a y - с о з д а н и е K e y e d A r r a y ф и к с и р о в а н н о г о

/ / р а з м е р а

 

 

 

p u b l i c

K e y e d A r r a y ( i n t n S i z e )

 

s K e y s = n e w s t r i n g [ n S i z e ] ;

 

o A r r a y E l e m e n t s

= n e w o b j e c t [ n S i z e ] ;

/ / F i n d - п о и с к и н д е к с а з а п и с и ,

с о о т в е т с т в у ю щ е й с т р о к е

/ / s T a r g e t K e y

( е с л и з а п и с ь н е н а й д е н а , в о з в р а щ а е т - 1 )

p r i v a t e

i n t

F i n d ( s t r i n g s T a r g e t K e y )

f o r ( i n t i = 0,-

i < s K e y s . L e n g t h ; i + + )

i f

( S t r i n g . C o m p a r e ( s K e y s [ i ] ,

s T a r g e t K e y ) = = 0 )

r e t u r n

i,-

 

 

}

}

r e t u r n - 1 ;

}

/ / F i n d E m p t y - п о и с к с в о б о д н о г о м е с т а в м а с с и в е д л я / / н о в о й з а п и с и

p r i v a t e

i n t F i n d E m p t y ( )

f o r ( i n t i = 0 ;

i < s K e y s . L e n g t h ; i + + )

i f

( s K e y s [ i ]

= = n u l l )

{

 

 

return i;

 

}

 

 

}

 

 

t h r o w n e w E x c e p t i o n ( " М а с с и в з а п о л н е н " ) ;

/ / Ищем

с о д е р ж и м о е п о у к а з а н н о й с т р о к е - э т о и е с т ь

/ / и н д е к с а т о р public object this[string sKey]

{

s e t

{

466

Часть VII. Дополнительные главы


/ / П р о в е р я е м , н е т л и т у ж е т а к о й с т р о к и i n t i n d e x = F i n d ( s K e y ) ;

i f ( i n d e x < 0 )

{

/ / Е с л и н е т - и щ е м н о в о е м е с т о i n d e x = F i n d E m p t y ( ) ;

s K e y s [ i n d e x ] = s K e y ;

}

/ / С о х р а н я е м о б ъ е к т o A r r a y E l e m e n t s [ i n d e x ]

в с о о т в е т с т в у ю щ е й п о з и ц и и

=v a l u e ;

}

g e t

{

i n t i n d e x i f ( i n d e x

=F i n d ( s K e y ) ;

<0 )

{

 

r e t u r n

n u l l ;

 

}

 

 

 

 

r e t u r n

o A r r a y E l e m e n t s [ i n d e x ] ;

 

}

 

 

 

 

}

 

 

 

 

}

 

 

 

 

p u b l i c

c l a s s

P r o g r a m

 

{

 

 

 

 

p u b l i c

s t a t i c

v o i d M a i n ( s t r i n g [ ]

a r g s )

{

/ /

С о з д а е м м а с с и в с д о с т а т о ч н ы м к о л и ч е с т в о м э л е м е н т о в

K e y e d A r r a y m a = n e w K e y e d A r r a y ( 1 0 0 ) ;

/ /

С о х р а н я е м в о з р а с т ч л е н о в с е м ь и С и м п с о н о в

ma [ " B a r t " ] = 8;

 

т а [ " L i s a " ]

= 1 0 ;

 

т а [ " M a g g i e " ]

= 2 ;

 

/ / Ищем в о з р а с т L i s a

 

C o n s o l e . W r i t e L i n e ( " И щ е м в о з р а с т

L i s a " ) ;

i n t a g e = ( i n t ) m a [ " L i s a " ] ;

 

C o n s o l e . W r i t e L i n e ( " В о з р а с т L i s a -

{ o } " , a g e ) ;

/ / О ж и д а е м п о д т в е р ж д е н и я п о л ь з о в а т е л я

C o n s o l e . W r i t e L i n e ( " Н а ж м и т е < E n t e r > д л я " +

" з а в е р ш е н и я п р о г р а м м ы . . . " ) ;

C o n s o l e . R e a d ( ) ;

Класс K e y e d A r r a y включает два обычных массива. Массив o A r r a y E l e m e n t s со­ держит реальные данные K e y e d A r r a y . Строки, которые хранятся в массиве s K e y s , ра­ ботают в качестве идентификаторов массива объектов, г'-ый элемент s K e y s соответству­ ет /-ой записи o A r r a y E l e m e n t s . Это позволяет прикладной программе индексировать K e y e d A r r a y с п о м о щ ь ю индексов типа s t r i n g .

Глава 20. Работа с коллекциями

467


Индексы, не являющиеся целыми числами, известны как ключи (key). Кстати, можно реализовать K e y e d A r r a y с использованием L i s t < T > (см. гла ву 15, "Обобщенное программирование") вместо массива фиксированного размера. L i s t < T > индексируется, как и массив, так как они оба реализуют интерфейс I L i s t (или I L i s t < T > ) . Это позволит сделать KeyedArray обобщенным классом и получить большую гибкость, чем при использова­ нии внутреннего массива.

Индексатор s e t [ s t r i n g ] начинает с проверки, не имеется ли уже данного индекса

в массиве, для чего он

применяет функцию F i n d ( ) . Если

она возвращает индекс,

s e t [] сохраняет новый

объект данных в соответствующем

элементе oArrayEle ­

m e n t s . Ели F i n d () не может найти ключ, s e t [] вызывает F i n d E m p t y () для возвра­ та пустого элемента, где и будет сохранен переданный объект.

Функция g e t [] работает с индексом с применением аналогичной логики. Сначала она ищет определенный ключ с использованием метода F i n d О . Если F i n d () возвращает не­ отрицательный индекс, g e t [] возвращает соответствующий член o A r r a y E l e m e n t s , где хранятся запрошенные данные. Если же F i n d О возвращает -1, то метод g e t [] возвра­ щает значение n u l l , указывающее, что переданный ключ в списке отсутствует.

Метод F i n d () циклически проходит по всем элементам массива s K e y s в поисках элемента с тем же значением, что и переданное значение типа s t r i n g . Метод F i n d ( ) возвращает индекс найденного элемента (или значение -1, если элемент не найден). Функция F i n d E m p t y () возвращает индекс первого элемента, который не имеет связан­ ного ключевого элемента.

При написании методов F i n d ( ) и F i n d E m p t y () не ставилась цель повысить их эффективность, так что имеется множество возможностей сделать их быстрее, но все они не имеют никакого отношения к индексаторам.

Правда, было бы здорово добавить возможность индексирования к классу связанного списка L i n k e d L i s t ? Да, это можно сделать. Но вспомните, что даже в классе KeyedAr­ r a y требуется проход по массиву s K e y s для поиска определенного ключа, а значит, и функций F i n d () и F i n d E m p t y ( ) , которые этим занимаются. Точно так же при реа­ лизации индексатора для L i n k e d L i s t вам придется осуществлять проход по всему свя­ занному списку, и единственный способ сделать это — пройти по всему списку с исполь­ зованием итератора L i n k e d L i s t l t e r a t o r , следуя по ссылкам f o r w a r d от узла к уз­ лу. Индексатор окажется удобным, но очень медлительным.

Заметьте, что вы не можете удалять элементы посредством ключа n u l l . Как же реа­ лизовать удаление? Как часто говорится в учебниках — "данная задача остается читате­ лю в качестве домашнего упражнения".

Функция M a i n () демонстрирует применение индексатора. Сначала программа соз­ дает объект та типа K e y e d A r r a y длины 100 (т.е. со 100 свободными элементами). Да­ лее в этом объекте сохраняется возраст детей семьи Симпсонов с использованием имен в качестве индексов. И наконец, программа получает возраст Лизы с применением вы­ ражения ma [" L i s a " ] и выводит его на экран.

Обратите внимание, что программа должна выполнить преобразование типа для зна­ чения, возвращенного из та [ ] , так как K e y e d A r r a y написан таким образом, что может хранить объекты любого типа. Без такого преобразования типов можно обойтись, если индексатор написан так, что может работать только со значениями типа i n t , или если K e y e d A r r a y — обобщенный класс (см. главу 15, "Обобщенное программирование").

468

Часть VII. Дополнительные главы


Вывод программы прост и элегантен:

 

 

 

 

 

Ищем в о з р а с т

L i s a

 

 

 

 

 

 

 

Возраст

L i s a

-

10

 

 

 

 

 

 

 

Нажмите

< E n t e r >

д л я

з а в е р ш е н и я п р о г р а м м ы . . .

 

 

 

В

качестве

о т с т у п л е н и я —

интерфейс

I L i s t описывает

класс,

предостав­

л я ю щ и й целый

индексатор

в

ф о р м е

o b j e c t t h i s [ i n t ] . В

С #

имеется

также интерфейс

I L i s t < T > ,

который

позволяет заменить

o b j e c t

выбран­

н ы м вами т и п о м Т . Это устраняет необходимость преобразования типов из предыдущего примера .

Помните код функции M a i n ( ) , написанный при демонстрации самостоятельно раз­ работанного класса L i n k e d L i s t ? (Его можно найти в демонстрационной программе L i n k e d L i s t C o n t a i n e r на прилагаемом компакт-диске.) Вот его фрагмент:

p u b l i c s t a t i c v o i d M a i n ( s t r i n g [ ] a r g s )

{

/ / С о з д а е м к о н т е й н е р и д о б а в л я е м в н е г о т р и э л е м е н т а L i n k e d L i s t 1 1 с = n e w L i n k e d L i s t ( ) ;

/ / Д о б а в л я е м о б ъ е к т ы . . .

C o n s o l e . W r i t e L i n e ( " П р о х о д п о к о н т е й н е р у в р у ч н у ю " ) ; L i n k e d L i s t l t e r a t o r H i

=( L i n k e d L i s t l t e r a t o r ) 1 1 с . G e t E n u m e r a t o r ( ) ;

H i . R e s e t ( ) ;

w h i l e ( H i . M o v e N e x t ( ) )

{

s t r i n g s = ( s t r i n g ) H i . C u r r e n t ; C o n s o l e . W r i t e L i n e ( s ) ;

}

Данный код получает L i n k e d L i s t l t e r a t o r и использует его метод M o v e N e x t () и свойство C u r r e n t для обхода связанного списка. Однако С# 2.0 может упростить вам этот обход так, что вы получите перечисленные преимущества.

Вам не придется вызывать G e t E n u m e r a t o r () (и выполнять преобразование ти­ па результатов).

Вам не понадобится вызывать M o v e N e x t ( ) .

Вам не придется вызывать C u r r e n t и выполнять преобразование типа, возвра­ щаемого значения.

Вы сможете просто использовать f o r e a c h для обхода коллекции (С# сделает все остальное за вас).

Если быть честным, то f o r e a c h работает и для класса L i n k e d L i s t из этой гла­ вы. Это связано с наличием метода G e t E n u m e r a t o r ( ) . Но я все еще должен са­ мостоятельно писать класс L i n k e d L i s t l t e r a t o r . Новизна состоит в том, что вы можете пропустить при обходе часть вашего класса.

Глава 20. Работа с коллекциями

469