fgdhgdfj

Page 1

Синхронизация потоков М н о г о п о т о ч н ы й р е ж и м р а б о т ы о т к р ы в а е т новые в о з м о ж н о с т и для п р о г р а м м и с т о в , од­ нако за эти в о з м о ж н о с т и п р и х о д и т с я р а с п л а ч и в а т ь с я у с л о ж н е н и е м п р о ц е с с а п р о е к т и ­ р о в а н и я п р и л о ж е н и я и о т л а д к и . О с н о в н а я т р у д н о с т ь , с которой с т а л к и в а ю т с я про­ г р а м м и с т ы , р а н е е н и к о г д а не с о з д а в а в ш и е м н о г о п о т о ч н ы е п р о г р а м м ы , это синхронизация одновременно работающих потоков. Для чего и к о г д а она н у ж н а ? О д н о п о т о ч н а я п р о г р а м м а , т а к а я , н а п р и м е р , как п р о г р а м м а M S - D O S , при запуске п о л у ч а е т в м о н о п о л ь н о е р а с п о р я ж е н и е все р е с у р с ы к о м п ь ю т е р а . Так как в о д н о п о т о ч ной ОС с у щ е с т в у е т т о л ь к о один п р о ц е с с , он и с п о л ь з у е т эти р е с у р с ы в той п о с л е д о в а ­ т е л ь н о с т и , к о т о р а я с о о т в е т с т в у е т л о г и к е р а б о т ы п р о г р а м м ы . П р о ц е с с ы и з а д а ч и , рабо¬ тающие одновременно в многопоточной системе, обращаются одновременно к одним и тем же р е с у р с а м . Если при р а з р а б о т к е п р о г р а м м не у ч и т ы в а т ь это о б с т о я т е л ь с т в о , программы будут работать с ошибками. П о я с н и м это на п р о с т о м Пусть мы с о з д а е м п р о г р а м м у , в ы п о л н я ю щ у ю о п е р а ц и и с б а н к о в с к и м с ч е т о м . Опе¬ рация снятия н е к о т о р о й с у м м ы д е н е г со счета м о ж е т п р о и с х о д и т ь в такой последова¬ тельности: •

на п е р в о м ш а г е п р о в е р я е т с я о б щ а я с у м м а д е н е г , к о т о р а я х р а н и т с я на счету;

если эта с у м м а р а в н а или п р е в ы ш а е т размер с н и м а е м о й с у м м ы д е н е г , о б щ а я сумма уменьшается на необходимую величину;

з н а ч е н и е о с т а т к а з а п и с ы в а е т с я на т е к у щ и й счет.

Если о п е р а ц и я у м е н ь ш е н и я т е к у щ е г о счета в ы п о л н я е т с я в о д н о п о т о ч н о й то н и к а к и х п р о б л е м не в о з н и к н е т . О д н а к о п р е д с т а в и м себе, что два п р о ц е с с а тока) п ы т а ю т с я о д н о в р е м е н н о в ы п о л н и т ь т о л ь к о что о п и с а н н у ю о п е р а ц и ю и тем же с ч е т о м . П у с т ь при этом на счету н а х о д и т с я 5 млн. д о л л а р о в , а оба п ы т а ю т с я снять с него по 3 млн. д о л л а р о в . Допустим, события разворачиваются следующим образом:

системе, (или по¬ с одним процесса

п е р в ы й п р о ц е с с п р о в е р я е т с о с т о я н и е т е к у щ е г о счета и у б е ж д а е т с я , что на нем хра¬ нится 5

второй процесс проверяет состояние что на нем х р а н и т с я 5 млн. д о л л а р о в ;

п е р в ы й п р о ц е с с у м е н ь ш а е т счет на 3 млн. д о л л а р о в и з а п и с ы в а е т о с т а т о к (2 млн. д о л л а р о в ) на счет;

второй п р о ц е с с в ы п о л н я е т ту же с а м у ю о п е р а ц и ю , так как п о с л е п р о в е р к и с ч и т а е т , что на счету п о - п р е ж н е м у х р а н и т с я 5 млн. д о л л а р о в .

текущего

счета

и

также

убеждается,

В р е з у л ь т а т е п о л у ч и л о с ь , что со счета, на к о т о р о м н а х о д и л о с ь 5 м л н . д о л л а р о в , было снято 6 м л н . д о л л а р о в и при этом там осталось еще 2 млн. д о л л а р о в ! И т о г о банку нанесен у щ е р б в 3 млн. д о л л а р о в . Как же с о с т а в и т ь п р о г р а м м у у м е н ь ш е н и я счета, чтобы она не п о з в о л я л а в ы т в о р я т ь подобное?

340

А. В. Фролов, Г В.

Язык СМ. Самоучитель


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

процесс б л о к и р у е т счет для в ы п о л н е н и я о п е р а ц и й д р у г и м и его в м о н о п о л ь н о е в л а д е н и е ;

процессами, получая

п р о ц е с с п р о в о д и т п р о ц е д у р у у м е н ь ш е н и я счета и з а п и с ы в а е т на т е к у щ и й счет н о ­ вое з н а ч е н и е остатка;

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

К о г д а п е р в ы й п р о ц е с с б л о к и р у е т счет, о н с т а н о в и т с я н е д о с т у п е н д р у г и м п р о ц е с ­ сам. Если в т о р о й процесс т а к ж е п о п ы т а е т с я з а б л о к и р о в а т ь этот же счет, он будет пе¬ реведен в с о с т о я н и е о ж и д а н и я . К о г д а п е р в ы й п р о ц е с с у м е н ь ш и т счет и на нем оста¬ нется 2 м л н . д о л л а р о в , второй п р о ц е с с будет р а з б л о к и р о в а н . Он п р о в е р и т о с т а т о к , у б е д и т с я , что сумма н е д о с т а т о ч н а и не будет п р о в о д и т ь о п е р а ц и ю . Таким о б р а з о м , в м н о г о п о т о ч н о й среде н е о б х о д и м а с и н х р о н и з а ц и я п р о ц е с с о в и п о т о к о в при о б р а щ е н и и к к р и т и ч е с к и м р е с у р с а м . Если н а д такими р е с у р с а м и будут в ы п о л н я т ь с я о п е р а ц и и в н е п р а в и л ь н о й п о с л е д о в а т е л ь н о с т и , это п р и в е д е т к возникно¬ вению трудно обнаруживаемых ошибок. В я з ы к е п р о г р а м м и р о в а н и я С# п р е д у с м о т р е н о н е с к о л ь к о средств с и н х р о н и з а ц и и п о т о к о в , к о т о р ы е мы р а с с м о т р и м в этом р а з д е л е .

Ожидание завершения потока С п о м о щ ь ю метода J o i n , о п р е д е л е н н о г о в классе T h r e a d , п о т о к м о ж е т о ж и д а т ь за¬ в е р ш е н и я р а б о т ы д р у г о г о п о т о к а , для к о т о р о й этот м е т о д в ы з в а н . Предусмотрено 3 перегруженных определения метода public public public

void Join(int); bool

П е р в ы й из них в ы п о л н я е т о ж и д а н и е о г р а н и ч е н и я во в р е м е н и . Для в т о р о г о о ж и д а н и е будет п р е р в а н о п р и н у д и т е л ь н о ч е р е з интервал в р е м е н и , з а д а н н ы й в милли¬ с е к у н д а х . Третий м е т о д п о з в о л я е т у к а з ы в а т ь более д л и т е л ь н ы е и н т е р в а л ы в р е м е н и . М ы р а с с к а з ы в а л и о б этом р а н е е при о п и с а н и и метода S l e e p . У ч т и т е , что р е а л ь н о вы не с м о ж е т е у к а з ы в а т ь в р е м я с т о ч н о с т ь ю до н а н о с е к у н д , так как д и с к р е т н о с т ь с и с т е м н о г о т а й м е р а к о м п ь ю т е р а н а м н о г о б о л ь ш е . Метод может возвращать значение или и з них возвращает¬ ся в том случае, если поток завершил свою работу через заданный интервал времени, а второе — если указанный период времени истек, но поток так и не завершился. А н а л и з и р у я з н а ч е н и е , п о л у ч е н н о е о т м е т о д а J o i n , ваша п р о г р а м м а м о ж е т опреде¬ л и т ь , сумел ли поток з а в е р ш и т ь с я в о т в е д е н н о е для этого время или нет. В зависимо¬ сти от логики работы п р о г р а м м ы в п о с л е д н е м случае м о ж н о п р и б е г н у т ь , н а п р и м е р , к а в а р и й н о м у з а в е р ш е н и ю р а б о т ы « у п р я м о г о » потока. В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 10.8, мы п р и в е л и п р и ­ мер и с п о л ь з о в а н и я метода Глава

341


Листинг using using

10.8.

Файл

System;

namespace

JoinDemo

class public

static

string

void

s;

для

завершения

любую работы

строку

или

\"exit\"

s ввели

static

void

Thread

строку

s);

args) new

ReadThread

завершил

свою

М е т о д M a i n этой п р о г р а м м ы с о з д а е т н о в ы й п о т о к R e a d T h r e a d и з а п у с к а е т его н а в ы п о л н е н и е м е т о д о м S t a r t . З а т е м г л а в н ы й п о т о к п р о г р а м м ы (т. е . тот поток, в р а м к а х к о т о р о г о р а б о т а е т м е т о д M a i n ) п е р е в о д и т с я в с о с т о я н и е о ж и д а н и я до тех п о р , пока н е з а в е р ш и т с в о ю р а б о т у п о т о к R e a d T h r e a d : Thread thr new thr.

342

ReadThread

завершил

А.

Фролов, Г.

свою

Фролов. Язык С#. Самоучитель


Когда ж е это, н а к о н е ц , п р о и с х о д и т , главный поток в ы в о д и т н а к о н с о л ь с о о б щ е н и е о завершении р а б о т ы п о т о к а Что ж е п р е д с т а в л я е т собой поток R e a d T h r e a d ? Он в ы п о л н я е т п р о с т у ю работу: в б е с к о н е ч н о м цикле он вводит с к л а в и а т у р ы тек¬ с т о в ы е строки и затем в ы в о д и т их на к о н с о л ь : public

static

string or(

void

s;

"Введите

любую

строку

или

\"exit\"

для

завершения

работы

s ввели

строку

У с л о в и е м з а в е р ш е н и я цикла является в в о д строки R e a d T h r e a d выходит из цикла и завершает свою работу.

s)

exit.

При

этом

поток

Д а н н о е с о б ы т и е о ж и д а е т с я г л а в н ы м п о т о к о м п р и л о ж е н и я при п о м о щ и м е т о д а J o i n . Д о тех п о р , пока р а б о т а е т п о т о к R e a d T h r e a d , г л а в н ы й п о т о к н а ш е й програм¬ мы н а х о д и т с я в с о с т о я н и и о ж и д а н и я .

Критические секции Критические секции

(critical

sections)

являются

наиболее простым

с р е д с т в о м синхро¬

н и з а ц и и п о т о к о в для о б е с п е ч е н и я п о с л е д о в а т е л ь н о г о д о с т у п а к р е с у р с а м . Они рабо¬ т а ю т б ы с т р о и не с н и ж а ю т п р о и з в о д и т е л ь н о с т и с и с т е м ы Как работают критические секции? Если один п о т о к в о ш е л в к р и т и ч е с к у ю с е к ц и ю , но еще не в ы ш е л из н е е , то при по¬ п ы т к е д р у г и х п о т о к о в войти в ту же с а м у ю к р и т и ч е с к у ю с е к ц и ю они будут переведе¬ ны в с о с т о я н и е о ж и д а н и я . П о т о к и п р о б у д у т в этом с о с т о я н и и до тех п о р , пока п о т о к , к о т о р ы й в о ш е л в к р и т и ч е с к у ю с е к ц и ю п е р в ы м , не в ы й д е т из нее. Т а к и м с п о с о б о м г а р а н т и р у е т с я , что ф р а г м е н т кода, з а к л ю ч е н н ы й в н у т р и к р и т и ч е ­ ской

секции,

будет в ы п о л н я т ь с я

потоками

последовательно, если

все они

работают

с одной н той же к р и т и ч е с к о й с е к ц и е й . Если поток р а б о т а е т с н е с к о л ь к и м и р е с у р с а м и , д о с т у п к к о т о р ы м д о л ж е н выпол¬ няться п о с л е д о в а т е л ь н о , о н м о ж е т создать н е с к о л ь к о к р и т и ч е с к и х с е к ц и й . З а м е т и м , ч т о , когда потоки р а б о т а ю т с н е с к о л ь к и м и к р и т и ч е с к и м и с е к ц и я м и , все они д о л ж н ы и с п о л ь з о в а т ь о д и н а к о в у ю п о с л е д о в а т е л ь н о с т ь входа в эти к р и т и ч е с к и е секции и вы¬ х о д а и з них, иначе в о з м о ж н ы в з а и м н ы е б л о к и р о в к и п о т о к о в .

Глава

343


П р и м е н е н и е к л ю ч е в о г о слова lock П р о с т е й ш и й с п о с о б с и н х р о н и з а ц и и п о т о к о в с п о м о щ ь ю к р и т и ч е с к и х секций о с н о в а н н а и с п о л ь з о в а н и и к л ю ч е в о г о слова l o c k , с п е ц и а л ь н о п р е д у с м о т р е н н о г о для этой задачи в я з ы к е п р о г р а м м и р о в а н и я С#. Для того ч т о б ы о ф о р м и т ь ф р а г м е н т кода в виде

секции, достаточно

з а к л ю ч и т ь его в блок l o c k , как это п о к а з а н о н и ж е :

К л ю ч е в о е с л о в о t h i s и с п о л ь з у е т с я здесь для т о г о , ч т о б ы у к а з а т ь , что з а щ и щ а е т с я к о н к р е т н ы й о б ъ е к т класса. В о б щ е м с л у ч а е вы д о л ж н ы задавать в с к о б к а х после к л ю ­ чевого слова l o c k ссылку на о б ъ е к т , и с п о л ь з у е м ы й для с и н х р о н и з а ц и и (т. е. объект, за д о с т у п к к о т о р о м у будут к о н к у р и р о в а т ь н е с к о л ь к о п о т о к о в ) . Рассмотрим следующий пример программы (листинг Листинг using using

10.9.

10.9).

Файл

System;

namespace

L oc k De m o

public

void

i

0;

i

10;

i+ + )

class

[STAThread] static void

args)

NumberWriter Thread Thread

344

thr2

new new new

Thread(new Thread(new А В Фролов, Г. В. Фролов. Язык С#. Самоучитель


Эта п р о г р а м м а создает д в а п о т о к а , к а ж д ы й из к о т о р ы х в ы в о д и т в цикле ч и с л а от О до 9, п о с л е в ы в о д а с о з д а е т с я небольшая з а д е р ж к а : public

void

i

0;

i

10;

i+ + )

Если бы мы не з а к л ю ч и л и тело о п е р а т о р а цикла в блок l o c k , потоки к о н к у р и р о в а ¬ ли бы м е ж д у собой за к о н с о л ь , о т о б р а ж а я числа в х а о т и ч е с к о м п о р я д к е :

Н а л и ч и е блока l o c k г а р а н т и р у е т , ч т о , пока один п о т о к н е з а к о н ч и т в ы в о д , д р у г о й будет н а х о д и т ь с я в с о с т о я н и и о ж и д а н и я . В о т что мы у в и д и м на консоли в р е з у л ь т а т е и с п о л ь з о в а н и я такой с и н х р о н и з а ц и и :

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

Использование класса Если н е о б х о д и м о о б е с п е ч и т ь п о с л е д о в а т е л ь н о е и с п о л ь з о в а н и е р е с у р с о в п о т о к а м и , с о з д а н н ы м и в р а м к а х р а з н ы х п р о ц е с с о в , в м е с т о к р и т и ч е с к и х секций н е о б х о д и м о ис¬ п о л ь з о в а т ь о б ъ е к т ы с и н х р о н и з а ц и и M u t e x . Свое н а з в а н и е они п о л у ч и л и о т в ы р а ж е н и я e x c l u s i v e , что о з н а ч а е т « в з а и м н о и с к л ю ч а ю щ и й » . О б ъ е к т M u t e x м о ж е т н а х о д и т ь с я в о т м е ч е н н о м или н е о т м е ч е н н о м с о с т о я н и и . Ко¬ гда к а к о й - л и б о поток, п р и н а д л е ж а щ и й л ю б о м у п р о ц е с с у , с т а н о в и т с я в л а д е л ь ц е м объ¬ екта M u t e x , п о с л е д н и й п е р е к л ю ч а е т с я в н е о т м е ч е н н о е с о с т о я н и е . Если же п о т о к «от¬ к а з ы в а е т с я » о т в л а д е н и я о б ъ е к т о м M u t e x , его с о с т о я н и е с т а н о в и т с я о т м е ч е н н ы м . Организация последовательного доступа к ресурсам с использованием объектов M u t e x в о з м о ж н а п о т о м у , что в к а ж д ы й м о м е н т т о л ь к о один п о т о к м о ж е т в л а д е т ь э т и м о б ъ е к т о м . Для того чтобы з а в л а д е т ь о б ъ е к т о м , к о т о р ы й у ж е з а х в а ч е н , все о с т а л ь н ы е потоки в ы н у ж д е н ы ж д а т ь . Д л я того ч т о б ы о б ъ е к т M u t e x был д о с т у п е н п о т о к а м , при¬ н а д л е ж а щ и м р а з л и ч н ы м п р о ц е с с а м , при создании вы д о л ж н ы п р и с в о и т ь ему имя. Глава 10.

345


З а м е т и м , что о б ъ е к т ы в я з ы к е С# р е а л и з у ю т м е х а н и з м с и н х р о н и з а ц и и п о т о ­ ков ОС Microsoft W i n d o w s , д о с т у п н ы й на у р о в н е п р о г р а м м н о г о Win32 этой О С . С р е д с т в а м н о г о п о т о ч н о с т и этой ОС мы п о д р о б н о р а с с м о т р е л и в [10]. Применение класса M u t e x в п р о г р а м м е С# д е м о н с т р и р у е т с я в л и с т и н г е Листинг using using

10.10.

10.10.

Файл

System;

namespace class

NumberWriter

Mutex

mutex;

public mutex

public

new M u t e x ( f a l s e ,

void

for(int i

0;

i

10;

i+ + ) i);

class

static

void

NumberWriter Thread Thread

346

thr2

args) nw

new

new T h r e a d ( n e w new T h r e a d ( n e w

А В. Фролов, Г. В, Фролов. Язык С#. Самоучитель


Эта п р о г р а м м а очень н а п о м и н а е т п р е д ы д у щ у ю п р о г р а м м у (см. л и с т и н г 9.10), од¬ нако для р е а л и з а ц и и к р и т и ч е с к о й секции мы и с п о л ь з о в а л и в ней не к л ю ч е в о е слово а класс M u t e x . И з м е н е н и я затрагивают только класс

отображающий на консоли

в о т д е л ь н о м п о т о к е числа от 0 до 9: class

NumberWriter

Mutex

mutex;

public mutex

public

new M u t e x ( f a l s e ,

void

i

0;

i

10;

i+ + )

Как в и д и т е , теперь в этом классе и м е е т с я к о н с т р у к т о р , с о з д а ю щ и й о б ъ е к т класса Mutex: mutex

new M u t e x ( f a l s e ,

С у щ е с т в у е т н е с к о л ь к о п е р е г р у ж е н н ы х к о н с т р у к т о р о в в классе M u t e x . И х прото¬ т и п ы мы п р и в е л и н и ж е : public public

Mutex Mutex(bool); Mutex(bool,

П е р в ы й и з них с о з д а е т о б ъ е к т класса M u t e x , о б л а д а ю щ и й с в о й с т в а м и , п р и н я т ы м и по

умолчанию.

Второй

конструктор

позволяет указать,

должен

ли

объект

класса

M u t e x с о з д а в а т ь с я в н е о т м е ч е н н о м с о с т о я н и и , т. е. д о л ж е н ли им владеть вызываю¬ щий поток. Третий к о н с т р у к т о р а н а л о г и ч е н п р е д ы д у щ е м у , о д н а к о д о п о л н и т е л ь н о по¬ зволяет указать имя о б ъ е к т а M u t e x , д о с т у п н о е з а п р е д е л а м и т е к у щ е г о процесса. В нашей п р о г р а м м е мы создали о б ъ е к т класса M u t e x в о т м е ч е н н о м с о с т о я н и и . Та¬ ким о б р а з о м , п о т о к , с о з д а в ш и й M u t e x , н е с т а н о в и т с я его в л а д е л ь ц е м . Глава


В х о д в к р и т и ч е с к у ю с е к ц и ю в ы п о л н я е т с я в нашей п р о г р а м м е

образом:

Вызов метода без п а р а м е т р о в п р и в е д е т к т о м у , что п р о г р а м м а будет на¬ х о д и т ь с я в к р и т и ч е с к о й секции н е о п р е д е л е н н о д о л г о , пока она не п о к и н е т его, закрыв о б ъ е к т класса M u t e x я в н ы м о б р а з о м . В нашей п р о г р а м м е в ы х о д из к р и т и ч е с к о й секции в ы п о л н я е т с я т а к :

Здесь м ы п р о с т о в ы з ы в а е м метод C l o s e , о п р е д е л е н н ы й в классе M u t e x . В классе M u t e x и м е е т с я еще два метода позволяющих задать время о ж и д а н и я , а также д е й с т в и я , п р е д п р и н и м а е м ы е после его и с т е ч е н и я :

Они о т л и ч а ю т с я л и ш ь с п о с о б о м о п р е д е л е н и я в р е м е н н о г о и н т е р в а л а (в первом слу¬ чае время у к а з ы в а е т с я в м и л л и с е к у н д а х , а во в т о р о м — задается при п о м о щ и класса

Использование класса Monitor И н а к о н е ц , еще о д н о с р е д с т в о с и н х р о н и з а ц и и п о т о к о в , к о т о р о е мы р а с с м о т р и м в на¬ шей к н и г е , это класс M o n i t o r . Он н е с к о л ь к о п р о щ е в и с п о л ь з о в а н и и по с р а в н е н и ю с только что р а с с м о т р е н н ы м к л а с с о м M u t e x , о д н а к о он не п о з в о л я е т с и н х р о н и з и р о ¬ вать п о т о к и , в ы п о л н я е м ы е в р а м к а х р а з н ы х п р о ц е с с о в . В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е класс M o n i t o r для с о з д а н и я к р и т и ч е с к о й с е к ц и и . 10.11.

Листинг using using

мы п р и м е н и л и

Файл

System;

namespace class

NumberWriter

public

void

(int i

0; i

10; i+ + ) i);

348

В. Фролов, Г, В, Фролов. Язык С#. Самоучитель


static

void

args)

NumberWriter Thread Thread

thr2

new new new

Thread(new Thread(new

Все с а м о е и н т е р е с н о е н а х о д и т с я в н у т р и о б ъ я в л е н и я м е т о д а W r i t e T h r e a d , п о л н я ю щ е г о с я в р а м к а х о т д е л ь н о г о потока: public

вы¬

void

for(int i

0;

i

10;

i+ + ) i)

Как в и д и т е , в х о д в к р и т и ч е с к у ю с е к ц и ю в ы п о л н я е т с я при п о м о щ и м е т о д а E n t e r , а в ы х о д из нее — при п о м о щ и м е т о д а E x i t . О б о и м э т и м м е т о д а м в к а ч е с т в е парамет¬ ра н е о б х о д и м о п е р е д а в а т ь ссылку на о б ъ е к т с и н х р о н и з а ц и и . П о м и м о методов E n t e r и E x i t , в классе M o n i t o r есть еще ряд полезных методов. Н а п р и м е р , с п о м о щ ь ю м е т о д а T r y E n t e r п р о г р а м м а м о ж е т о п р е д е л и т ь , заблокирова¬ на ли к р и т и ч е с к а я секция. Э т о п о з в о л и т п о т о к у , н а п р и м е р , в х о д и т ь в к р и т и ч е с к у ю с е к ц и ю т о л ь к о в т о м с л у ч а е , если она с в о б о д н а , избегая б л о к и р о в к и . Более подробную и н ф о р м а ц и ю о средствах синхронизации потоков, доступных п р и л о ж е н и я м С#, вы найдете в с п р а в о ч н о й с и с т е м е M S D N , в х о д я щ е й в состав систе¬ мы р а з р а б о т к и Microsoft Visual Studio

Глава 10.

349


Глава 11. Делегаты и события В п р е д ы д у щ е й главе мы уже р а с с к а з ы в а л и вам о д е л е г а т а х , с л у ж а щ и х в качестве не¬ п л о х о й з а м е н ы у к а з а т е л я м на ф у н к ц и и . В этой главе мы р а с с м о т р и м д е л е г а т ы более детально. Ч т о б ы вам б ы л о п о н я т н е е н а з н а ч е н и е д е л е г а т о в , р а с с к а ж е м п о д р о б н е е о ф у н к ц и я х о б р а т н о г о в ы з о в а и их п р и м е н е н и и . Вы з н а е т е , что п р о г р а м м ы в качестве а р г у м е н т о в п р о г р а м м ы м о г у т п е р е д а в а т ь ме¬ т о д а м ссылки на п а р а м е т р ы — п е р е м е н н ы е , числа, м а с с и в ы и т. п. С у щ е с т в у е т и еще одна в о з м о ж н о с т ь : в качестве а р г у м е н т о в м о ж н о п е р е д а в а т ь м е т о д а м с с ы л к и на дру¬

гие методы. П р е д с т а в и м с е б е , что нам н у ж н о р а с п е ч а т ы в а т ь массив с т р о к на р а з н ы х п р и н т е р а х , п р и ч е м для к а ж д о г о п р и н т е р а п р е д у с м о т р е н а с о б с т в е н н а я п р о ц е д у р а печати. Эта про¬ ц е д у р а м о ж е т быть о ф о р м л е н а , н а п р и м е р , в виде м е т о д а к а к о г о - л и б о класса. В з а в и с и м о с т и от тех или и н ы х условий п р о г р а м м а п е ч а т и могла бы п е р е д а в а т ь м е т о д у , о т в е т с т в е н н о м у за п е ч а т ь , ссылку на тот или иной м е т о д , р е а л и з у ю щ и й осо¬ бенности конкретного принтера. Д р у г о й п р и м е р , когда т р е б у ю т с я ф у н к ц и и о б р а т н о г о в ы з о в а , — с о р т и р о в к а произ¬ в о л ь н ы х о б ъ е к т о в . Э т о т п р и м е р в с т р е ч а е т с я п р а к т и ч е с к и во всех у ч е б н и к а х по про¬ г р а м м и р о в а н и ю . С о з д а е т с я о б щ и й к о д , п р и г о д н ы й для с о р т и р о в к и р а з л и ч н ы х объек¬ т о в , а п р о ц е д у р ы с р а в н е н и я о б ъ е к т о в , к о т о р ы е зависят от типа с р а в н и в а е м ы х объек¬ т о в , о ф о р м л я ю т с я в виде ф у н к ц и й о б р а т н о г о вызова. П р и с о з д а н и и м н о г о п о т о ч н ы х п р о г р а м м , р а с с м о т р е н н ы х в п р е д ы д у щ е й главе, так¬ же н е о б х о д и м м е х а н и з м о б р а т н о г о вызова. Н а п о м н и м , что для з а п у с к а метода в от¬ д е л ь н о м п о т о к е нам н у ж н о создать д л я этого м е т о д а д е л е г а т , т. е. ссылку на метод. П р и этом п р о г р а м м а н и к о г д а не в ы з ы в а е т н а п р я м у ю м е т о д , п р е д н а з н а ч е н н ы й для ра¬ боты в о т д е л ь н о м п о т о к е . В м е с т о э т о г о ссылка на д а н н ы й м е т о д п е р е д а е т с я конструк¬ тору класса T h r e a d , ф о р м и р у ю щ е м у новый поток. Ф у н к ц и и о б р а т н о г о вызова часто и с п о л ь з у ю т с я для о б р а б о т к и р е д к о в о з н и к а ю щ и х с о б ы т и й . Т и п и ч н ы й п р и м е р — п е р е д а ч а д а н н ы х по сети. П р и этом п р о г р а м м а ини¬ ц и и р у е т п р о ц е с с п е р е д а ч и или п р и е м а блока д а н н ы х , но не д о ж и д а е т с я его заверше¬ ния. К о г д а ж е д а н н ы е будут п о л у ч е н ы , д р а й в е р с е т е в о г о п р о т о к о л а , о т в е ч а ю щ и й за передачу данных, вызовет функцию программы, предназначенную для обработки д а н н ы х . С с ы л к а н а эту ф у н к ц и ю п е р е д а е т с я д р а й в е р у при и н и ц и и р о в а н и и п р о ц е с с а передачи данных. Механизм функций обратного вызова реализован во многих языках программиро¬ в а н и я . В я з ы к а х С и С++ для этого п р и м е н я ю т с я у к а з а т е л и на ф у н к ц и и , т. е. перемен¬ н ы е , с о д е р ж а щ и е адрес ф у н к ц и й . О д н а к о , как мы н е о д н о к р а т н о з а м е ч а л и , у к а з а т е л и С и о б л а д а ю т в е с ь м а с у щ е с т в е н н ы м н е д о с т а т к о м — они н е б е з о п а с н ы в и с п о л ь з о в а п р о г р а м м а м о ж е т по о ш и б к е л е г к о и з м е н и т ь с о д е р ж и м о е у к а з а т е л я т а к и м обра¬ зом, что он у к а з ы в а т ь не туда, куда н у ж н о . В р е з у л ь т а т е п о в е д е н и е п р о г р а м м ы станет н е п р е д с к а з у е м ы м .

350


В этом с м ы с л е д е л е г а т ы С# п о л н о с т ь ю б е з о п а с н ы . Д е л е г а т м о ж е т с о д е р ж а т ь толь¬ ко ссылку на м е т о д , о б ъ я в л е н н ы й в п р о г р а м м е , и н и ч е г о б о л ь ш е . Вы не с м о ж е т е изме¬ нить с о д е р ж и м о е д е л е г а т а н и к а к и м н е п р е д у с м о т р е н н ы м с п о с о б о м , что с у щ е с т в е н н о уменьшает вероятность возникновения ошибки.

Использование делегатов Р а с с м о т р и м п р о с т е й ш и й п р и м е р делегата для х р а н е н и я ссылки н а м е т о д ы , р еал и з у ю ¬ щий в о з м о ж н о с т и р а з н ы х п р и н т е р о в . Наша программа (листинг д в а ж д ы р а с п е ч а т а е т на в и р т у а л ь н о м п р и н т е р е (вы¬ п о л н я ю щ е м вместо н а с т о я щ е й печати в ы в о д строк н а к о н с о л ь ) с о д е р ж и м о е м а с с и в а строк. 11.1. using

Файл

System;

namespace

public

delegate

public

void

void str,

StringPrinter

printer)

class public

static

void

public

static

void

str)

str)

J

static

void

string[]

Глава

args) Lines

Делегаты и события

351


new

new на foreach

(string

первом

CurrentString

in

Lines)

StringPrinter new на foreach

(string

mp

PrintString

втором

CurrentString

in

Lines)

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

на на

первом принтере: втором принтере:

реализации

функции

This is С# s t r i n g T h i s . i s . С#. печати

в

нашей

array array.

программе

предусмотрен

класс

M y P r i n t e r , объявленный с л е д у ю щ и м образом: class

MyPrinter

public

delegate

public

void

void str,

StringPrinter

printer)

Обратите внимание, что в качестве члена этого класса мы объявили поле-делегат

public

delegate

void

Получив управление, м е т о д M a i n создает объект класса M y P r i n t e r , при п о м о щ и которого наша программа б у д е т распечатывать строки массива: M y P r i n t e r mp

352

new A

Г. В. Фролов. Язык

Самоучитель


Д а л е е она и н и ц и а л и з и р у е т д е л е г а т S t r i n g P r i n t e r , н а «простой» п р и н т е р S i m p l e P r i n t e r :

записывая

в

него

ссылку

StringPrinter new П о с л е такой и н и ц и а л и з а ц и и м о ж н о п р и с т у п а т ь к печати: на п е р в о м п р и н т е р е : foreach

(string

in

Lines)

В процессе п е ч а т и , в ы п о л н я е м о м м е т о д о м P r i n t S t r i n g и з класса M y P r i n t e r , м ы п е р е д а е м этому методу д е л е г а т S t r i n g P r i n t e r . Таким с п о с о б о м м ы с о о б щ а е м методу P r i n t S t r i n g , какой м е т о д д о л ж е н и с п о л ь з о в а т ь с я д л я печати. К о г д а с о д е р ж и м о е массива п е ч а т а е т с я в п е р в ы й раз, в роли в и р т у а л ь н о г о п р и н т е р а будет и с п о л ь з о в а н м е т о д public

static

void

str)

П е р е д тем как п е ч а т а т ь с о д е р ж и м о е м а с с и в а во второй раз, п р о г р а м м а з а п и с ы в а е т в делегат S t r i n g P r i n t e r ссылку на другой виртуальный принтер, реализованный методом SmartPrinter: StringPrinter new Д а л е е мы п о в т о р я е м цикл печати в н е и з м е н н о м виде: на foreach

(string

CurrentString

втором in

принтере:

Lines)

о д н а к о , печать будет в ы п о л н я т ь с я п о - д р у г о м у , так как за нее б у д е т отве¬ чать м е т о д S r a a r t P r i n t e r : public

static

void

str)

Console. Write

str)

О т у п о м я н у т о г о в ы ш е м е т о д а S i r a p l e P r i n t e r метод S r a a r t P r i n t e r о т л и ч а е т с я т е м , что он д о б а в л я е т точку к к а ж д о й р а с п е ч а т ы в а е м о й с т р о к е .

Статические делегаты В п р е д ы д у щ е м п р и м е р е п р о г р а м м ы мы создали один д е л е г а т и з а т е м , в п р о ц е с с е рабо¬ ты п р о г р а м м ы , з а п и с ы в а л и в него ссылки на р а з н ы е м е т о д ы . С у щ е с т в у е т и д р у г а я в о з м о ж н о с т ь , о с н о в а н н а я н а о б ъ я в л е н и е д е л е г а т о в как с т а т и ч е с к и х ч л е н о в к л а с с а .

Глава 12 Язык С#

Делегаты и события


Такие д е л е г а т ы и н и ц и а л и з и р у ю т с я вместе с д р у г и м и с т а т и ч е с к и м и ч л е н а м и п ер ед ис­ пользованием класса и проще в употреблении. Р а с с м о т р и м п р и м е р п р о г р а м м ы , и с х о д н ы й текст к о т о р о й приведен в 1 Листинг using

Файл System;

namespace class

DelegateStatic MyPrinter

public

delegate

public

void

void str,

StringPrinter

printer)

printer

class

DelegateStaticApp

public

static

void

str) str)

public

static

void

str) str)

public static readonly stringSimplePrinter new public

static

readonly

new static

void

args) Lines

354

А В Фролов,

В Фролов.

Самоучитель


M y P r i n t e r mp

new на

foreach

(string

первом

CurrentString

на foreach

(string

CurrentString

принтере:

in

Lines)

втором in

Lines)

От п р е д ы д у щ е й п р о г р а м м ы она о т л и ч а е т с я т е м , что теперь мы о б ъ я в и л и два стати¬ ческих делегата, первый из которых содержит ссылку на виртуальный принтер S i r a p l e P r i n t e r , а второй на виртуальный принтер S m a r t P r i n t e r : public new

static

readonly

public

static

readonly

StringPrinter

stringSimplePrinter

new Эти д е л е г а т ы о б ъ я в л е н ы т о л ь к о ч и т а е м ы м и с п о м о щ ь ю к л ю ч е в о г о слова r e a d o n l y , п о э т о м у наша п р о г р а м м а п о о ш и б к е н е с м о ж е т и з м е н и т ь х р а н я щ и е с я в Как м е т о д M a i n п о л ь з у е т с я с т а т и ч е с к и м и д е л е г а т а м и ? П р и печати н а « п р о с т о м » п р и н т е р е д е л е г а т передается методу P r i n t S t r i n g ( в ы п о л н я ю щ е м у п е ч а т ь ) в качестве п а р а м е т р а : на foreach

(string

первом

CurrentString

in

принтере: Lines)

Этот д е л е г а т был на этапе и н и ц и а л и з а ц и и п р о г р а м м ы — он с о д е р ж и т с с ы л к у н а метод S i m p l e P r i n t e r . П е ч а т ь на в т о р о м п р и н т е р е а н а л о г и ч н о , но с и с п о л ь з о в а н и е м д р у г о г о а именно делегата на foreach

(string

CurrentString

втором in

Lines)

Так как этот д е л е г а т был п р о и н и ц и а л и з и р о в а н с с ы л к о й н а м е т о д S m a r t P r i n t e r , во второй раз печать будет в ы п о л н я т ь с я « п о - у м н о м у » , т. е. с д о б а в л е н и е м т о ч к и . Глава

Делегаты и события

355


Массивы делегатов При н е о б х о д и м о с т и п р и л о ж е н и е м о ж е т с о з д а в а т ь м а с с и в ы д е л е г а т о в , их с с ы л к а м и на Это п о з в о л я е т в ы б и р а т ь для о б р а б о т к и ф у н к ц и ю об¬ р а т н о г о в ы з о в а д и н а м и ч е с к и , в о время р а б о т ы п р о г р а м м ы . Этот п р и е м м ы д е м о н с т р и р у е м н а п р и м е р е приведен в листинге Листинг using

п р о г р а м м ы , и с х о д н ы й текст к о т о р о й

Файл System;

namespace class

DelegateArray MyPrinter

public

delegate

public

void

class

void str,

StringPrinter

printer)

DelegateArrayApp

public

static

void

str)

public

static

void

str)

public

static

void

str)

Console.WriteC

static

void

args) Lines

356

А В

Г. В. Фролов. Язык

Самоучитель


MyPrinter

new M y P r i n t e r StringPrinter

new new new new на foreach

(string

первом

CurrentString

на foreach

(string

(string

Lines)

втором

CurrentString

на foreach

in

принтере:

in

Lines)

третьем

CurrentString

in

принтере:

Lines)

В о с н о в н о м классе п р о г р а м м ы D e l e g a t e A r r a y A p p мы о п р е д е л и л и ждый

3

м е т о д а , ка­

из которых играет роль виртуального принтера:

public

static

void

str)

public

static

void

str)

public

static

void

str)

Эти методы аналогичны тем, что мы использовали в нашей п р е д ы д у щ е й програм­ ме.

П е р в ы й из них при выводе строки добавляет к ее концу символ пробела, второй

точку, а третий

Делегаты

символ подчеркивания.

357


В теле метода M a i n м ы объявили массив делегатов M y P r i n t e r new Далее н а ш а п р о г р а м м а и н и ц и а л и з и р у е т я ч е й к и м а с с и в а , з а п и с ы в а я в них д е л е г а т ы , с о з д а н н ы е на соответствующих методов: new new new Т е п е р ь , ч т о б ы в ы б р а т ь н у ж н ы й нам п р и н т е р , м ы м о ж е м у к а з а т ь и н д е к с соответст¬ вующего делегата в массиве: на foreach

(string

первом

CurrentString

на foreach

(string

CurrentString

на foreach

(string

CurrentString

in

Lines)

втором принтере: in

Lines)

третьем in

принтере:

Lines)

Так как в п р о ц е с с е своей р а б о т ы п р о г р а м м а может д и н а м и ч е с к и о б р а щ а т ь с я к лю¬ бым н у ж н ы м е й я ч е й к а м м а с с и в а , д а н н ы й с п о с о б о б е с п е ч и в а е т д и н а м и ч е с к и й в ы б о р н е о б х о д и м о й ф у н к ц и и о б р а т н о г о в ы з о в а ( т о ч н е е г о в о р я , м е т о д а о б р а т н о г о вызова, хо¬ тя э т о т т е р м и н и не я в л я е т с я о б щ е п р и н я т ы м ) .

Обработка событий Самые первые п р о г р а м м ы , создаваемые для к о м п ь ю т е р о в , представляли собой потоки м а ш и н н ы х команд, и с п о л н я е м ы х л и н е й н о . Л и н е й н ы й ход программы нарушали лишь ус¬ ловные или циклические операторы. О б ы ч н о такие программы получали в начале своей работы все н е о б х о д и м ы е д а н н ы е ( н а п р и м е р , через перфокарты или перфоленты), а после завершения выдавали результат на устройство печати, перфокарты и перфоленты. О п и с а н н ы й р е ж и м обработки д а н н ы х называется пакетным, так как к о м п ь ю т е р загру¬ жает по очереди пакеты программ с д а н н ы м и и обрабатывает их, выдавая результат. С о в р е м е н н ы е д и а л о г о в ы е п р о г р а м м ы , н а п и с а н н ы е для п е р с о н а л ь н ы х компьюте¬ р о в , р а б о т а ю т с о в е р ш е н н о п о - д р у г о м у . П о с л е запуска они в о с п р и н и м а ю т д е й с т в и я п о л ь з о в а т е л е й над м ы ш ь ю и к л а в и а т у р о й как события (events), в ы п о л н я я при в о з н и к ­ н о в е н и и р а з л и ч н ы х с о б ы т и й т с или и н ы е д е й с т в и я . Г о в о р я т , что работа с о в р е м е н н ы х д и а л о г о в ы х п р о г р а м м с г р а ф и ч е с к и м и н т е р ф е й с о м управляется событиями (events program).

358

А В. Фролов, Г. В, Фролов. Язык С#. Самоучитель


Щ е л к а я м ы ш ь ю , н а п р и м е р , кнопки и н с т р у м е н т а л ь н о й л и н е й к и , п о л ь з о в а т е л ь м о ­ в ы з ы в а т ь на экран те или иные д и а л о г о в ы е окна. При к а ж д о м т а к о м щ е л ч к е ОС с о б ы т и я (т. с. щелчки м ы ш ь ю ) , п е р е д а в а я у п р а в л е н и е р а з л и ч н ы м фраг¬ ментам программы.

Эти

фрагменты

программы

выполняют асинхронную обработку

с о б ы т и й , так как д е й с т в и я п о л ь з о в а т е л е й над п р о г р а м м о й а с и н х р о н н ы п о о т н о ш е н и ю к р а б о т е самой п р о г р а м м ы и с о в е р ш е н н о н е п р е д с к а з у е м ы . Если п р о г р а м м а р а б о т а е т в м н о г о п о т о ч н о м р е ж и м е , то в ней м о г у т в о з н и к а т ь со¬ б ы т и я , с в я з а н н ы е не только с д е й с т в и я м и п о л ь з о в а т е л е й , но и с р а б о т о й д о п о л н и т е л ь ¬ ных п о т о к о в . Е с л и , н а п р и м е р , п р о г р а м м а з а п у с к а е т п о т о к , в ы п о л н я ю щ и й к а к у ю - л и б о д л и т е л ь н у ю работу ( н а п р и м е р , п о и с к и н ф о р м а ц и и в базе д а н н ы х ) , то о к о н ч а н и е этой р а б о т ы м о ж е т р а с с м а т р и в а т ь с я в качестве п р и м е р а т а к о г о с о б ы т и я .

Публикация событий В о т л и ч и е от м н о г и х д р у г и х я з ы к о в п р о г р а м м и р о в а н и я , в к о т о р ы х о б р а б о т к а с о б ы т и й в ы п о л н я е т с я при п о м о щ и ф у н к ц и й о б р а т н о г о в ы з о в а , в я з ы к е С# п р и м е н я е т с я более развитая

технология

публикации событий

(events

publishing)

и

подписки

на

события

(events s u b s c r i b i n g ) . И д е я п у б л и к а ц и и с о б ы т и й и п о д п и с к и на них д о с т а т о ч н о п о н я т н а . П р о в е д е м ана¬ логию с интернетовскими сайтами новостей. В д а н н о м случае п у б л и к а ц и я н о в о с т и на т а к о м сайте будет играть роль с о б ы т и я . О н о п о х о ж е на с о б ы т и е , в о з н и к а ю щ е е в п р о г р а м м е , в том с м ы с л е , что п о я в л я е т с я не¬ п р е д с к а з у е м ы м о б р а з о м и с о д е р ж и м о е его т а к ж е н е п р е д с к а з у е м о . М н о г и е сайты новостей с о д е р ж а т раздел п о д п и с к и , где к а ж д ы й ж е л а ю щ и й м о ж е т о с т а в и т ь свой адрес э л е к т р о н н о й п о ч т ы д л я о т п р а в к и т е к с т о в н о в о с т е й . П о л ь з о в а т е л и И н т е р н е т а могут сами п о д п и с ы в а т ь с я на и н т е р е с у ю щ и е их н о в о с т и на тех или и н ы х К о г д а на сайте п о я в л я е т с я н о в о с т ь , она р а с с ы л а е т с я всем п о д п и с ч и к а м по элек¬ тронной почте. Получение и чтение новости в данном контексте можно рассматривать как о б р а б о т к у с о б ы т и я . Создавая программу на языке С#, вы можете разрабатывать объекты классов, ини­ ц и и р у ю щ и е п о я в л е н и е с о б ы т и й , а т а к ж е о б ъ е к т ы к л а с с о в , о б р а б а т ы в а ю щ и х эти собы¬ тия. П е р в ы е из них д о л ж н ы о п у б л и к о в а т ь с о б ы т и я , а в т о р ы е — п о д п и с а т ь с я на них. Для р е а л и з а ц и и о б р а б о т ч и к о в с о б ы т и й в я з ы к е С# и с п о л ь з у ю т с я у ж е з н а к о м ы е вам д е л е г а т ы . К л а с с , п у б л и к у ю щ и й с о б ы т и я (т. е. к л а с с , в к о т о р о м эти с о б ы т и я возника¬ ю т ) , д о л ж е н о п р е д е л и т ь делегат. Э т о т д е л е г а т р е а л и з у е т с я к л а с с о м , п о д п и с ы в а ю щ и м ¬ ся на с о б ы т и е , т. е. о т в е т с т в е н н ы м за о б р а б о т к у с о б ы т и я . К а к т о л ь к о с о б ы т и е возника¬ ет, п р о и с х о д и т вызов м е т о д а о б р а б а т ы в а ю щ е г о класса, з а д а н н о г о д е л е г а т о м . Э т о т ме¬ тод

обычно

называется

обработчиком

события

(event h a n d l e r ) .

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

п р и ч е м это о т о б р а ж е н и е в ы п о л н я е т с я в рамках о т д е л ь н о г о потока. И с х о д н ы й

текст с о о т в е т с т в у ю щ е г о класса п р е д с т а в л е н н и ж е : Глава

и

359


public

class

private private

StringWriter

Thread int count;

public count

public

0;

void

count++

public

void

thr

go

new

Как видите, пока здесь для нас нет ничего нового. М е т о д W r i t e T h r e a d содержит цикл отображения символа а метод запускает процесс отображения в отдельном по¬ токе. В переменной c o u n t ведется подсчет общего количества выведенных символов. Д а в а й т е д о п о л н и м этот класс к о д о м , с о з д а ю щ и м и п у б л и к у ю щ и м с о б ы т и е , связан¬ ное с о т о б р а ж е н и е м о ч е р е д н о г о с и м в о л а на к о н с о л и . С этой ц е л ь ю мы д о б а в и м в класс S t r i n g W r i t e r д в а п о л я , п е р в о е и з к о т о р ы х , с и м е н е м E v e n t H a n d l e r , я в л я е т с я де¬ л е г а т о м , а в т о р о е , с и м е н е м O n L o o p D o n e , и г р а е т роль так н а з ы в а е м о й п е р е м е н н о й события: public

delegate

public

event

void

EventHandler (object writer,

EventHandler

OnLoopDone;

Д е л е г а т , п р е д н а з н а ч е н н ы й д л я о б р а б о т к и с о б ы т и й , д о л ж е н всегда о б ъ я в л я т ь с я с д в у м я п а р а м е т р а м и . П е р в ы й из них задает о б ъ е к т , с о з д а ю щ и й с о б ы т и е , а в т о р о й и н ф о р м а ц и ю о событии (например, время возникновения события). П е р е м е н н а я с о б ы т и я O n L o o p D o n e будет х р а н и т ь ссылку н а м е т о д о б р а б о т ч и к а события. Остановимся подробнее на параметрах делегата. Для т о г о ч т о б ы п р о г р а м м а м о г л а п е р е д а в а т ь и н ф о р м а ц и ю о с о б ы т и и о б р а б о т ч и к у э т о г о с о б ы т и я , н е о б х о д и м о о б ъ я в и т ь п р о и з в о д н ы й к л а с с , создав его на базе класса EventArgs:

360

В. Фролов, Г. В Фролов Язык

Самоучитель


public

class

public

count) count;

dt

public public

readonly readonly

int count; System. DateTime

dt;

В этом классе следует определить п о л я , х р а н я щ и е в с ю н е о б х о д и м у ю и н ф о р м а ц и ю о с о б ы т и и , а т а к ж е к о н с т р у к т о р , и н и ц и а л и з и р у ю щ и й эти поля. В д а н н о м случае мы о п р е д е л и л и поле c o u n t для х р а н е н и я п о р я д к о в о г о н о м е р а о ч е р е д н о г о о т о б р а ж а е м о г о на к о н с о л и с и м в о л а , а т а к ж е поле dt класса в котором будет х р а н и т ь с я д а т а и время в о з н и к н о в е н и я с о б ы т и я ( с в о й с т в о х р а н и т и н ф о р м а ц и ю о т е к у щ е й дате и в р е м е н и ) . В своей п р о г р а м м е вы будете о п р е д е л я т ь д р у г и е п о л я , в с о о т в е т с т в и и с л о г и к о й работы программы. З а м е т и м , что оба поля д о с т у п н ы т о л ь к о д л я ч т е н и я , так как о б ъ я в л е н ы при п о м о щ и к л ю ч е в о г о слова r e a d o n l y . И х с о д е р ж и м о е и з м е н я е т с я т о л ь к о к о н с т р у к т о р о м в м о с о з д а н и я объекта.

Создание события Р а с с м о т р и м теперь п р о ц е д у р у с о з д а н и я с о б ы т и я . М ы н е м н о г о м о д и ф и ц и р у е м исход¬ ный код м е т о д а р а б о т а ю щ е г о в р а м к а х о т д е л ь н о г о потока: public

void

new null)

О б р а т и т е в н и м а н и е , что на к а ж д о й и т е р а ц и и ц и к л а мы с о з д а е м н о в ы й о б ъ е к т клас¬ са I n f o E v e n t A r g s , хранящий информацию о событии: new П р и этом т е к у щ и й н о м е р о т о б р а ж а е м о г о с и м в о л а п е р е д а е т с я к о н с т р у к т о р у класса а время возникновения с о б ы т и я к о н с т р у к т о р с о х р а н я е т самостоя¬ тельно. Глава

Делегаты и события

361


П о с л е того как о б ъ е к т , с о д е р ж а щ и й и н ф о р м а ц и ю о с о б ы т и и , с ф о р м и р о в а н , наша п р о г р а м м а в ы з ы в а е т о б р а б о т ч и к с о б ы т и я , о б р а щ а я с ь для этого к п е р е м е н н о й с о б ы т и я OnLoopDone: if

null)

В качестве п е р в о г о п а р а м е т р а мы п е р е д а е м ссылку на о б ъ е к т , с о з д а ю щ и й с о б ы т и е (указав к л ю ч е в о е с л о в о t h i s , о з н а ч а ю щ е е в этом к о н т е к с т е ссылку на д а н н ы й объ¬ ект), а в к а ч е с т в е

ссылку

на

объект c o u n t l n f o r m a t i o n ,

содержащий

информацию о событии. О б р а т и т е в н и м а н и е , что перед в ы з о в о м о б р а б о т ч и к а с о б ы т и я мы п р о в е р я е м пере¬ менную события на равенство значению n u l l .

Это д е л а е т с я для т о г о , ч т о б ы прове¬

р и т ь , и м е ю т с я л и п о д п и с ч и к и н а д а н н о е с о б ы т и е . Если п о д п и с ч и к о в нет, и н и ц и и р о в а ­ ние с о б ы т и я п р и в е д е т к

и с к л ю ч е н и я во время р а б о т ы п р о г р а м м ы .

Н и ж е м ы п р и в е л и новый в а р и а н т класса S t r i n g W r i t e r , п у б л и к у ю щ и й с о б ы т и е и вызывающий обработчик этого события: public

class

StringWriter

public

delegate

public

event

private private

void

EventHandler

EventHandler

writer,

OnLoopDone;

Thread t h r ; int count;

public count

public

0;

void

Console WriteLine

new if

(OnLoopDone

null)

Г. R Фролов. Язык

Самоучитель


public

void

go()

new

Подписка на события И т а к , т е п е р ь у нас есть о б ъ е к т , п у б л и к у ю щ и й и с о з д а ю щ и й с о б ы т и я . Т е п е р ь нам нуж¬ ны о б ъ е к т ы , п о д п и с ы в а ю щ и е с я на с о б ы т и я и в ы п о л н я ю щ и е их о б р а б о т к у . З а м е т и м , что к а ж д о е с о б ы т и е могут о б р а б а т ы в а т ь п р о и з в о л ь н о е к о л и ч е с т в о п о д п и с ч и к о в . Здесь я з ы к С# В

накладывает никаких ограничений.

нашем

примере

мы

создадим

два

подписчика —

классы

DisplayCount

и public

class

public

DisplayCount

void

Subscribe (StringWriter

writer)

new

public

void

writer,

info)

dt at:

public

class

public

DisplayCount2

void

Subscribe (StringWriter

writer)

new

public

void

writer,

info)

count:

В

каждом

из

этих

и CountHasChanged.

классов Первый

мы из

объявили них

два

играет роль

метода

с

именами

подписчика,

Subscribe

а второй — обра¬

батывает событие. Глава

Делегаты и

363


в о б о и х классах в ы г л я д я т о д и н а к о в о : public

void

Subscribe (StringWriter

writer)

new С помощью операции чиков.

В

качестве

они д о б а в л я ю т о б р а б о т ч и к с о б ы т и я в ц е п о ч к у о б р а б о т ­

параметра

переменной

C o u n t H a s C h a n g e d , непосредственно

события

передается

ссылка

на

метод

обрабатывающий события.

П о с л е в ы п о л н е н и я этой о п е р а ц и и класс будет п о д п и с а н на с о б ы т и е и с м о ж е т его обрабатывать. Что ж е касается м е т о д о в C o u n t H a s C h a n g e d , в ы п о л н я ю щ и х о б р а б о т к у с о б ы т и й , то первый из них в ы в о д и т на к о н с о л ь в р е м я в о з н и к н о в е н и я с о б ы т и я , а второй — те¬ к у щ и й н о м е р о т о б р а ж а е м о г о символа. Как мы у ж е г о в о р и л и , эта и н ф о р м а ц и я переда¬ ется о б р а б о т ч и к у при в о з н и к н о в е н и и с о б ы т и я .

Программа с обработкой событий Теперь м ы р а с с м о т р и м п о л н ы й и с х о д н ы й т е к с т п р о г р а м м ы , о б р а б а т ы в а ю щ е й с о б ы т и я (листинг

В ней о п р е д е л е н ы классы и д р у г и е о б ъ е к т ы , р а с с м о т р е н н ы е нами ранее

в этом р а з д е л е . Листинг using using

11.4.

Файл

System;

namespace public

EventsDemo class

public

delegate

public

event

void

Thread thr; int count;

public

StringWriter

public

EventHandler

EventHandler

private private

count

364

StringWriter writer,

OnLoopDone;

0;

void

WriteThread

В Фролов, Г. В. Фролов Язык С# Самоучитель


new if

public

(OnLoopDone

void

thr

public

new T h r e a d ( n e w

class

public

EventArgs

InfoEventArgs

int

count)

count; dt

public public public

readonly readonly

class

public

int

count; dt;

DisplayCount

void

Subscribe (StringWriter writer)

new

public

void

writer,

InfoEventArgs

info)

dt at: dt.Hour,

public

class

public

void

DisplayCount2 Subscribe (StringWriter

writer)

new Глава

Делегаты и события

365


public

void

writer, count:

class

EventsDemoApp

static

void

args)

StringWriter

sw

new

DisplayCount

dc

new

DisplayCount2

Получив

управление,

dc2

new D i s p l a y C o u n t 2

метод

Main

нашей

программы

создаст

объект

класса

StringWriter: StringWriter

sw

new

StringWriter

Д а л е е о н с о з д а е т два о б ъ е к т а , п р е д н а з н а ч е н н ы е для о б р а б о т к и с о б ы т и й : DisplayCount

DisplayCount2

dc

dc2

new

new

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

создания

обработчиков

мы

вызываем

для

каждого

из

них

метод

Sub¬

s c r i b e , осуществляющий подписку н а события. Д а л е е нам о с т а е т с я т о л ь к о з а п у с т и т ь в ы в о д с о б ы т и я в о т д е л ь н о м п о т о к е , вызвав для этого м е т о д

Вот что вы у в и д и т е на э к р а н е , з а п у с т и в нашу п р о г р а м м у на в ы п о л н е н и е :

366

А В Фролов, Г. В Фролов. Язык

Самоучитель


Event at: Current

12:42:3 1

Event at: 12:42:4 Current count: 2 Event at: 12:42:5 Current count: 3 Event at: 12:42:6 Current count: 4 Event at: 12:42:7 Current count: 5 Event at: 12:42:8 Current count: 6 После отображения символа в дело включаются наши обработчики событий. П е р в ы й из них п о к а з ы в а е т в р е м я о т о б р а ж е н и я , а второй — т е к у щ и й н о м е р с и м в о л а . З а м е т и м , что д е л е г а т ы и о б р а б о т ч и к и с о б ы т и й н у ж н ы д а л е к о не в к а ж д о й про¬ г р а м м е . И с п о л ь з у й т е их (как и д р у г и е с р е д с т в а я з ы к а С#) т о л ь к о при н е о б х о д и м о с т и . В м е с т е с тем м о щ н ы й , а г л а в н о е , у д о б н ы й в и с п о л ь з о в а н и и м е х а н и з м о б р а б о т к и собы¬ тий о б я з а т е л ь н о вам п р и г о д и т с я в тех с л у ч а я х , когда н у ж н о сочетать д л и т е л ь н у ю об¬ р а б о т к у д а н н ы х или о ж и д а н и е ч е г о - л и б о с ф у н к ц и о н и р о в а н и е м п о л ь з о в а т е л ь с к о г о и н ­ т е р ф е й с а п р о г р а м м ы . Эти с р е д с т в а будут вам н у ж н ы при р а б о т е в базами д а н н ы х и передаче д а н н ы х по л о к а л ь н ы м сетям и через И н т е р н е т .

Глава

Делегаты и события

367


Глава 12. Работа с текстовыми строками В то время как п е р в ы е к о м п ь ю т е р ы с о з д а в а л и с ь г л а в н ы м о б р а з о м для в ы п о л н е н и я ма¬ т е м а т и ч е с к и х в ы ч и с л е н и й , сейчас они а к т и в н о п р и м е н я ю т с я для р а б о т ы с т е к с т а м и , г р а ф и к о й , з в у к о м и в и д е о . П р и этом задача о б р а б о т к и т е к с т о в ы х с и м в о л о в и строк яв¬ л я е т с я одной из в а ж н е й ш и х . С е г о д н я вы не н а й д е т е ни одной п р о г р а м м ы , в к о т о р о й так или иначе не и с п о л ь з о в а л и с ь бы т е к с т о в ы е с т р о к и . П р а к т и ч е с к и в л ю б о м я з ы к е п р о г р а м м и р о в а н и я п р е д у с м о т р е н богатый набор ф у н к ц и й , п р е д н а з н а ч е н н ы х д л я р а б о т ы с с и м в о л а м и и т е к с т о в ы м и с т р о к а м и . Эти ф у н к ц и и п о з в о л я ю т о б ъ е д и н я т ь с т р о к и , в ы р е з а т ь и з строк ф р а г м е н т ы , менять строч¬ ные буквы на п р о п и с н ы е и о б р а т н о , искать в с т р о к а х д р у г и е строки и п р о и з в о л ь н ы е п о с л е д о в а т е л ь н о с т и с и м в о л о в и т. д. В я з ы к а х С и С + + с т а н д а р т н ы е ф у н к ц и и , о б р а б а т ы в а ю щ и е с и м в о л ы и с т р о к и , яв¬ л я ю т с я ч а с т ь ю б и б л и о т е к и т р а н с л я т о р а . Ф а к т и ч е с к и они уже стали ч а с т ь ю этих язы¬ ков и п о т о м у о п и с ы в а ю т с я во всех у ч е б н и к а х , п о с в я щ е н н ы х С и С + + . Что к а с а е т с я я з ы к а п р о г р а м м и р о в а н и я С # , т о , как м ы у ж е г о в о р и л и , для п р е д ­ с т а в л е н и я т е к с т о в ы х с т р о к в б и б л и о т е к е классов Microsoft F r a m e w o r k имеется класс S y s t e m . S t r i n g . В м е с т е с н а б о р о м о п е р а ц и й , м е т о д о в и и н д е к с а т о р о в этот класс п р е д с т а в л я е т собой м о щ н о е с р е д с т в о о б р а б о т к и строк. П о м и м о этого класса, существуют и другие, предназначенные для построения строк. Э т о , например, класс S t r i n g B u i l d e r , а также класс R e g e x . Последний из них предна¬ значен для использования так называемых регулярных выражений (regular expressions), представляющих собой м о щ н е й ш е е средство обработки текста. Рассказ об этих возможно¬ стях в ы х о д и т за рамки нашей книги. При необходимости вы можете найти описание регу¬ л я р н ы х выражений в а также в учебниках по языку Perl. П е р е д тем как мы п р и с т у п и м к и з у ч е н и ю с п о с о б о в п о с т р о е н и я т е к с т о в ы х с т р о к н а п о м н и м , чем строки С# о т л и ч а ю т с я от строк, с к о т о р ы м и и м е ю т дело про¬ г р а м м и с т ы С, С + + и P a s c a l . П р е ж д е в с е г о , п р о г р а м м ы , с о с т а в л е н н ы е на я з ы к а х С, С + + и P a s c a l , и м е ю т непо¬ с р е д с т в е н н ы й д о с т у п к п р е д с т а в л е н и ю т е к с т о в ы х строк, х р а н я щ и х с я в о п е р а т и в н о й п а м я т и к о м п ь ю т е р а . Такие п р о г р а м м ы м о г у т , н а п р и м е р , п е р е з а п и с ы в а т ь или и з м е н я т ь с о д е р ж и м о е с т р о к по месту их р а с п о л о ж е н и я . Что же касается п р о г р а м м С # , то все о п е р а ц и и с т е к с т о в ы м и с т р о к а м и выполняют¬ ся т о л ь к о при п о м о щ и м е т о д о в , с в о й с т в , и н т е р ф е й с о в и и н д е к с а т о р о в , предусмотрен¬ ных в с о о т в е т с т в у ю щ и х классах б и б л и о т е к и Microsoft Framework. Д а л е е , п р о г р а м м ы С, С + + и Pascal м о г у т р а б о т а т ь с р а з л и ч н ы м и п р е д с т а в л е н и я м и т е к с т о в ы х с т р о к , т а к и м и , как, н а п р и м е р A S C I I и U N I C O D E . В з а в и с и м о с т и от пред¬ с т а в л е н и я ( и л и , как е щ е г о в о р я т , к о д и р о в к и ) для х р а н е н и я к а ж д о г о с и м в о л а строки может использоваться или н е с к о л ь к о байт п а м я т и . В я з ы к е С# т е к с т о в ы е строки п р е д с т а в л е н ы только в к о д и р о в к е U N I C O D E , п р е д ­ п о л а г а ю щ е й п р е д с т а в л е н и е о д н о г о т е к с т о в о г о с и м в о л а 2 байтами п а м я т и .

368


Применение класса М е т о д ы и свойства класса

S t r i n g п о з в о л я ю т в ы п о л н я т ь над с т р о к а м и

с а м ы е н е о б х о д и м ы е о п е р а ц и и , т а к и е , н а п р и м е р , как с р а в н е н и е , п о и с к п о д с т р о к и , объ¬ е д и н е н и е строк, к о п и р о в а н и е с и м в о л о в из одной строки в д р у г у ю , и з в л е ч е н и е под¬ строки и т. д. Н и ж е мы р а с с м о т р и м п р и е м ы

в ы п о л н е н и я этих о п е р а ц и й и п р и в е д е м

примеры

программ.

Создание строк В гл.

1 мы р а с с к а з ы в а л и вам о т е к с т о в ы х строках и л и т е р а л а х , а также п р и в о д и л и не­

к о т о р ы е п р и м е р ы их и с п о л ь з о в а н и я в п р о г р а м м а х С#. До сих пор в н а ш и х п р о г р а м м а х в с т р е ч а л и с ь г л а в н ы м о б р а з о м с т р о к и , с о з д а н н ы е при п о м о щ и л и т е р а л о в или п о л у ч е н н ы е при р а б о т е тех или и н ы х м е т о д о в ( н а п р и м е р , т е к с т о в ы е строки с о о б щ е н и й о б о ш и б к а х ) . В классе S y s t e m . S t r i n g и м е е т с я н а б о р п е р е г р у ж е н н ы х к о н с т р у к т о р о в , п о з в о л я ю щ и х с о з д а в а т ь т е к с т о в ы е строки и д р у г и м и способами.

П р е о б р а з о в а н и е м а с с и в а с и м в о л о в в строку Если у вас есть м а с с и в с и м в о л о в U N I C O D E типа c h a r , то его м о ж н о п р е о б р а з о в а т ь в с т р о к у s t r i n g , в о с п о л ь з о в а в ш и с ь для этого к о н с т р у к т о р о м , п р е д у с м о т р е н н ы м спе­ ц и а л ь н о д л я этой цели в классе S y s t e m . Р а с с м о т р и м п р и м е р п р о г р а м м ы , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 12.1. Листинг

12.1.

Файл

chl2\CreateString\CreateStringApp.cs

using System; namespace CreateString class

CreateStringApp

static

void

chart] string s

args)

new

String(ch);

Здесь мы о б ъ я в и л и массив с и м в о л о в ch и п р о и н и ц и а л и з и р о в а л и его при п о м о щ и символьных литералов: ch Глава 12. Работа с

строками

369


Ч т о б ы п р е о б р а з о в а т ь этот м а с с и в с и м в о л о в в т е к с т о в у ю с т р о к у , мы воспользова¬ лись к о н с т р у к т о р о м класса S y s t e m . S t r i n g : string

s

new

О т о б р а з и в п о л у ч е н н у ю с т р о к у м е т о д о м W r i t e L i n e , м ы п о л у ч и м о ж и д а е м ы й ре¬ зультат, а и м е н н о у в и д и м н а к о н с о л и строку « Н е 1 1 о

С о з д а н и е с т р о к и н а базе ф р а г м е н т а м а с с и в а В п р е д ы д у щ е й п р о г р а м м е мы п р е о б р а з о в а л и в строку ц е л и к о м весь м а с с и в с и м в о л о в . С у щ е с т в у е т с п о с о б с о з д а н и я т е к с т о в о й строки на базе л ю б о г о ф р а г м е н т а такого мас¬ сива. Рассмотрим следующую программу (листинг Листинг

12.2.

12.2).

Файл

using System; namespace SubArray class

SubArrayApp

static

void

char

string

args) H

s

new

String(ch,

7,

6);

Здесь м ы в о с п о л ь з о в а л и с ь к о н с т р у к т о р о м класса параметра: string

s

new

String(ch,

7,

S t r i n g , и м е ю щ и м три

6)

Первый параметр задает ссылку на массив символов U N I C O D E , из которых нужно сделать с т р о к у . В т о р о й и т р е т и й п а р а м е т р ы о п р е д е л я ю т с о о т в е т с т в е н н о индекс перво¬ го э л е м е н т а м а с с и в а и к о л и ч е с т в о с и м в о л о в , из к о т о р ы х будет с о з д а н а строка. В р е з у л ь т а т е р а б о т ы п р и в е д е н н о й в ы ш е п р о г р а м м ы на к о н с о л ь в ы в о д и т с я т о л ь к о п о с л е д н и е 6 с и м в о л о в массива, о б р а з у ю щ и е строку

Заполнение строки символом И н о г д а п р о г р а м м и с т у м о ж е т п о т р е б о в а т ь с я создать с т р о к у , с о д е р ж а щ у ю ко¬ л и ч е с т в о о д и н а к о в ы х с и м в о л о в . Т и п и ч н ы й п р и м е р — с о з д а н и е р а з д е л и т е л е й для заго¬ ловков в листингах. В исходный текст которой представлен в листинге ются два с п о с о б а р е ш е н и я этой п р о б л е м ы .

370

Г.

12.3, демонстриру¬

Фролов. Язык С# Самоучитель


Листинг

12.3.

Файл

using System; namespace Fill class

FillApp

static

void

args

string string

header title=

string

headerl

"Оформление

new

40);

П е р в ы й с п о с о б п р е д п о л а г а е т « л о б о в о е » р е ш е н и е . М ы и с п о л ь з у е м для инициализа¬ ции с т р о к и д л и н н ы й т е к с т о в ы й л и т е р а л : string

header

Э т о т с п о с о б имеет, п о к р а й н е й м е р е , д в а н е д о с т а т к а . В о - п е р в ы х , такие л и т е р а л ы м о г у т з а г р о м о ж д а т ь л и с т и н г п р о г р а м м ы . В о - в т о р ы х , создавая литерал из повторяющихся символов, приходится подсчитывать количество этих с и м в о л о в в р у ч н у ю , что очень н е у д о б н о . Другой способ основан на использовании одного из конструкторов, предусмотрен¬ ных в классе S y s t e m . S t r i n g с п е ц и а л ь н о для и н и ц и а л и з а ц и и п о д о б н ы х с т р о к : string

headerl

new

В к а ч е с т в е п е р в о г о п а р а м е т р а этому к о н с т р у к т о р у н у ж н о п е р е д а т ь п о в т о р я ю щ и й с я символ, а в качестве второго

количество повторов символа.

Н е б е з о п а с н ы е к о н с т р у к т о р ы в классе Как м ы у ж е г о в о р и л и , п р о г р а м м ы С # р а б о т а ю т под у п р а в л е н и е м с и с т е м ы и с п о л н е н и я F r a m e w o r k , и д а ж е в случае н а л и ч и я в них о ш и б о к не п р е д с т а в л я ю т о п а с н о с т и для д р у г и х р а б о т а ю щ и х п р о г р а м м или о п е р а ц и о н н о й с и с т е м ы . Такая б е з о п а с н а я рабо¬ та н а к л а д ы в а е т н е к о т о р ы е о г р а н и ч е н и я на п р о г р а м м ы , что не всегда п р и е м л е м о . При о б р а щ е н и и к п р о г р а м м н о м у и н т е р ф е й с у A P I о п е р а ц и о н н о й с и с т е м ы часто п р и х о д и т с я иметь д е л о с у к а з а т е л я м и , з а п р е щ е н н ы м и к п р и м е н е н и ю в о б ы ч н ы х про¬ г р а м м а х С#. Тем не м е н е е , вы м о ж е т е с о з д а т ь так н а з ы в а е м у ю н е б е з о п а с н у ю про¬ г р а м м у (или н е к о н т р о л и р у е м у ю п р о г р а м м у ) , в к о т о р о й и с п о л ь з о в а н и е у к а з а т е л е й до¬ пускается. Глава

Работа с

371


такой п р о г р а м м ы п р и в е д е н в л и с т и н г е Листинг

12.4.

12.4.

Файл

using System; namespace UnsafeString class unsafe

static

ch string

void

H

args)

e

s; ptr

s

new

Обратите внимание на определение метода M a i n : unsafe s t a t i c void args)

Как в и д и т е , мы д о б а в и л и к л ю ч е в о е слово

Это к л ю ч е в о е слово с о о б щ а е т

к о м п и л я т о р у С# о т о м , что в н у т р и д а н н о г о м е т о д а б у д у т и с п о л ь з о в а н ы н е б е з о п а с н ы е к о н с т р у к ц и и С# ( т а к и е , н а п р и м е р , как у к а з а т е л и ) . В н у т р и м е т о д а M a i n имеется массив с и м в о л о в U N I C O D E с и м е н е м c h , п р о и н и циализированный

статически.

Нашей

задачей

будет п р е о б р а з о в а н и е

этого

массива

в т е к с т о в у ю строку с и с п о л ь з о в а н и е м у к а з а т е л е й . Все самое и н т е р е с н о е н а х о д и т с я в с л е д у ю щ е м ф р а г м е н т е кода: f i x e d ( c h a r * ptr s

new

Этот ф р а г м е н т п р е д с т а в л я е т собой блок fixed

расположен

оператор,

В к р у г л ы х с к о б к а х п о с л е ключе¬

вого

слова

выполняющий

ptr.

И н и ц и а л и з а ц и я в ы п о л н я е т с я п о с р е д с т в о м записи в п е р е м е н н у ю p t r адреса са­

инициализацию

указателя

мого п е р в о г о э л е м е н т а м а с с и в а ch с и н д е к с о м 0.

372

Г. В. Фролов. Язык С# Самоучитель


П р о г р а м м и с т а м , с о с т а в л я в ш и м ранее п р о г р а м м ы на я з ы к а х С и С + + , и з в е с т н о , что для п о л у ч е н и я адреса р а с п о л о ж е н и я и с п о л ь з у е т с я символ Таким

образом,

в оперативной памяти компьютера

Этот же символ п р и м е н я е т с я и в н е б е з о п а с н о м коде С#. перед

выполнением

тела

блока

fixed

программа

записывает

в у к а з а т е л ь p t r адрес начала массива c h . Д а л е е этот у к а з а т е л ь п е р е д а е т с я с о о т в е т с т ­ в у ю щ е м у к о н с т р у к т о р у класса

к о т о р ы й и ф о р м и р у е т из него тек¬

стовую строку. В д о к у м е н т а ц и и на С# вы найдете еще н е с к о л ь к о н е б е з о п а с н ы х к о н с т р у к т о р о в , по¬ з в о л я ю щ и х создавать строки из м а с с и в о в 8-разрядных ц е л ы х ч и с е л , а т а к ж е с и м в о л о в UNICODE. З а м е т и м т а к ж е , что при т р а н с л я ц и и н е б е з о п а с н о й п р о г р а м м ы , и с х о д н ы й т е к с т ко¬ торой п р и в е д е н в л и с т и н г е

12.4, н е о б х о д и м о и с п о л ь з о в а т ь ключ

(для п а к е т н о й

т р а н с л я ц и и в к о м а н д н о й строке) или п р и р а в н я т ь с в о й с т в а проекта A l l o w u n s a f e c o d e b l o c k s з н а ч е н и ю T r u e в д и а л о г о в о м окне с в о й с т в проекта (рис.

Включение

режима

использования

12.1).

небезопасного

кода

Чтобы о т о б р а з и т ь это о к н о на э к р а н е , щ е л к н и т е н а з в а н и е п р о е к т а в окне S o l u t i o n E x p l o r e r , р а с п о л о ж е н н о м по у м о л ч а н и ю в п р а в о м в е р х н е м углу г л а в н о г о окна систе¬ мы р а з р а б о т к и п р о г р а м м Microsoft Visual Studio

Копирование и клонирование строк Если вам н у ж н о создать к о п и ю т е к с т о в о й с т р о к и , то это м о ж н о сделать при п о м о щ и о б ы к н о в е н н о й о п е р а ц и и п р и с в а и в а н и я . В этом случае в п а м я т и будет н а х о д и т ь с я два о д и н а к о в ы х объекта. Глава

Работа с

строками

373


Н а п р и м е р , н и ж е мы о б ъ я в и л и т е к с т о в ы е стр о ки

и

после чего с к о п и р о в а л и

п е р в у ю с т р о к у во в т о р у ю : string

src

"Hello,

Теперь у строки.

нас

есть два р а з л и ч н ы х объекта, с о д е р ж а щ и х о д и н а к о в ы е текстовые

Для к о п и р о в а н и я строк м о ж н о также в о с п о л ь з о в а т ь с я с т а т и ч е с к и м м е т о д о м о п р е д е л е н н ы м в классе S t r i n g : string

s2

Р е з у л ь т а т будет такой ж е , как и при и с п о л ь з о в а н и и о п е р а т о р а п р и с в а и в а н и я ,

бу­

дет создан н о в ы й о б ъ е к т , с о д е р ж а щ и й и с х о д н у ю строку. В п е р е м е н н у ю s2 будет за­ п и с а н а с с ы л к а на этот о б ъ е к т . О п е р а ц и я к л о н и р о в а н и я строк очень п о х о ж а н а о п е р а ц и ю к о п и р о в а н и я , однако она не п р и в о д и т к с о з д а н и ю о б ъ е к т а - б л и з н е ц а . В м е с т о этого с о з д а е т с я еще одна ссылка на у ж е с у щ е с т в у ю щ и й объект: string

s3

П о с л е в ы п о л н е н и я этого кода в п е р е м е н н у ю s3 src. Так как м е т о д

б у д е т з а п и с а н а с с ы л к а на строку

возвращает данные базового типа

нам здесь при­

шлось выполнить явное приведение типов. О п и с а н н ы е в ы ш е о п е р а ц и и к о п и р о в а н и я и к л о н и р о в а н и я д е м о н с т р и р у ю т с я в про­ г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 12.5. Листинг

12.5.

Файл

using System; namespace StringCopy class

StringCopyApp

static

void

string string string string

src

args) "Hello,

s2 s3 строка: 1: 2:

374

Ав.

src) s2);

Г. В. Фролов.


Конкатенация строк И н о г д а перед п р о г р а м м и с т о м встает задача строк. Эта о п е р а ц и я в ы п о л н я е т с я с л е д у ю щ и м а затем з а п и с а т ь в нее о б ъ е д и н я е м ы е тода Вот как о п е р а т о р о б ъ е д и н я е т строки string string string

конкатенации (объединения) текстовых образом: вначале нужно объявить новую строки при п о м о щ и о п е р а т о р а или ме­ и

s2 s2;

В результате будет с о д е р ж а т ь строку З а м е т и м , что т а к и м о б р а з о м м о ж н о о б ъ е д и н я т ь п р о и з в о л ь н о е к о л и ч е с т в о тексто¬ вых строк. Другой способ объединения текстовых строк string

res2

применение метода S t r i n g

s2);

О б ъ е д и н я е м ы е строки п е р е д а ю т с я э т о м у м е т о д у в к а ч е с т в е п а р а м е т р о в . М е т о д в о з в р а щ а е т с с ы л к у на н о в у ю строку, п о л у ч е н н у ю в р е з у л ь т а т е объ¬ е д и н е н и я и с х о д н ы х строк. Существует несколько перегруженных методов объединять до четырех текстовых строк: string

res3

s2,

Кроме того, метод S t r i n g щиеся в массиве: string[]

string

resl,

позволяющих res2);

в с о с т о я н и и о б ъ е д и н и т ь все с т р о к и , храня¬

array

res4

После выполнения этого

ф р а г м е н т а кода в

строка « Э т о м а с с и в строк». Все о п и с а н н ы е в ы ш е с п о с о б ы о б ъ е д и н е н и я в программе ( л и с т и н г 12.6). Листинг

12.6.

переменной

res4 будет н а х о д и т ь с я

текстовых

строк демонстрируются

Файл

using System; namespace StringConcat class

StringConcatApp

static

void

string string Глава

args) "Hello,

s2

Работа с текстовыми строками

375


string string

res2

s2);

string

res3

s2,

resl,

array

string

res4 1:

4:

Извлечение подстроки Ч т о б ы с о з д а т ь н о в у ю строку на базе ф р а г м е н т а с у щ е с т в у ю щ е й с т р о к и , м о ж н о вос¬ п о л ь з о в а т ь с я м е т о д о м S u b s t r i n g ( л и с т и н г 12.7). Листинг

12.7.

Файл

System; namespace StringSubstring class

StringSubstringApp

static

void

string string

src res

args) "Hello,

Первый параметр метода

6)

S u b s t r i n g задает н а ч а л ь н ы й и н д е к с и з в л е к а е м о й под­

с т р о к и , а в т о р о й , н е о б я з а т е л ь н ы й , р а з м е р п о д с т р о к и . Н и ж е в с т р о к у r e s будет сан ф р а г м е н т с т р о к и s r c д л и н о й 6 с и м в о л о в и н а ч и н а ю щ и й с я с 7-й п о з и ц и и : string

res

Если в т о р о й п а р а м е т р не з а д а н , к о п и р у ю т с я все

376

метода

S u b s t r i n g ( о п р е д е л я ю щ и й размер п о д с т р о к и ) н а ч и н а я с и с х о д н о й п о з и ц и и и до конца строки. А. В Фролов, Г. В.

Самоучитель


Вставка подстроки Одна из о с о б е н н о с т е й с т р о к С# з а к л ю ч а е т с я в т о м , что прямая м о д и ф и к а ц и я сущест¬ в у ю щ е й строки н е в о з м о ж н а . Т а к и м о б р а з о м , если п р о г р а м м а создала т е к с т о в у ю стро¬ ку, т о , чтобы и з м е н и т ь ее, п р и д е т с я с о з д а в а т ь к о п и ю и уже п о т о м п е р е п и с ы в а т ь туда с и м в о л ы и с х о д н о й строки ( в н е и з м е н н о м или м о д и ф и ц и р о в а н н о м в и д е ) . С п е ц и а л ь н о для в ы п о л н е н и я о п е р а ц и и вставки одной строки в з а д а н н о е м е с т о дру¬ гой

строки

был

предусмотрен

метод

Insert.

Его

использование демонстрируется

в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 12.8. Листинг

12.8.

Файл

using System; namespace InsertString class

InsertStringApp

static

void

args)

string string

В к а ч е с т в е п е р в о г о п а р а м е т р а м е т о д у I n s e r t н у ж н о п е р е д а т ь и н д е к с п о з и ц и и ис¬ х о д н о й с т р о к и , в к о т о р у ю н у ж н о вставить д р у г у ю строку. Через в т о р о й п а р а м е т р пе¬ р е д а е т с я с с ы л к а на в с т а в л я е м у ю строку. Здесь мы в с т а в и л и з а п я т у ю и слово

между словом

и восклица¬

тельным знаком: string string

res

В р е з у л ь т а т е , как н е т р у д н о д о г а д а т ь с я , мы п о л у ч и м б е с с м е р т н у ю фразу «Не11о,

Замена символов и строк Если вам н у ж н о з а м е н и т ь в с т р о к е к а к о й - л и б о с и м в о л д р у г и м или в ы п о л н и т ь т а к у ю замену для п о с л е д о в а т е л ь н о с т и с и м в о л о в , и с п о л ь з у й т е м е т о д Replace. В качестве п е р в о г о п а р а м е т р а этому м е т о д у н у ж н о п е р е д а т ь з а м е н я е м ы й с и м в о л или з а м е н я е м у ю п о с л е д о в а т е л ь н о с т ь с и м в о л о в . В т о р о й п а р а м е т р задает н о в ы е симво¬ л ы , на к о т о р ы е н е о б х о д и м о з а м е н и т ь и с х о д н ы й с и м в о л или п о с л е д о в а т е л ь н о с т ь сим¬ волов. Глава 1 2 . Работа с

377


Пример использования этого метода приведен в листинге Листинг

12.9.

Файл

using System; namespace Replace class

ReplaceApp

static

void

string string string

args)

str res resl

"Hello, "C#

Прежде всего м ы меняем в н а слова « С #

строке

слово

World»:

string str s t r i n g res

"Hello,

В результате получится фраза « H e l l o ,

С#

Далее мы меняем в полученной строке все буквы о на цифру string

resl

В итоге получим фразу в «хакерском» стиле:

Удаление символов из строки Теперь решим д р у г у ю задачу

удалим из текстовой строки фрагмент с заданным на­

чальным и н д е к с о м и длиной. Для решения этой задачи нам потребуется м е т о д R e m o v e (листинг 12.10). Листинг

12.10.

Файл

using System; n a m e s p a c e R e m ove

static

void

string string

378

str res

args) "Hello,

В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


В к а ч е с т в е п е р в о г о п а р а м е т р а мы п е р е д а е м методу R e m o v e и н д е к с з а п я т о й , а в ка­ честве в т о р о г о — число 7: string string

str res

"Hello,

В результате мы получим из строки

строку

Удаление незначащих пробелов При о б р а б о т к е строк, в в е д е н н ы х п о л ь з о в а т е л е м , из п о л у ч е н н о й в р е з у л ь т а т е ввода строки р а з л и ч н ы х еще н а з ы в а ю т , (white spaces). Это л я ц и и , с и м в о л ы в о з в р а т а к а р е т к и , п е р е в о д а строки и

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

М е т о д T r i m п о з в о л я е т вам у д а л и т ь н е з н а ч а щ и е п р о б е л ы , р а с п о л о ж е н н ы е как в н а ч а л е , так и в к о н ц е т е к с т о в о й с т р о к и . Ее п р и м е н е н и е д е м о н с т р и р у е т с я в п р о г р а м ­ м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 12.11. Листинг

12.11.

Файл

System; n a m e s p a c e Trim class static

void

string string

str res

"\t

Здесь м е т о д T r i m у д а л и т с и м в о л т а б у л я ц и и и п р о б е л , р а с п о л о ж е н н ы й в н а ч а л е строки s t r , а т а к ж е п р о б е л , н а х о д я щ и й с я в к о н ц е этой с т р о к и : string string

str res

"\t str.TrimO;

При этом с и м в о л т а б у л я ц и и , р а з д е л я ю щ и й слова фразы и р а с п о л о ж е н н ы й в сере¬ дине строки, останется нетронутым. П о м и м о т о л ь к о что о п и с а н н о г о м е т о д а незначащих пробелов мож­ но выполнять методами Они и с п о л ь з у ю т с я а н а л о г и ч н о ме­ тоду T r i m . П е р в ы й и з этих м е т о д о в у д а л я е т пробелы, расположенные в конце с т р о к и , а второй в ее начале. Глава 12. Работа с текстовыми строками

379


Преобразование к верхнему и нижнему регистру Для п р е о б р а з о в а н и я всех с и м в о л о в строки к в е р х н е м у или н и ж н е м у регистру вы м о ­ жете и с п о л ь з о в а т ь м е т о д ы T o U p p e r и

соответственно.

Работа с э т и м и м е т о д а м и д е м о н с т р и р у е т с я в п р о г р а м м е , п р е д с т а в л е н н о й в листин¬ ге 12.12. Листинг

12.12.

Файл

using System; namespace

static

void

string string string

str resl res2

args) "Однажды,

в

с т у д е н у ю зимнюю

Если запустить эту программу на в ы п о л н е н и е , то на экране консоли появятся две стро¬ ки, первая из которых состоит из букв нижнего

а

из букв верхнего ре¬

гистра: однажды,

Так

как

в В

с т у д е н у ю зимнюю

в

я з ы к е С#

текстовые

символы

хранятся

в

кодировке

U N I C O D E , п р е о б р а з о в а н и е р е г и с т р а будет в ы п о л н я т ь с я п р а в и л ь н о не т о л ь к о для ла¬ т и н с к и х с и м в о л о в , но и д л я с и м в о л о в д р у г и х а л ф а в и т о в .

Выравнивание по левому и правому краю поля П р и ф о р м а т н о м в ы в о д е текста и н о г д а т р е б у е т с я в ы р о в н я т ь т е к с т о в у ю строку п о лево¬ му или п р а в о м у к р а ю поля з а д а н н о й ш и р и н ы . Это м о ж н о сделать при п о м о щ и м е т о д о в

и P a d R i g h t соответственно.

В к а ч е с т в е п е р в о г о п а р а м е т р а этим м е т о д а м п е р е д а е т с я ш и р и н а поля (в с и м в о л а х ) , внутри к о т о р о г о н е о б х о д и м о в ы п о л н и т ь в ы р а в н и в а н и е , а в к а ч е с т в е в т о р о г о вол-заполнитель

сим­

пробел). этих м е т о д о в д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст которой

п р е д с т а в л е н в л и с т и н г е 12.13.

380

В. Фролов, Г.

Фролов Язык С#. Самоучитель


Листинг

12.13.

Файл

using System; namespace Padding class

PaddingApp

static

void

string string string

str resl res2

-

"Однажды,

в

студеную

В к а ч е с т в е с и м в о л а з а п о л н и т е л я мы п р и м е н и л и н а ш а п р о г р а м м а в ы в е д е т н а к о н с о л ь после з а п у с к а : Однажды, Однажды,

в

студеную

в

зимнюю

символ подчеркивания.

В о т что

с т у д е н у ю зимнюю

зимнюю

Объединение массива строк Для

объединения строк, хранящихся в массиве, удобно использовать метод В качестве первого параметра этому методу передается символр а з д е л и т е л ь , к о т о р ы й будет д о б а в л е н п о с л е вставки к а ж д о й строки м а с с и в а . Ч е р е з в т о р о й п а р а м е т р п е р е д а е т с я с с ы л к а н а о б ъ е д и н я е м ы й м а с с и в строк. Третий и четвертый параметры необязательные. Они задают соответственно индекс первого элемента массива, с которого д о л ж н о начинаться объединение, и количество объ¬ единяемых элементов. Если эти параметры не заданы, объединяется весь массив. П р и м е р и с п о л ь з о в а н и я этого м е т о д а вы н а й д е т е в п р о г р а м м е , и с х о д н ы й т е к с т ко¬ т о р о й п р е д с т а в л е н в л и с т и н г е 12.14. 12.14.

Файл

ch

using System; namespace Join class static

void

string[]

args) array "массив",

Глава 12. Работа с

"строк"

строками

381


string

res

array,

0,

М а с с и в с т р о к о п р е д е л е н в этой п р о г р а м м е с т а т и ч е с к и : array "Это",

"массив",

"строк"

В к а ч е с т в е с и м в о л а - з а п о л н и т е л я мы и с п о л ь з у е м пробел: s t r i n g res

array,

0,

3)

Х о т я м ы о б ъ е д и н я е м все с т р о к и м а с с и в а a r r a y , для п р и м е р а м ы у к а з а л и необяза­ параметры

метода

определяющие

первую

ячейку

массива

и количество объединяемых ячеек.

Разбор строки Метод

Split,

определенный в классе

String,

п о з в о л я е т в ы п о л н я т ь р а з б о р строк,

с о д е р ж а щ и х к л ю ч е в ы е с л о в а , о т д е л е н н ы е д р у г о т д р у г а символами-разделителями. Р а с с м о т р и м п р о г р а м м у , п р е д н а з н а ч е н н у ю для р а з б о р а строки з а п у с к а у т и л и т с па¬ р а м е т р а м и вида Листинг

( л и с т и н г 12.15).

12.15.

Файл

using System; namespace Split class

SplitApp

static

void

string

args)

str resArray res

382

str in

S p l i t (new Char resArray)

А В

Г. В. Фролов. Язык

Самоучитель


И с х о д н а я с т р о к а , п о д л е ж а щ а я р а з б о р у , п р е д с т а в л е н а ниже: string

str

Здесь

мы

о б ъ я в и л и с т р о к у , п р е д н а з н а ч е н н у ю для з а п у с к а п р о г р а м м ы с и м е н е м

m y p r g , в к о т о р о й этой п р о г р а м м е п е р е д а ю т с я 3 п а р а м е т р а с и м е н а м и

у,

и Z.

Р а з б о р в ы п о л н я е т с я при п о м о щ и м е т о д а S p l i t : resArray В качестве п е р в о г о п а р а м е т р а э т о м у м е т о д у н у ж н о п е р е д а т ь с с ы л к у на м а с с и в сим¬ в о л о в - р а з д е л и т е л е й . В качестве т а к и х с и м в о л о в мы у к а з а л и здесь з а п я т у ю и п р о б е л . М е т о д S p l i t р а з б и р а е т строку, с о з д а в а я м а с с и в и з о т д е л ь н ы х к л ю ч е в ы х с л о в , обна¬ р у ж е н н ы х в с т р о к е . П р и н е о б х о д и м о с т и вы м о ж е т е о г р а н и ч и т ь р а з м е р с о з д а в а е м о г о м а с с и в а , у к а з а в м а к с и м а л ь н о д о п у с т и м о е з н а ч е н и е верхней г р а н и ц ы при п о м о щ и вто¬ рого п а р а м е т р а метода S p l i t . П о с л е запуска о п и с а н н о й в ы ш е п р о г р а м м ы н а к о н с о л и п о я в и т с я р е з у л ь т а т р а з б о р а строки: myprg х=1 у=4 z 5

Сравнение строк Вы м о ж е т е с р а в н и в а т ь строки т а к и м же о б р а з о м , как и числа. Об этом мы п о д р о б н о рассказывали в главе, посвященной условным операторам. Однако

есть

и

еще

одна

использование

статического

метода

C o m p a r e . С р а в н и в а е м ы е строки п е р е д а ю т с я э т о м у м е т о д у в к а ч е с т в е п а р а м е т р о в . До¬ п о л н и т е л ь н о м е т о д у C o m p a r e м о ж н о у к а з ы в а т ь , каким и м е н н о о б р а з о м в ы п о л н я т ь сравнение. Если строки р а в н ы , метод C o m p a r e в о з в р а щ а е т н у л е в о е з н а ч е н и е . строка

меньше

второй

(используется лексикографическое

сравнение),

Если п е р в а я возвращается

о т р и ц а т е л ь н о е з н а ч е н и е , а если б о л ь ш е — п о л о ж и т е л ь н о е . В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 12.16, мы п е р е д а е м м е ­ тоду C o m p a r e 3 п а р а м е т р а . П е р в ы е два из них п р е д с т а в л я ю т собой ссылки на сравни¬ ваемые строки, а третий, имеющий значение

t r u e , у к а з ы в а е т , что при с р а в н е н и и

не с л е д у е т у ч и т ы в а т ь р е г и с т р букв. 12.16.

Файл

using System; n a m e s p a c e Compare class static Глава

void

Работа с

args) строками

383


for строку: string

s "exit",

true)

0)

В нашей программе имеется бесконечный цикл, который прерывает свою работу, если ввести с к о н с о л и слово exit. З а м е т и м , что это слово м о ж е т быть н а б р а н о на лю¬ бом р е г и с т р е (так как при с р а в н е н и и р е г и с т р не у ч и т ы в а е т с я ) . S t r i n g содержит несколько перегруженных методов мы п р и в е л и п р о т о т и п т а к о г о м е т о д а , п р е д н а з н а ч е н н о г о д л я л е к с и к о г р а ф и ч е с к о г о сравне¬ ния т е к с т о в ы х с т р о к с у ч е т о м о с о б е н н о с т е й н а ц и о н а л ь н о г о алфавита: public s t a t i c int String stringl, Int32 String string2, Int32 index2, Int32 length, Boolean ignoreCase, Параметры s t r i n g l и i n d e x l задают соответственно первую сравниваемую с т р о к у и п о з и ц и ю в н у т р и ее. П а р а м е т р ы string2 и i n d e x 2 и м е ю т то же н а з н а ч е н и е , о т н о с я т с я к о в т о р о й с р а в н и в а е м о й с т р о к е . З а м е т и м , что п а р а м е т р ы indexl и i n d e x 2 м о ж н о не у к а з ы в а т ь . В этом случае п р о и с х о д и т с р а в н е н и е п о л н ы х строк. С п о м о щ ь ю н е о б я з а т е л ь н о г о п а р а м е т р а l e n g t h м о ж н о задать м а к с и м а л ь н у ю д л и н у с р а в н и в а е м ы х строк. Если з н а ч е н и е п а р а м е т р а i g n o r e C a s e р а в н о t r u e , т о м е т о д C o m p a r e н е будет у ч и т ы в а т ь р е г и с т р с р а в н и в а е м ы х строк. Однако самый интересный С его п о м о щ ь ю м о ж н о указать ф у н к ц и и н а ц и о н а л ь н ы й а л ф а в и т , к о т о р ы й д о л ж е н п р и м е н я т ь с я для л е к с и к о г р а ф и ч е ¬ ского с р а в н е н и я строк. В л и с т и н г е 12.17 мы п р и в е л и п р и м е р и с п о л ь з о в а н и я м е т о д а C o m p a r e для сравне¬ ния с т р о к с у ч е т о м н а ц и о н а л ь н о г о а л ф а в и т а . 12.17.

Листинг using namespace

Файл

Culture

class

static 384

void

args) А В

Г. В Фролов. Язык

Самоучитель


string s t r l string str2 string str3 string str4

" Я и з л е с у вышел"; "Был сильный м о р о з " ; "Hello, "Learning str2,

true,

str4,

true,

new new

о

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

Библиотека

классов

Framework

содержит

позволяющий или, в терминологии

специальный

задать

класс

Sys-

национальный

язык,

F r a m e w o r k , культуру (culture).

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

имя

12.1 мы привели имена и к о д ы н е к о т о р ы х к у л ь т у р . (Более п о л н ы й

с п и с о к имен и к о д о в к у л ь т у р вы найдете в п р и л о ж е н и и 2 к этой к н и г е . ) О б р а т и т е вни¬ м а н и е , что при с р а в н е н и и с т р о к s t r l и s t r 2 , с о д е р ж а щ и х с и м в о л ы к и р и л л и ц ы , м ы у к а з а л и р у с с к и й алфавит. Для этого мы п е р е д а л и о б о з н а ч е н и е р у с с к о г о а л ф а в и т а ru к о н с т р у к т о р у класса S y s t e m .

Латинский алфавит,

и с п о л ь з о в а н н ы й для с р а в н е н и я строк s t Таблица

12.1.

Имена

Национальный

и

язык,

коды

некоторых

страна

3 и s t r 4 , о б о з н а ч а е т с я как культур

или район

Имя

не у ч и т ы в а е т с я Английский

Код

культуры

0x007F 0x0009

Английский

(Канада)

0x1009

Английский (Объединенное Королевство)

en-GB

0x0809

Английский (Соединенные Штаты Америки)

en-US

0x0409

Арабский

0x0001

Белорусский Белорусский

(Беларусь)

Испанский Испанский

(Испания)

Итальянский Итальянский

(Италия)

Китайский Глава 12. Работа с текстовыми строками 13

культуры

(пустая с т р о к а )

Язык C # С а м о у ч и т е л ь

be

0x0023

be-BY

0x0423

es

OxOOOA

es-ES

OxOCOA

it

0x0010 0x0410 0x0804 385


язык,

страна

Имя

или район ко

Корейский

Код

культуры

0x0012 0x0412

(Корея) de

Немецкий

культуры

0x0007 0x0015

Польский

0x0415

(Польша)

Русский

ru

0x0019

Русский (Россия)

ru-RU

0x0419

Татарский

tt

0x0044

tt-RU

0x0444

Татарский

(Россия)

0x0043

Узбекский Узбекский (Узбекистан, кириллица)

Cy-uz-UZ

0x0843

Узбекский (Узбекистан, латиница)

Lt-uz-UZ

0x0443 0x0022

Украинский Украинский

(Украина)

Французский

uk-UA fr

Французский

(Франция)

fr-FR

Французский

(Швейцария)

fr-CH

Хинди Хинди

0x0422

hi

0x040C 0x0039 0x0439

(Индия)

0x0011 Японский

(Япония)

ja-JP

0x0411

Форматирование текстовых строк Одна и з в а ж н е й ш и х з а д а ч , к о т о р у ю п р и х о д и т с я р е ш а т ь п р о г р а м м и с т у при р а з р а б о т к е п р и л о ж е н и й л ю б о г о типа, — ф о р м а т и р о в а н и е т е к с т о в ы х с т р о к . С п о м о щ ь ю т е к с т о в ы х строк о б ы ч н о п р е д с т а в л я е т с я ч и с л о в а я и н ф о р м а ц и я , т а к а я , как н о м е р а з а к а з о в , коли¬ чество к а к и х - л и б о п р е д м е т о в , ц е н ы , д а т а , в р е м я и т. д. Если вы п р о г р а м м и р о в а л и раньше на я з ы к е С или С + + , то вам з н а к о м ы такие сред­ ства ф о р м а т и р о в а н и я , как ф у н к ц и и и а также управляющие симво­ лы в п о т о к а х в ы в о д а . Эти с р е д с т в а , с т а в ш и е с т а н д а р т н ы м и , п о з в о л я ю т п р е д с т а в и т ь числа различных типов практически в л ю б о м формате. Что же к а с а е т с я С # , то сам по себе этот я з ы к не с о д е р ж и т с р е д с т в ф о р м а т и р о в а н и я строк. О д н а к о б о г а т е й ш и е в о з м о ж н о с т и т а к о г о ф о р м а т и р о в а н и я п р е д о с т а в л я ю т с я п р о г р а м м и с т у в р а м к а х б и б л и о т е к и к л а с с о в Microsoft F r a m e w o r k . Мы ис­ пользовали некоторые средства форматирования, предоставляемые методом C o n 1е когда в ы в о д и л и в к о н с о л ь н о е о к н о ш е с т н а д ц а т е р и ч н ы е числа. А н а л о г и ч н о е ф о р м а т и р о в а н и е д о с т у п н о при ф о р м и р о в а н и и т е к с т о в ы х строк м е т о д о м String н е и м е ю щ и м н и к а к о г о о т н о ш е н и я к к о н с о л ь н о м у выводу. 386

А В Фролов. Г В Фролов. Язык С# Самоучитель


Так как д а н н ы е л ю б о г о типа в С# я в л я ю т с я о б ъ е к т а м и н е к о т о р ы х к л а с с о в , это от¬ к р ы в а е т н о в ы е в о з м о ж н о с т и для ф о р м а т н о г о п р е д с т а в л е н и я этих о б ъ е к т о в в виде тек¬ стовых строк. В этом разделе мы р а с с м о т р и м этот в о п р о с п р и м е н и т е л ь н о к форматно¬ му п р е д с т а в л е н и ю чисел. З а м е т и м , что б и б л и о т е к а классов F r a m e w o r k п о з в о л я е т легко р е ш и т ь и обрат¬ н у ю задачу — п р е о б р а з о в а н и е т е к с т о в ы х строк с ч и с л а м и в ч и с л о в ы е з н а ч е н и я . Это н е о б х о д и м о , н а п р и м е р , для о б р а б о т к и ч и с е л , в в е д е н н ы х п о л ь з о в а т е л я м и при п о м о щ и клавиатуры.

Представление целых чисел Ч т о б ы п р е о б р а з о в а т ь ц е л о ч и с л е н н о е з н а ч е н и е в т е к с т о в у ю строку с ф о р м а т и р о в а н и е м при п о м о щ и м е т о д а F o r m a t , н е о б х о д и м о задать этому методу так н а з ы в а е ­ м у ю строку формата (format а т а к ж е п е р е д а т ь в к а ч е с т в е п а р а м е т р о в о д н о или н е с к о л ь к о п р е о б р а з у е м ы х з н а ч е н и й . В ответ д а н н ы й м е т о д в о з в р а т и т о т ф о р м а т и р о ¬ ванную строку. П о п р и н ц и п у и с п о л ь з о в а н и я метод S t r i n g . F o r m a t б о л ь ш е всего п о х о ж н а ф у н к ­ цию

з н а к о м у ю всем п р о г р а м м и с т а м С .

Строка формата задается методу

F o r m a t в с л е д у ю щ е м виде:

N

М

Здесь

ч и с л о N задает н о м е р п р е о б р а з у е м о г о F o r m a t в качестве параметра.

аргумента,

передаваемого

методу

Необязательное число задает ш и р и н у области т е к с т о в о й строки (в с и м в о л а х ) , внутри к о т о р о й н е о б х о д и м о п о м е с т и т ь ц и ф р ы п р е о б р а з у е м о г о з н а ч е н и я . Е с л и это число о т р и ц а т е л ь н о е , ц и ф р ы ч и с л а в ы р а в н и в а ю т с я п о л е в о й г р а н и ц е д а н н о й о б л а с т и , а если п о л о ж и т е л ь н о е — по правой г р а н и ц е о б л а с т и . И наконец, строка задает к о д ы ф о р м а т и р о в а н и я , к о т о р ы е м ы с к о ­ р о р а с с м о т р и м . Для ф о р м а т и р о в а н и я ц е л ы х чисел и с п о л ь з у ю т с я э к в и в а л е н т н ы е к о д ы ф о р м а т и р о в а н и я d и D. Р а с с м о т р и м п р о г р а м м у , и с х о д н ы й текст к о т о р о й Эта п р о г р а м м а д е м о н с т р и р у е т п р и м е н е н и е р а з л и ч н ы х с п о с о б о в п р е д с т а в л е н и я ц е л ы х чисел в в и д е т е к с т о в ы х с т р о к . 12.18.

в л и с т и н г е 12.18. ф о р м а т и р о в а н и я для

Файл

using System; namespace StringFormat class static

void

int string

args) 777; result;

result

Глава

Работа с

строками

387


result

result

result

result

result

result

счастливое

Автоматическое форматирование В с а м о м п р о с т е й ш е м случае м о ж н о в о о б щ е не при этом будет и с п о л ь з о в а н ф о р м а т по у м о л ч а н и ю : int iSignedNumber string result;

указывать

код

форматирования,

777;

result В д а н н о м с л у ч а е на к о н с о л ь будет в ы в е д е н а с т р о к а 7 7 7 . А н а л о г и ч н о г о р е з у л ь т а т а м о ж н о б ы л о бы Добиться и с л е д у ю щ и м более п р о с т ы м о б р а з о м :

И м е н н о так мы в ы в о д и л и на к о н с о л ь д а н н ы е в п р и м е р а х п р о г р а м м , в нашей к н и г е . З а м е т и м , о д н а к о , что этот с п о с о б п о д х о д и т т о л ь к о для к о н с о л ь н ы х п р о г р а м м . Что же к а с а е т с я п р о г р а м м с о к о н н ы м п о л ь з о в а т е л ь с к и м и н т е р ф е й с о м , т о т а м , как п р а в и л о , н е о б х о д и м о п е р е д в ы в о д о м п р е о б р а з о в ы в а т ь ч и с л о в ы е значения в т е к с т о в ы е с т р о к и . Эта задача и р е ш а е т с я при п о м о щ и м е т о д а S t r i n g

Представление чисел в

формате

Если вам н у ж н о п р е д с т а в и т ь ц е л о ч и с л е н н о е з н а ч е н и е в ш е с т н а д ц а т е р и ч н о м ф о р м а т е , н е о б х о д и м о у к а з а т ь код ф о р м а т и р о в а н и я х или X: result

388

А

Г.

Фролов. Язык

Самоучитель


result

0x23fabc);

В п е р в о м случае ш е с т н а д ц а т е р и ч н о е число 0x23fabc б у д е т о т о б р а ж а т ь с я с и с п о л ь ­ з о в а н и е м н и ж н е г о р е г и с т р а , а во втором

верхнего:

23 При отображении

ч и с е л п р е ф и к с Ох не д о б а в л я е т с я , так что

вы м о ж е т е в ы д е л я т ь

числа л ю б ы м с п о с о б о м по в а ш е м у у с м о т р е ­

н и ю или не в ы д е л я т ь их вовсе.

Определение ш и р и н ы поля вывода Непосредственно после кода ф о р м а т и р о в а н и я вы м о ж е т е у к а з а т ь ш и р и н у поля в ы в о д а в с и м в о л а х . П р и этом если в ч и с л е м е н ь ш е ц и ф р , чем ш и р и н а поля в ы в о д а , то при ф о р м и р о в а н и и строки это ч и с л о будет д о п о л н е н о слева н у л я м и . Если ж е ц и ф р б о л ь ш е , чем з н а ч е н и е ш и р и н ы в ы в о д а , то будут в ы в е д е н ы все ц и ф р ы числа. Р а с с м о т р и м с л е д у ю щ и й п р и м е р , где м ы ф о р м а т и р у е м т р е х з н а ч н о е ч и с л о : int iSignedNumber string result;

777;

result

result

В р е з у л ь т а т е р а б о т ы этого ф р а г м е н т а п р о г р а м м ы на к о н с о л и п о я в я т с я с л е д у ю щ и е две с т р о к и : 777 00000777 Н е с м о т р я на то что в п е р в о м случае мы у к а з а л и в с т р о к е ф о р м а т а ш и р и н у п о л я , р а в н у ю д в у м , в п о л у ч е н н о й т е к с т о в о й с т р о к е п р и с у т с т в у ю т все ц и ф р ы и с х о д н о г о чис¬ ла. Таким о б р а з о м , ф о р м а т и р о в а н и е не м о ж е т и с к а з и т ь з н а ч е н и е числа. Во в т о р о м с л у ч а е ш и р и н а поля в ы в о д а с о с т а в л я е т 8 с и м в о л о в , но в ч и с л е т о л ь к о 3 значащих цифры.

П о э т о м у при ф о р м а т и р о в а н и и это число

было д о п о л н е н о слева

пятью нулями. Ш и р и н а поля в ы в о д а м о ж е т з а д а в а т ь с я и при о т о б р а ж е н и и числе в ш е с т н а д ц а т е р и ч н о й с и с т е м е счисления.

Выравнивание числа внутри поля вывода У к а з а в ш и р и н у поля в ы в о д а после н о м е р а а р г у м е н т а (через з а п я т у ю ) , м о ж н о выпол¬ нить в ы р а в н и в а н и е числа внутри этого п о л я . Глава 12. Работа с

строками

389


Ш и р и н а поля в ы в о д а м о ж е т з а д а в а т ь с я как п о л о ж и т е л ь н ы м и ч и с л а м и , так и отри¬ цательными: -

result

счастливое

число",

В первом и в т о р о м случаях мы у к а з а л и ш и р и н у поля в ы в о д а , р а в н у ю пяти симво¬ л а м . К о г д а это ч и с л о п о л о ж и т е л ь н о е , с и м в о л ы п р о б е л а д о б а в л я ю т с я слева, а когда от¬ р и ц а т е л ь н о е — справа: 777 777 Это

счастливое

число

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

Представление чисел с фиксированной десятичной точкой Для п р е д с т а в л е н и я чисел с ф и к с и р о в а н н о й д е с я т и ч н о й т о ч к о й и с п о л ь з у ю т с я равно¬ з н а ч н ы е к о д ы ф о р м а т а d и D. В с л е д за к о д о м ф о р м а т а о б ы ч н о у к а з ы в а ю т н е о б х о д и м о е к о л и ч е с т в о з н а к о в после д е с я т и ч н о й т о ч к и . П р и м е р п р о г р а м м ы ф о р м а т н о г о в ы в о д а чисел с ф и к с и р о в а н н о й д е с я т и ч н о й т о ч к о й п р и в е д е н в л и с т и н г е 12.19. Листинг

12.19.

Файл

using System; namespace Decimal class

DecimalApp

static

void

double string

args)

pi 3.1415926; result;

result

result

result

result

390

pi);

А В

Г

Фролов Язык С#


result

pi);

result

pi);

Ф о р м а т ПО у м о л ч а н и ю П р е ж д е всего н а ш а п р о г р а м м а в ы в о д и т з н а ч е н и е ч и с л а тирование по умолчанию: double string

и с п о л ь з у я для этого форма¬

pi 3.1415926; result;

result

Д а н н о е з н а ч е н и е будет в ы в е д е н о на к о н с о л ь в с л е д у ю щ е м виде:

М о ж н о и с п о л ь з о в а т ь т о л ь к о код ф о р м а т а , не у к а з ы в а я к о л и ч е с т в о з н а к о в п о с л е де¬ сятичной точки: result

В этом с л у ч а е , о д н а к о , з н а ч е н и е м о ж е т быть о к р у г л е н о : 3 , 14 Как

результат,

точность

отображаемого

значения

может

оказаться

недоста¬

точной.

У к а з а н и е к о л и ч е с т в а знаков после д е с я т и ч н о й т о ч к и Для п о л у ч е н и я п р е д с к а з у е м о г о р е з у л ь т а т а мы р е к о м е н д у е м всегда задавать необходи¬ мое к о л и ч е с т в о знаков после д е с я т и ч н о й т о ч к и : result

В этом п р и м е р е п о с л е запятой будет о т о б р а ж е н о 3 ц и ф р ы : 142

12.

с текстовыми

391


Ограничение ширины поля вывода Для о г р а н и ч е н и я ш и р и н ы поля в ы в о д а м о ж н о задать н е о б х о д и м о е п я т у ю после н о м е р а а р г у м е н т а м е т о д а

через за­

F o r m a t , как м ы это д е л а л и для целых

чисел: result

pi);

В р е з у л ь т а т е на консоль будет в ы в е д е н о т о л ь к о 5 с и м в о л о в н а ш е г о ч и с л а ( в к л ю ч а я десятичную запятую):

Извлечение целой части числа Если вам н у ж н о о т ф о р м а т и р о в а т ь при в ы в о д е ч и с л о с п л а в а ю щ е й д е с я т и ч н о й т о ч к о й т а к и м о б р а з о м , ч т о б ы о т о б р а з и т ь т о л ь к о ц е л у ю часть числа, у к а ж и т е к о л и ч е с т в о цифр после д е с я т и ч н о й т о ч к и , р а в н о е н у л ю : result

Этот ф р а г м е н т кода в ы в е д е т на к о н с о л ь ч и с л о 3.

Избыточная точность при выводе Если ш и р и н а п о л я в ы в о д а п р е в ы ш а е т к о л и ч е с т в о з н а ч а щ и х ц и ф р в числе с фиксиро¬ ванной д е с я т и ч н о й т о ч к о й , то такое число б у д е т д о п о л н е н о с п р а в а н е о б х о д и м ы м ко¬ личеством нулей. Н а п р и м е р , з д е с ь мы ф о р м а т и р у е м н а ш е ч и с л о для в ы в о д а в поле ш и р и н о й 10 сим¬

result

П р и этом к ч и с л у будет д о п и с а н о с п р а в а 3 нуля: 3,1415926000 О б р а т и т е в н и м а н и е , что т а к и м с п о с о б о м м ы н е у в е л и ч и л и т о ч н о с т ь п р е д с т а в л е н и я числа

а т о л ь к о д о б а в и л и к числу д о п о л н и т е л ь н ы е нули.

Представление чисел в научном формате Н а у ч н ы й , или э к с п о н е н ц и а л ь н ы й ,

формат обычно используется в научных расчетах

для п р е д с т а в л е н и я ч и с е л , л е ж а щ и х в б о л ь ш о м д и а п а з о н е з н а ч е н и й . Д л я форматирова¬ ния т а к и х ч и с е л п р и м е н я ю т с я коды ф о р м а т о в е и Е. П р и м е р п р о г р а м м ы , о т о б р а ж а ю щ е й на к о н с о л и о т ф о р м а т и р о в а н н ы е ч и с л а в науч¬ ном ф о р м а т е , п р е д с т а в л е н в л и с т и н г е 12.20.

392

А В. Фролов, Г. В. Фролов. Язык

Самоучитель


Листинг

12.20.

Файл

using System; namespace Science class static

void

double string result

args)

pi 0.31415926E1; result; pi)

result

result

Здесь мы будем в ы в о д и т ь з н а ч е н и е ч и с л а тс, п р е д с т а в л е н н о г о в н а у ч н о м ф о р м а т е : double

pi

Такие ч и с л а м о ж н о в ы в о д и т ь с и с п о л ь з о в а н и е м ф о р м а т а по у м о л ч а н и ю : result Если при э т о м р е з у л ь т а т м о ж н о будет п р е д с т а в и т ь в ф о р м а т е с ф и к с и р о в а н н о й де¬ с я т и ч н о й т о ч к о й , то он и б у д е т и с п о л ь з о в а н . В о т что вы у в и д и т е на к о н с о л и , з а п у с т и в нашу программу:

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

Глава

Работа с

393


Выделение тысяч при отображении больших чисел При б о л ь ш и х м н о г о з н а ч н ы х чисел для большей н а г л я д н о с т и часто отде¬ л я ю т д р у г от д р у г а р а з р я д ы тысяч при п о м о щ и п р о б е л а (по 3 р а з р я д а ) . Для п о д о б н о г о ф о р м а т и р о в а н и я п р и м е н я е т с я код ф о р м а т а п или N. Р а с с м о т р и м п р и м е р п р о г р а м м ы ( л и с т и н г 12.21). Листинг

Файл

using System; namespace

static

void

double string

args)

oneMByte result;

1 0 4 8 5 7 6;

result

result

result

Здесь мы с н а ч а л а у к а з ы в а е м код ф о р м а т а п, не д о п о л н я я его к о л и ч е с т в о м ц и ф р д р о б н о й части: double string

oneMByte result;

1048576;

result В р е з у л ь т а т е н а ш е ч и с л о ( к о л и ч е с т в о байтов в о д н о м м е г а б а й т е ) будет о т о б р а ж е н о следующим образом: 1

048

576,00

П р и н е о б х о д и м о с т и м ы м о ж е м у к а з а т ь н у ж н о е к о л и ч е с т в о ц и ф р после з а п я т о й : result

result

394

А В.

Г. В Фролов. Язык

Самоучитель


В первом случае мы указали 4 ц и ф р ы п о с л е з а п я т о й , а во в т о р о м — 3. В о т что по¬ л у ч и л о с ь после з а п у с к а п р о г р а м м ы : 1 1

048 048

576,0000 576,000

Универсальный формат для представления чисел В тех с л у ч а я х , когда р е з у л ь т а т в ы ч и с л е н и й д о л ж е н быть п р е д с т а в л е н в н а и б о л е е на¬ глядном в и д е , у д о б н е е и с п о л ь з о в а т ь у н и в е р с а л ь н ы й ф о р м а т . Код этого ф о р м а т а

g

или В з а в и с и м о с т и от т о г о , какое ф о р м а т и р у е т с я , система а в т о м а т и ч е с к и вы¬ бирает ф о р м а т с п л а в а ю щ е й д е с я т и ч н о й точкой или н а у ч н ы й формат. Р а с с м о т р и м п р о г р а м м у , и с х о д н ы й текст к о т о р о й п р и в е д е н в л и с т и н г е 12.22. Листинг

12.22.

using System; namespace General class

GeneralApp

static

void

double double string

args)

oneMByte small result;

1 0 4 8 5 7 6;

result

result

result

oneMByte)

result

small)

Здесь мы в ы в о д и м два числа, одно из к о т о р ы х по своей в е л и ч и н е дос¬ таточно большое, а другое — маленькое: double double string

oneMByte small result;

1 0 4 8 5 7 6;

Глава 12. Работа с текстовыми строками

395


result result При этом д л я п е р в о г о числа будет а в т о м а т и ч е с к и в ы б р а н ф о р м а т с п л а в а ю щ е й де¬ сятичной точкой, а для второго научный формат: 1048576 1, 2345е-06 При н е о б х о д и м о с т и м о ж н о о г р а н и ч и т ь т о ч н о с т ь о т о б р а ж а е м о г о з н а ч е н и я , указав к о л и ч е с т в о з н а ч а щ и х д р о б н ы х разрядов: result

result

small)

В о т р е з у л ь т а т р а б о т ы этого ф р а г м е н т а кода:

Формат для представления денежных сумм С п е ц и а л ь н о для в ы в о д а з н а ч е н и й д е н е ж н ы х сумм п р е д у с м о т р е н ы ф о р м а т ы с и

С.

П р и и с п о л ь з о в а н и и этих ф о р м а т о в ч и с л а в ы в о д я т с я с р а з д е л е н и е м р а з р я д о в на т ы с я ч и , а также с добавлением обозначения денежной единицы. Символ-разделитель, а также о б о з н а ч е н и е д е н е ж н о й е д и н и ц ы з а в и с я т от н а с т р о й к и л о к а л и з а ц и и в у п р а в л я ю щ е й па¬ нели ОС Microsoft W i n d o w s . Пример

программы,

отображающей

форматах, приведен в листинге Листинг

12.23.

на. к о н с о л и

денежную

сумму

в

различных

12.23.

Файл

using System; namespace Currency class

CurrencyApp

static void double string

args)

result;

result

396

А В Фролов, Г. В. Фролов. Язык

Самоучитель


result

result

Вот что эта п р о г р а м м а в ы в о д и т на к о н с о л ь : 1 1 1

048 048 048

576,25р. 576,250р.

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

Использование шаблонов при форматировании Ш а б л о н ы о т к р ы в а ю т перед п р о г р а м м и с т а м и д о п о л н и т е л ь н ы е в о з м о ж н о с т и формати¬ р о в а н и я чисел в п р о ц е с с е их п р е о б р а з о в а н и я в т е к с т о в ы е с т р о к и . Ш а б л о н ы с о з д а ю т с я с п о м о щ ь ю с и м в о л о в 0, % и точки с з а п я т о й . И с п о л ь з о в а н и е этих с и м в о л о в з а в и с и т о т с п о с о б а ф о р м а т и р о в а н и я .

Форматирование целых чисел П р и ф о р м а т и р о в а н и и ц е л ы х чисел с и м в о л 0 с о о т в е т с т в у е т с и м в о л у п р е о б р а з у е м о й строки или н у л е в о м у з н а ч е н и ю (если д а н н а я п о з и ц и я н е и с п о л ь з у е т с я ) . П о з и ц и я , в к о т о р о й стоит с и м в о л будет и г н о р и р о в а т ь с я , если в ш а б л о н е п е р е д ней не с т о и т 0 или д р у г о й с и м в о л . В п е р в о м с л у ч а е будет в ы в е д е н о н у л е в о е з н а ч е н и е или с и м в о л п р е о б р а з у е м о й с т р о к и , а во в т о р о м — д а н н а я п о з и ц и я будет п р о п у щ е н а или в ней б у д е т с и м в о л п р е о б р а з о в а н н о й с т р о к и . Р а с с м о т р и м п р о г р а м м у , о т о б р а ж а ю щ у ю н а к о н с о л и 7-значный т е л е ф о н н ы й н о м е р ( л и с т и н г 12.24). ( Т е л е ф о н н ы й н о м е р п р и в е д е н в ней т о л ь к о для п р и м е р а , так что не п ы т а й т е с ь по н е м у звонить!) Листинг

12.24.

Файл

using System; namespace class static

void

Глава 12. Работа с текстовыми строками

args)

397


u i n t number 9367733 string result;

только

для

result

result

result

В п е р в о м с л у ч а е мы в ы д е л я е м в т е л е ф о н н о м н о м е р е г р у п п ы ц и ф р , р а з д е л я я их де¬ фисом: result В о т что п о я в и т с я на к о н с о л и в р е з у л ь т а т е в ы п о л н е н и я этих с т р о к : 936-77-33 Если н у ж н о д о п о л н и т ь о т о б р а ж а е м о е целое число н у л я м и с левой с т о р о н ы , укажи¬ те н е о б х о д и м о е к о л и ч е с т в о нулей в л е в о й п о з и ц и и : result Т е п е р ь н а ш н о м е р будет д о п о л н е н слева о д н и м н у л е м : 0936-77-33 Если у к а з а т ь м е н ь ш е н у л е й , чем н а д о , ч и с л о все р а в н о будет в ы в е д е н о п о л н о с т ь ю : result Вот результат работы

фрагмента программы:

936-77-33

Форматирование чисел с плавающей десятичной точкой При чисел с десятичной точкой символы шаблона 0 и п р и м е н я ю т с я а н а л о г и ч н о т о м у , как они п р и м е н я ю т с я при ф о р м а т и р о в а н и и ц е л ы х чи¬ сел. Д о п о л н и т е л ь н о для в ы д е л е н и я т ы с я ч в ш а б л о н е п р и м е н я е т с я з а п я т а я . Мы приведем пример использования нестандартного форматирования для решения та­ кой задачи, как выделение тысяч при выводе чисел (листинг 12.25).

398

А В. Фролов, Г. В Фролов. Язык

Самоучитель


Листинг

12.25.

Файл

using System; namespace Template2 class static

void

double

args)

number

string

result;

result

Здесь мы у к а з а л и в ш а б л о н е м е ж д у с и м в о л а м и при в ы в о д е числа р а з р я д ы т ы с я ч

символ з а п я т о й , в р е з у л ь т а т е чего

будут в ы д е л е н ы . Н а ш а п р о г р а м м а в ы в о д и т ч и с л о

в и с х о д н о м и о т ф о р м а т и р о в а н н о м виде: 478

903

208

Обратите

236,568000 внимание,

В результате

наше

что

число

мы

задали

отображение шести

цифр

после запятой.

после округления было д о п о л н е н о тремя н е з н а ч а щ и м и

нулями.

Форматирование чисел с процентами Если к с т р о к е ш а б л о н а д о б а в и т ь с и м в о л п р о ц е н т а

то перед в ы в о д о м з н а ч е н и е

числа будет у в е л и ч е н о в 100 раз. К р о м е т о г о , будет в ы в е д е н и сам с и м в о л Этот п р и е м ф о р м а т и р о в а н и я д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст к о т о ­ рой п р и в е д е н в л и с т и н г е 12.26. Листинг

12.26.

Файл

using System; namespace TemplatePercent class

TemplatePercentApp

static

void

double string Глава 12. Работа с

number result;

args) 0.4756; строками

399


result

Вот что эта п р о г р а м м а в ы в е д е т на к о н с о л ь : 47, З а м е т и м , что в о д н о м ш а б л о н е м о ж е т в с т р е ч а т ь с я н е с к о л ь к о с и м в о л о в и з них будет д е й с т в о в а т ь о п и с а н н ы м в ы ш е о б р а з о м .

Каждый

Форматирование с учетом знака чисел Если для п о л о ж и т е л ь н ы х , о т р и ц а т е л ь н ы х и н у л е в ы х чисел н у ж н о и с п о л ь з о в а т ь разное ф о р м а т и р о в а н и е , вы м о ж е т е у к а з а т ь 3 ш а б л о н а , р а з д е л и в их т о ч к о й с з а п я т о й . П е р в ы й из этих ш а б л о н о в будет и с п о л ь з о в а т ь с я для в ы в о д а п о л о ж и т е л ь н ы х ч и с е л , второй — отрицательных, а третий — равных нулю. В л и с т и н г е 12.27 мы привели и с х о д н ы й т е к с т п р о г р а м м ы , д е м о н с т р и р у ю щ е й ис¬ пользование описанного выше форматирования. 12.27.

Файл

using System; namespace class static

void

string

args) result;

result result

0);

result

-10);

В о т что н а ш а п р о г р а м м а вывела на к о н с о л ь после запуска: плюс 0 минус Как венно.

мы смогли заменить с и м в о л ы

и - на слова п л ю с и м и н у с соответст¬

А В. Фролов, Г. В. Фролов. Язык

Самоучитель


Создание новых форматов Как вы объект л ю б о г о класса м о ж е т быть представлен в виде текстовой строки. Для э т о г о в классе н е о б х о д и м о определить м е т о д T o S t r i n g . Однако таким о б р а з о м м о ж н о получить только о д н о - е д и н с т в е н н о е текстовое представление объекта, что не Реализуя м е т о д T o S t r i n g интерфейса ваша программа сможет выбирать различные форматы для преобразования объектов в текстовые строки. Рассмотрим пример программы,

исходный текст которой представлен в л и с т и н ­

ге 12.28. Листинг

12.28.

Файл

ch

using System; namespace class

MyNumber:

IFormattable

public

uint

public

MyNumber ( u i n t

number public

n)

n;

string

ToString (string

format,

I Format P r o v i d e r

fp)

i return else return else return else return

class С static

number

void

MyNumber n Глава

fp)

args) new

Работа с текстовыми строками

(10241024)

401


String

В

result

result

null);

result

null);

этой

программе

class

мы

объявили

класс

реализующий

интерфейс

IFormattable

public public

MyNumber ( u i n t

number

public

n)

n

string

ToString (string

format,

fp)

В рамках э т о г о интерфейса мы определили м е т о д T o S t r i n g , которому п е р е д а ю т ­ ся д в а параметра — текстовая строка с о б о з н а ч е н и е м формата и ссылка на интерфейс провайдера формата I F o r m a t P r o v i d e r . Класс M y N u m b e r с о д е р ж и т также конструктор и поле n u m b e r для хранения целых чисел б е з В о т и с х о д н ы й текст м е т о д а T o S t r i n g , выполняющего т о или и н о е форматирова­ ние в з а в и с и м о с т и от строки формата, передаваемого в качестве первого параметра: public

string

ToString (string

format,

IFormatProvider

fp)

return

return

10);

lse return

402

Фролов, Г.

Фролов. Язык

Самоучитель


return

fp)

Преобразование vert

чисел

ToString.

в

текстовые

строки

выполняется

здесь

методом

Con­

В качестве п е р в о г о п а р а м е т р а этому м е т о д у передается п р е о б р а ­

з у е м о е ч и с л о , а в качестве второго

о с н о в а н и е ( д в о и ч н о е , д е с я т и ч н о е или ш е с т н а -

дцатеричное). Получив MyNumber

управление,

метод

Main

нашей

программы

создает

объект

класса

при п о м о щ и к о н с т р у к т о р а , а затем п р е о б р а з у е т его в т е к с т о в у ю строку

тремя

способами:

MyNumber n

new

String result

result

null);

result

null);

В

первом

случае з н а ч е н и е

числа

выводится

на к о н с о л ь

в

двоичном

формате,

во в т о р о м — в д е с я т и ч н о м ф о р м а т е , а в т р е т ь е м — в ш е с т н а д ц а т е р и ч н о м ф о р м а т е : 100111000100010000000000 10241024 9с4400 В т о м с л у ч а е , если ни один у к а з а н н ы й ф о р м а т не п о д о й д е т , наш м е т о д T o S t r i n g использует форматирование по умолчанию.

Преобразование текстовых строк в числа Очень часто перед программистом встает задача преобразования текстовых строк в числа. Для решения этой задачи проще всего воспользоваться методом P a r s e , предусмотрен­ ным с этой целью в классах числовых типов (таких, как I n t l 6

I n t 3 2 и т. д.).

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

приходится

проверять,

содержит

ли

такая

строка

только

цифры

или д р у г и е д о п у с т и м ы е с и м в о л ы . М е т о д P a r s e и з б а в и т вас о т этой н е о б х о д и м о с т и , так как при н е в о з м о ж н о с т и в ы п о л н и т ь п р е о б р а з о в а н и е о б р а з у е т с я и с к л ю ч е н и е S y s ¬ FormatException. И с п о л ь з о в а н и е метода P a r s e д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст к о т о ­ рой п р и в е д е н в л и с т и н г е Глава 12. Работа с

строками

403


Листинг

12.29.

Файл

using System; namespace Text2Number class static

void

int

args)

0;

число: input

try val ex) Ошибка:

число

val);

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

val ex)

404

А В.

Г. В. Фролов.

Самоучитель


Здесь м ы в ы з ы в а е м с т а т и ч е с к и й м е т о д I n t 3 2 P a r s e , передавая ему п р е о б р а з у е ­ м у ю т е к с т о в у ю строку. Если т е к с т о в у ю строку у д а е т с я п р е о б р а з о в а т ь в ч и с л о в о е зна¬ ч е н и е , метод в о з в р а щ а е т р е з у л ь т а т п р е о б р а з о в а н и я . В п р о т и в н о м случае в о з н и к а е т ис¬ ключение Если в о з н и к л о и с к л ю ч е н и е , н а ш а п р о г р а м м а в о з о б н о в л я е т работу цикла с с а м о г о начала. Вот пример сценария работы с программой: Введите Ввели ч и с л о Введите

123 123

число: Input

s t r i n g was

not

in

a

correct

С т р о к а 1 2 3 была у с п е ш н о п р е о б р а з о в а н а в ч и с л о

format. а попытка преобразования

в число строки q w e r t y з а в е р ш и л а с ь а в а р и й н о с в о з н и к н о в е н и е м и с к л ю ч е н и я tem.

Sys­

FormatException.

Глава 1 2 . Работа с текстовыми строками

405


Глава 13. Контейнеры для хранения объектов При создании п р о г р а м м самого р а з н о г о н а з н а ч е н и я часто п р и х о д и т с я р е ш а т ь задачу создания н а б о р о в д а н н ы х , п о з в о л я ю щ и х х р а н и т ь д а н н ы е в с т р у к т у р и р о в а н н о м виде. П р о с т е й ш и м п р и м е р о м такого набора д а н н ы х м о ж е т п о с л у ж и т ь о б ы к н о в е н н ы й сив. М а с с и в п о з в о л я е т х р а н и т ь о б ъ е к т ы о д н о г о и того же т и п а , а д р е с у я с ь к ним по н о ­ м е р а м , т. е. с и с п о л ь з о в а н и е м и н д е к с а Зная и н д е к с , вы м о ж е т е и з м е н и т ь или извлечь с о д е р ж и м о е л ю б о й я ч е й к и массива. Заметим, что, несмотря на удобство использования, обычным массивам присущи довольно существенные недостатки: •

р а з м е р массива нельзя и з м е н и т ь после т о г о , как м а с с и в был создан;

массив н а в я з ы в а е т у п о р я д о ч е н н о е р а с п о л о ж е н и е д а н н ы х , д а ж е если по своей внут¬ ренней природе данные неупорядоченны;

для поиска д а н н ы х в массиве необходимо последовательно перебрать все его ячейки.

Р а з у м е е т с я , эти н е д о с т а т к и будут сказываться д а л е к о не всегда. О д н а к о в ряде слу¬ чаев д а н н ы е н а м н о г о у д о б н е е х р а н и т ь с п о м о щ ь ю д р у г и х с р е д с т в , в ч а с т н о с т и с по¬ м о щ ь ю так н а з ы в а е м ы х контейнеров. К о н т е й н е р ы п р е д с т а в л я ю т собой к л а с с ы , ально п р е д н а з н а ч е н н ы е для у п о р я д о ч е н н о г о х р а н е н и я д а н н ы х р а з л и ч н ы х т и п о в . В т е р м и н а х я з ы к а С# такие к о н т е й н е р ы н а з ы в а ю т с я наборами данных (collections), од­ нако в н а ш е й к н и г е мы будем и с п о л ь з о в а т ь более п р и в ы ч н ы й т е р м и н контейнер.

Контейнеры в библиотеке классов Framework Б и б л и о т е к а классов Microsoft F r a m e w o r k с о д е р ж и т н е с к о л ь к о к л а с с о в , позво¬ л я ю щ и х создавать к о н т е й н е р ы с л е д у ю щ и х т и п о в :

массив

словарь

is Hashtable,

сортированный •

стек

очередь Q u e u e ,

список

Stack,

массив

BitArray.

Д а л е е в этой главе мы п о д р о б н о р а с с к а ж е м вам об и с п о л ь з о в а н и и этих контейне¬ ров и п р и в е д е м п р и м е р ы п р о г р а м м .

406


Массив ArrayList Как м ы у ж е г о в о р и л и , о б ы ч н ы е м а с с и в ы о б л а д а ю т н е д о с т а т к а м и , н е с к о л ь к о о г р а н и ч и в а ю щ и м и и х п р и м е н е н и е . Н и ж е м ы р а с с м о т р и м п р е и м у щ е с т в а использо¬ вания м а с с и в а ArrayList, а также п р и в е д е м п р и м е р ы п р о г р а м м , р а б о т а ю щ и х с та¬ кими м а с с и в а м и .

Создание массива М а с с и в A r r a y L i s t создается о б ы ч н ы м о б р а з о м , как и о б ъ е к т л ю б о г о д р у г о г о клас¬ са, — с п о м о щ ь ю к о н с т р у к т о р а . Н а п р и м е р , здесь мы создали м а с с и в A r r a y L i s t myWords

new

Это один и з трех в о з м о ж н ы х к о н с т р у к т о р о в класса A r r a y L i s t . В т о р о й в а р и а н т к о н с т р у к т о р а п о з в о л я е т при создании массива задать его началь¬ ный р а з м е р : A r r a y L i s t myWords

new A r r a y L i s t

Здесь сразу п о с л е с о з д а н и я м а с с и в m y W o r d s будет с о д е р ж а т ь 10 я ч е е к (по у м о л ч а ­ н и ю создается м а с с и в р а з м е р о м 16 я ч е е к ) . З а м е т и м , что при н е о б х о д и м о с т и по мере д о б а в л е н и я э л е м е н т о в р а з м е р т а к о г о м а с с и в а будет а в т о м а т и ч е с к и у в е л и ч и в а т ь с я . При с о з д а н и и массива с п о м о щ ь ю этого к о н с т р у к т о р а в о з м о ж н о п о я в л е н и е исклю¬ чения О н о будет с о з д а н о в том с л у ч а е , если п о п ы т а т ь с я задать о т р и ц а т е л ь н ы й р а з м е р с о з д а в а е м о г о м а с с и в а . И н а к о н е ц , т р е т и й к о н с т р у к т о р п р е д н а з н а ч е н для с о з д а н и я м а с с и в о в A r r a y L i s t на базе у ж е A r r a y L i s t myWords ArrayList

new new

Если п е р е д а т ь этому к о н с т р у к т о р у с с ы л к у , с о д е р ж а щ у ю пустое з н а ч е н и е n u l l , возникнет исключение П р о г р а м м а , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 13.1, д е м о н с т р и р у е т п р о с т е й ш и й способ с о з д а н и я м а с с и в а класса A r r a y L i s t , д о б а в л е н и я в него н о в ы х э л е м е н т о в , а т а к ж е способ и з в л е ч е н и я э л е м е н т о в м а с с и в а . Листинг using using

13.1.

Файл

System;

namespace class

ArrayListDemoApp

static

void

A r r a y L i s t myWords

Глава 13. Контейнеры для хранения

args) new

407


О б р а т и т е в н и м а н и е , что для р а б о т ы с к о н т е й н е р а м и мы п о д к л ю ч и л и к нашей про¬ г р а м м е п р о с т р а н с т в о имен using В д а л ь н е й ш е м мы е щ е в е р н е м с я к и с х о д н о м у тексту этой п р о г р а м м ы .

Добавление элементов в массив Для д о б а в л е н и я э л е м е н т о в в м а с с и в A r r a y L i s t п р е д н а з н а ч е н м е т о д A d d . В к а ч е с т в е е д и н с т в е н н о г о п а р а м е т р а этот м е т о д п о л у ч а е т ссылку н а д о б а в л я е м ы й э л е м е н т . П о с л е д о б а в л е н и я метод A d d в о з в р а щ а е т и н д е к с н о в о г о э л е м е н т а в м а с с и в е . В п р о г р а м м е , и с х о д н ы й текст к о т о р о й был п р и в е д е н в л и с т и н г е 13.1, мы в о с п о л ь ­ з о в а л и с ь этим м е т о д о м для н а ч а л ь н о й и н и ц и а л и з а ц и и м а с с и в а , д о б а в и в в него 4 эле¬ мента: C#

Так как и н д е к с ы

э л е м е н т о в , в о з в р а щ а е м ы е м е т о д о м A d d , нам были н е н у ж н ы ,

мы проигнорировали возвращаемое значение. О б р а т и т е в н и м а н и е , что при с о з д а н и и м а с с и в а A r r a y L i s t м ы н е у к а з ы в а е м тип о б ъ е к т о в , к о т о р ы е он будет с о д е р ж а т ь . Это очень в а ж н ы й м о м е н т

подобно другим

к о н т е й н е р а м , р а с с м о т р е н н ы м в этой г л а в е , м а с с и в A r r a y L i s t м о ж е т с о д е р ж а т ь лю¬ бые

п р о и з в о д н ы е о т класса o b j e c t . А так как о т этого к л а с с а о б р а з у ю т с я

все о б ъ е к т ы , т о это о з н а ч а е т , что м а с с и в A r r a y L i s t м о ж е т х р а н и т ь э л е м е н т ы л ю б о г о типа. З а м е т и м , что м а с с и в A r r a y L i s t м о ж е т быть создан в т а к о м р е ж и м е , когда для не¬ го д о п у с к а е т с я т о л ь к о о п е р а ц и я ч т е н и я , но не з а п и с и или и з м е н е н и я к о л и ч е с т в а эле¬ м е н т о в . При п о п ы т к е д о б а в и т ь м е т о д о м A d d н о в ы й э л е м е н т в м а с с и в , для к о т о р о г о допустима

только

операция

чтения,

возникает

исключение

NotSupportedEx-

ception.

408

А

Фролов, Г.

Фролов. Язык С# Самоучитель


Чтение элементов массива Элементы массива можно читать с помощью специальных курсоров (ука­ зателей или итераторов), а также более привычным с п о с о б о м , адресуясь к ним по ин­ дексу с помощью квадратных скобок.

Использование итераторов Для последовательного извлечения всех элементов массива можно создать в програм­ ме специальный итератор в виде объекта класса rator.

Этот итератор н е о б х о д и м о проинициализировать при

помощи метода G e t -

E n u m e r a t o r , вызванного для обрабатываемого контейнера: myEnumerator Здесь

в

переменной

myEnumerator

будет

храниться

итератор

для

массива

Для перемещения итератора в направлении от начала массива к его концу необхо¬ димо использовать метод M o v e N e x t .

При этом текущий элемент массива (т. е. тот,

на который указывает итератор) можно будет извлечь с помощью свойства C u r r e n t :

В результате работы этого цикла на консоль будет выведено полное с о д е р ж и м о е массива. Для установки итератора

I E n u m e r a t o r в исходное

тояние нужно использовать метод R e s e t .

Использование индексов Более привычный с п о с о б извлечения элементов массива A r r a y L i s t , не предпола¬ гающий применения курсоров, демонстрируется в программе, исходный текст которой представлен в листинге 13.2. 13.2.

Листинг using using

Файл

System;

namespace

static

void

A r r a y L i s t myWords

Глава

args) new

Контейнеры для хранения объектов

409


C#

i

i

i++)

Здесь п р и м е н я е т с я к л а с с и ч е с к о е и н д е к с и р о в а н и е массива при п о м о щ и к в а д р а т н ы х с к о б о к , п р и ч е м р а з м е р массива о п р е д е л я е т с я с п о м о щ ь ю с в о й с т в а C o u n t : i

0;

i

i++)

Изменение элементов массива Для и з м е н е н и я э л е м е н т о в м а с с и в а м о ж н о и с п о л ь з о в а т ь о б ы ч н ы е к в а д р а т н ы е с к о б к и . З а м е т и м , о д н а к о , что с л е д у ю щ и й ф р а г м е н т кода работать н е б у д е т : A r r a y L i s t myWords new A r r a y L i s t ( 4 ) "Hello"; C# myWords "World"; В р е з у л ь т а т е его р а б о т ы в о з н и к н е т и с к л ю ч е н и е RangeExcept i o n . П р е ж д е чем и з м е н я т ь с о д е р ж и м о е э л е м е н т о в м а с с и в а , и х н е о б х о д и м о д о б а в и т ь в массив методом Add. Т е х н и к а и з м е н е н и я д о б а в л е н н ы х ранее э л е м е н т о в м а с с и в а с и с п о л ь з о в а н и е м квад¬ р а т н ы х с к о б о к д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й вы н а й д е т е в л и с т и н г е 13.3. Файл using using

System;

namespace class

ItemDemolApp

static

void

ArrayList

args) myWords

new

Фролов, Г.

Язык С#. Самоучитель


(int i

0;

i

myWords

i+ + )

мир "языка

for(int

i

0;

i

i+ + )

Здесь мы в н а ч а л е м а с с и в и в ы в е л и его с о д е р ж и м о е на кон¬ с о л ь . Затем с о д е р ж и м о е к а ж д о й я ч е й к и м а с с и в а б ы л о и з м е н е н о , п о с л е чего п р о г р а м м а в н о в ь в ы в е л а массив на к о н с о л ь . Так как с в о й с т в о и т е р а т о р а C u r r e n t д о с т у п н о т о л ь к о д л я ч т е н и я , с его п о м о щ ь ю в ы н е с м о ж е т е и з м е н и т ь с о д е р ж и м о е я ч е е к массива. С л е д у ю щ а я п о п ы т к а д о б а в л е н и я к к а ж д о й т е к с т о в о й с т р о к е , х р а н я щ е й с я в м а с с и в е , с и м в о л а п о д ч е р к и в а н и я будет пре¬ с е ч е н а еще н а этапе к о м п и л я ц и и и с х о д н о г о т е к с т а п р о г р а м м ы : I E n u m e r a t o r myEnumerator

и текущий размер массива Н а и б о л е е серьезный н е д о с т а т о к о б ы ч н о г о м а с с и в а A r r a y — н е в о з м о ж н о с т ь измене¬ ния р а з м е р а м а с с и в а после его с о з д а н и я . Что же к а с а е т с я м а с с и в о в , с о з д а н н ы х на базе класса A r r a y L i s t , то их р а з м е р у в е л и ч и в а е т с я а в т о м а т и ч е с к и по мере д о б а в л е н и я в него н о в ы х о б ъ е к т о в . Для того чтобы у с т а н о в и т ь н а ч а л ь н ы й р а з м е р м а с с и в а A r r a y L i s t , в ы м о ж е т е и с ­ пользовать свойство A r r a y L i s t Т е к у щ и й размер м а с с и в а д о с т у п е н ч е ­ рез с в о й с т в о Count. Глава

для хранения


Когда в увеличения размеров массива значение дос­ тигает значения A r r a y L i s t размер массива автоматически увеличива­ ется. При этом автоматически увеличивается объем памяти, полученной у ОС для хра­ нения массива. Таким образом, значение A r r a y L i s t всегда больше зна­ чения Сказанное демонстрируется в программе, исходный текст которой показан в лис­ тинге 13.4. Листинг using using

13.4.

Файл

System;

namespace class

Capacity CapacityApp

static

void

args)

A r r a y L i s t myWords

new

C#

элементов:

емкость

массива

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

Объединение массивов Помимо автоматического увеличения размера массив A r r a y L i s t предоставляет про¬ граммистам и другие возможности. В частности, программа может добавлять, встав­ лять или удалять как отдельные элементы массива, так и наборы таких элементов. На¬ помним, что обычный массив допускает работу только с одним элементом массива, причем программа может либо получить, либо изменить хранящееся там В листинге 13.5 мы показали способ слияния двух массивов при помощи метода AddRange.

А

Г. В. Фролов. Язык

Самоучитель


13.5.

Листинг

Файл

System; using namespace class

AddRangeApp

static

void

args)

A r r a y L i s t myWords

new

C#

ArrayList

new

ArrayListO;

элементов:

емкость

массива

myEnumerator

Массивы создаются обычным образом A r r a y L i s t myWords

с помощью конструкторов и метода Add:

new

C#

A r r a y L i s t myWordsl

Глава

new

для хранения

413


Далее мы д о б а в л я е м в п е р в ы й массив все э л е м е н т ы из второго м а с с и в а , как это по¬ казано н и ж е :

П о д о б н ы м о б р а з о м м о ж н о о б ъ е д и н я т ь не только м а с с и в ы , но и д р у г и е к о н т е й н е р ы .

Удаление элементов из массива П о с л е с о з д а н и я о б ы ч н о г о м а с с и в а A r r a y в ы н е м о ж е т е у д а л и т ь и з него существую¬ щие э л е м е н т ы . Е д и н с т в е н н ы й с п о с о б в ы п о л н е н и я такой о п е р а ц и и з а к л ю ч а е т с я в соз¬ дании нового м а с с и в а и п о с л е д у ю щ е м п е р е п и с ы в а н и и в него всех э л е м е н т о в , кроме у д а л я е м ы х . С т о и т ли г о в о р и т ь , что этот способ д а л е к о не с о в е р ш е н е н . Что же касается м а с с и в о в A r r a y L i s t , то для них вы м о ж е т е и с п о л ь з о в а т ь не¬ сколько методов удаления элементов: • •

метод C l e a r у д а л я е т все э л е м е н т ы и з массива, в ы п о л н я я его п о л н у ю о ч и с т к у ; с п о м о щ ь ю м е т о д а R e m o v e п р о г р а м м а м о ж е т у д а л и т ь из м а с с и в а л ю б о й з а д а н н ы й элемент; метод RemoveAt п р е д н а з н а ч е н для у д а л е н и я э л е м е н т а , з а д а н н о г о с в о и м и н д е к с о м ; метод

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

с к о л ь к о э л е м е н т о в массива, р а с п о л о ж е н н ы х р я д о м . И с п о л ь з о в а н и е всех п е р е ч и с л е н н ы х в ы ш е м е т о д о в д е м о н с т р и р у е т с я в п р о г р а м м е , исходный текст которой приведен в листинге Листинг using using

13.6.

Файл System;

namespace

static

Remove

void

A r r a y L i s t myWords

args) new

емкость myWords

массива

myWords myEnumerator

А В.

Г. В Фролов. Язык

Самоучитель


элементов:

емкость

массива

элементов:

емкость

массива

элементов:

емкость

массива

Глава 13. Контейнеры для хранения объектов


В н а ч а л е н а ш а п р о г р а м м а создает и с х о д н ы й м а с с и в , д о б а в л я е т в м е н т ы , а затем о т о б р а ж а е т на к о н с о л и р а з м е р м а с с и в а и его myWords

myWords

new

C o u n t , myWords

элементов: Capacity)

System.

емкость

массива

myEnumerator

Далее «C#

новые эле­

наша

программа

ищет

в

массиве

первый

элемент,

содержащий

строку

и у д а л я е т его при п о м о щ и м е т о д а R e m o v e :

Далее

содержимое

измененного

массива отображается

на консоли

при

помощи

итератора Console

("Всего

System. C o l l e c t i o n s

элементов:

(0),

емкость

массива

IEnumerator myEnumeratorl

Обратите внимание, что здесь нам пришлось создать новый итератор m y E n u m e r a t o r l , так как р а з м е р ы м а с с и в а были и з м е н е н ы . В р е з у л ь т а т е итератор с о з д а н н ы й ранее для э т о г о м а с с и в а , стал б е с п о л е з н ы м . В дальней¬ ш е м при к а ж д о м и з м е н е н и и м а с с и в а наша п р о г р а м м а создает для его о т о б р а ж е н и я но¬ вый и т е р а т о р . Н а с л е д у ю щ е м шаге п р о г р а м м а у д а л я е т все э л е м е н т ы и з м а с с и в а м е т о д о м C l e a r , а потом заполняет з а н о в о при п о м о щ и метода A d d :

А В

Г. В Фролов. Язык

Самоучитель


Далее из восстановленного таким способом массива удаляется элемент с индек­ сом 1 (т. е. строка « С #

В качестве единственного параметра этому методу нужно передать индекс удаляе¬ мого элемента. Содержимое массива вновь отображается на консоли при помощи итератора И наконец, программа удаляет из массива два элемента, начиная с элемента, имеющего индекс 1, для чего она вызывает метод RemoveRange:

Через первый параметр методу R e m o v e R a n g e нужно передать индекс первого удаляемого элемента, а в качестве второго — количество удаляемых элементов.

Запрет записи данных в массив При необходимости с помощью программа может создать массив A r r a y L i s t дос­ тупным только для чтения. Эта возможность отсутствует в обычных массивах, реали¬ зованных как с применением класса A r r a y и языка программирования С#, так и с применением других языков программирования. Создание массива, доступного только для чтения, демонстрируется в программе, исходный текст которой представлен в листинге 13.7. Листинг using using

13.7.

Файл

System;

namespace class

Readonly ReadOnlyApp

static

void

args)

A r r a y L i s t myWords

new

C#

ArrayList

"Исходный

м а с с и в myWords

доступен

только

для

else "Исходный м а с с и в myWords д о с т у п е н для чтения и Глава 13. Язык

Самоучитель

для

объектов

417


"Массив

доступен

только

"Массив m y R e a d O n l y W o r d s

доступен

для

для

else

catch

(Exception

чтения

и

ex)

В н а ч а л е мы с о з д а е м о б ы ч н ы й м а с с и в , д о с т у п н ы й для ч т е н и я и з а п и с и : A r r a y L i s t myWords

new A r r a y L i s t

Д а л е е этот м а с с и в и н и ц и а л и з и р у е т с я о б ы ч н ы м о б р а з о м с п о м о щ ь ю м е т о д а A d d : C#

К с о ж а л е н и ю , мы не м о ж е м сделать с о з д а н н ы й т а к и м с п о с о б о м м а с с и в д о с т у п н ы м т о л ь к о для ч т е н и я , о д н а к о есть д р у г а я в о з м о ж н о с т ь . М ы м о ж е м с о з д а т ь к о п и ю н а ш е г о массива, д о с т у п н у ю т о л ь к о для ч т е н и я . Эта о п е р а ц и я в ы п о л н я е т с я при п о м о щ и м е т о д а ArrayList

myReadOnlyWords

Readonly

В р е з у л ь т а т е будет создан м а с с и в m y R e a d O n l y W o r d s , д о с т у п н ы й т о л ь к о для чте¬ ния. П р о г р а м м а не с м о ж е т д о б а в л я т ь в него н о в ы е э л е м е н т ы , а т а к ж е и з м е н я т ь содер¬ жимое существующих элементов. В классе A r r a y L i s t о п р е д е л е н о с в о й с т в о I s R e a d O n l y , п о з в о л я ю щ е е опреде¬ л и т ь , в о з м о ж н о л и и з м е н е н и е с о д е р ж и м о г о м а с с и в а , или этот массив д о с т у п е н т о л ь к о для з а п и с и . В о т как мы п р о в е р я е м это с в о й с т в о д л я и с х о д н о г о м а с с и в а :

массив

myWords

доступен

только

для

else "Исходный м а с с и в myWords

д о с т у п е н для

чтения и

Фролов, Г. В. Фролов. Язык С#. Самоучитель


А н а л о г и ч н а я о п е р а ц и я в ы п о л н я е т с я и для н о в о г о массива, д о с т у п н о г о т о л ь к о для

myReadOnlyWords

доступен

только

"Массив m y R e a d O n l y W o r d s

доступен

для

для

else чтения

и

Д а л е е наша п р о г р а м м а в ы п о л н я е т п о п ы т к у д о б а в л е н и я н о в о г о э л е м е н т а в м а с с и в , д о с т у п н ы й т о л ь к о д л я ч т е н и я . Так как эта о п е р а ц и я о б я з а т е л ь н о в ы з о в е т и с к л ю ч е н и е , мы предусмотрели соответствующий обработчик: try

catch

(Exception

ex) исключение:

В о т что п о я в и т с я на к о н с о л и после запуска п р о г р а м м ы : Исходный м а с с и в myWords д о с т у п е н д л я ч т е н и я и з а п и с и Массив m y R e a d O n l y W o r d s д о с т у п е н т о л ь к о д л я ч т е н и я Возникло исключение: Collection at at

args) 30

in

is

c#

Как в и д и т е , п о п ы т к а и з м е н е н и я м а с с и в а , д о с т у п н о г о т о л ь к о для ч т е н и я , п р и в е л а к возникновению исключения

NotSupportedException.

Ограничение размера массива Если вам н у ж н ы все с в о й с т в а м а с с и в а A r r a y L i s t , к р о м е а в т о м а т и ч е с к о г о у в е л и ч е ­ ния его р а з м е р а , то такая в о з м о ж н о с т ь т а к ж е п р е д у с м о т р е н а . П р и н е о б х о д и м о с т и п р о г р а м м а м о ж е т з а ф и к с и р о в а т ь размер м а с с и в а ArrayList, п р е д о т в р а т и в его а в т о ­ м а т и ч е с к и й рост ( л и с т и н г 13.8). 13.8.

Листинг using using

ch

cs

System;

namespace class

Глава

Файл

FixedSize FixedSizeApp

Контейнеры

хранения объектов

419


static

void

args)

A r r a y L i s t myWords

new

C#

ArrayList

myFixedSizeWords

"Исходный

массив

myWords

имеет

фиксированный

else "Допускается

"Массив

изменение

myReadOnlyWords

размеров

имеет

массива

фиксированный

else "Допускается

изменение

размеров

массива

try

catch

(Exception

ex) исключение:

Здесь мы в н а ч а л е с о з д а е м и и н и ц и а л и з и р у е м массив m y W o r d s , а затем создаем н а его базе м а с с и в m y F i x e d S i z e W o r d s , и м е ю щ и й ф и к с и р о в а н н ы й р а з м е р : A r r a y L i s t myWords

ArrayList

new

myFixedSizeWords

Такой массив создастся при помощи метода F i x e d S i z e .

420

А В.

Г. В Фролов. Язык С# Самоучитель


Программа может определить, фиксирован ли размер массива, проверив свойство IsFixedSize:

"Исходный

массив

myWords

имеет

фиксированный

else "Допускается

изменение

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

размеров

массива с фиксированным размером наша

новый элемент:

try

catch

(Exception

ex) исключение:

Эта п о п ы т к а н е и з б е ж н о п р и в е д е т к в о з н и к н о в е н и ю и с к л ю ч е н и я S u p p o r t e d E x c e p t i o n , о чем м о ж н о с у д и т ь п о и н ф о р м а ц и и , о т о б р а ж а е м о й н а ш е й п р о г р а м м о й на к о н с о л и : Д о п у с к а е т с я и з м е н е н и е р а з м е р о в м а с с и в а myWords Массив m y R e a d O n l y W o r d s и м е е т ф и к с и р о в а н н ы й р а з м е р Возникло исключение: C o l l e c t i o n was of a fixed size. at at args) in c# 30

Сортировка М а с с и в ы класса A r r a y L i s t , так ж е как и м а с с и в ы класса A r r a y , у д о б н о сортиро¬ вать. С п е ц и а л ь н о д л я этого п р е д у с м о т р е н о н е с к о л ь к о п е р е г р у ж е н н ы х в а р и а н т о в мето¬ да Sort. В простейшем случае сортировка массива выполняется с п о м о щ ь ю метода S o r t , н е и м е ю щ е г о п а р а м е т р о в . П р и м е р п р о г р а м м ы , с о р т и р у ю щ е й массив п о д о б н ы м обра¬ з о м , п р и в е д е н в л и с т и н г е 13.9. Листинг

13.9.

using using

System;

namespace class

Sort SortApp

static Глава

Файл

void

args) хранения объектов

421


A r r a y L i s t myWords

new

myEnumerator

System. C o l l e c t i o n s

IEnumerator myEnumeratorl

Получив управление, наша программа создает массив, записывая в него слова из­ вестной фразы, п о м о г а ю щ е й запомнить 7 цветов радуги: A r r a y L i s t myWords

new

Далее мы отображаем с о д е р ж и м о е массива с помощью итератора и сортируем мас­ сив, вызывая м е т о д S o r t без параметров:

После сортировки с о д е р ж и м о е массива отображается вновь: каждый о х о т н и к ж е л а е т з н а т ь г д е г д е ж е л а е т з н а т ь каждый о х о т н и к 422

сидит сидит А

фазан

Фролов, Г.

Фролов. Язык


Как в и д и т е , с о р т и р о в к а т е к с т о в ы х строк с с и м в о л а м и к и р и л л и ц ы была в ы п о л н е н а правильно. К а к и м образом о с у щ е с т в л я е т с я с о р т и р о в к а м а с с и в а ? Для этого п р и м е н я е т с я а л г о р и т м б ы с т р о й с о р т и р о в к и Q u i c k s o r t .

Сравнение эле­

ментов выполняется с использованием интерфейса I C o m p a r a b l e , в рамках которого о п р е д е л е н метод М е т о д у C o m p a r e T o п е р е д а е т с я один п а р а м е т р

с с ы л к а на о б ъ е к т , с р а в н и в а е м ы й

с т е к у щ и м о б ъ е к т о м . В з а в и с и м о с т и от р е з у л ь т а т а с р а в н е н и я м е т о д C o m p a r e T o воз­ в р а щ а е т о т р и ц а т е л ь н о е , н у л е в о е или п о л о ж и т е л ь н о е з н а ч е н и е . О т р и ц а т е л ь н о е значе¬ ние в о з в р а щ а е т с я , если т е к у щ и й о б ъ е к т м е н ь ш е , чем о б ъ е к т , ссылка на к о т о р ы й пере¬ д а е т с я методу C o m p a r e T o через п а р а м е т р , а п о л о ж и т е л ь н о е — если б о л ь ш е . В том с л у ч а е , когда объекты р а в н ы , м е т о д C o m p a r e T o в о з в р а щ а е т н у л е в о е з н а ч е н и е . Когда в ы с о р т и р у е т е м а с с и в , с о д е р ж а щ и й т о л ь к о в с т р о е н н ы е т и п ы д а н н ы х ( т а к и е , как числа и т е к с т о в ы е с т р о к и ) , м о ж н о и с п о л ь з о в а т ь в с т р о е н н у ю в эти т и п ы д а н н ы х реализацию интерфейса Если ж е с о р т и р у е т с я м а с с и в , с о д е р ж а щ и й с о з д а н н ы е вами о б ъ е к т ы , н е о б х о д и м о реализовать интерфейс I C o m p a r a b l e .

Ссылка на реализованный интерфейс

p a r a b l e д о л ж н а быть п е р е д а н а методу S o r t в качестве параметра. М о ж н о о т с о р т и р о в а т ь не весь м а с с и в , а т о л ь к о его часть. В этом случае необходи¬ мо и с п о л ь з о в а т ь третий вариант метода S o r t , п р и н и м а ю щ и й 3 параметра. В к а ч е с т в е п е р в о г о п а р а м е т р а этому м е т о д у н е о б х о д и м о п е р е д а т ь и н д е к с первого э л е м е н т а мас¬ сива, с к о т о р о г о н а ч и н а е т с я с о р т и р у е м ы й блок, а в качестве сортируемых элементов.

Третий

параметр должен

с о д е р ж а т ь ссылку н а и н т е р ф е й с

I C o m p a r a b l e л и б о н а з н а ч е н и е n u l l , если будет и с п о л ь з о в а н а р е а л и з а ц и я интер¬ фейса I C o m p a r a b l e о б ъ е к т о в , с о д е р ж а щ и х с я в м а с с и в е .

Обратное расположение элементов массива С п о м о щ ь ю метода R e v e r s e п р о г р а м м а м о ж е т и з м е н и т ь р а с п о л о ж е н и е э л е м е н т о в м а с с и в а на о б р а т н о е . Это д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и ­ веден в л и с т и н г е 13.10. Листинг using using

13.10.

Файл

System;

namespace class

Reverse ReverseApp

static

void

A r r a y L i s t myWords —

Глава 13. Контейнеры для хранения

args) ArrayList

423


myWords

Add IEnumerator myEnumerator

IEnumerator myEnumeratorl

От предыдущей программы, т о л ь к о одной с т р о к о й :

выполняющей

сортировку

массива,

она о т л и ч а е т с я

В н а ч а л е п р о г р а м м а в ы в о д и т на к о н с о л ь с о д е р ж и м о е и с х о д н о г о м а с с и в а , а затем м а с с и в а , э л е м е н т ы в к о т о р о м были п е р е с т а в л е н ы м е т о д о м R e v e r s e . В о т результат работы нашей программы: каждый о х о т н и к ж е л а е т фазан сидит где знать

знать где сидит фазан ж е л а е т о х о т н и к каждый

При н е о б х о д и м о с т и м о ж н о о б р а б о т а т ь м е т о д о м R e v e r s e т о л ь к о часть массива. Для этого н у ж н о и с п о л ь з о в а т ь п е р е г р у ж е н н у ю в е р с и ю этого м е т о д а , п р и н и м а ю щ у ю два п а р а м е т р а . п а р а м е т р задает и н д е к с п е р в о г о э л е м е н т а о б р а б а т ы в а е м о г о блока, а в т о р о й — э л е м е н т о в в блоке.

Поиск в массиве П о д о б н о м а с с и в а м A r r a y , р а с с м о т р е н н ы м нами в гл. 7 , м а с с и в ы A r r a y L i s t д о п у с ­ кают в ы п о л н е н и е б и н а р н о г о поиска э л е м е н т о в . Для этого в классе A r r a y L i s t опре¬ делен

424

метод

B i n a r y S e a r c h (нестатический).

В

качестве

п а р а м е т р а этому м е т о д у

А В. Фролов, Г. В Фролов. Язык

Самоучитель


нужно передать и с к о м ы й э л е м е н т . При у с п е х е метод в о з в р а т и т и н д е к с э л е м е н т а , а в т о м с л у ч а е , если э л е м е н т не н а й д е н , — о т р и ц а т е л ь н о е И с х о д н ы й текст п р о г р а м м ы , д е м о н с т р и р у ю щ и й метода B i n a r y S e a r c h для п о и с к а т е к с т о в о й с т р о к и в массиве класса A r r a y L i s t , п р и в е д е н в л и с ­ тинге 1 3 . 1 1 . 13.11.

Листинг using using

System;

namespace class

Search SearchApp

static

void

ArrayList

string int

args) myWords

new

searchString

"фазан";

index

if

0 не

else строки

равен

index

В п р о г р а м м е о б ъ я в л е н и п р о и н и ц и а л и з и р о в а н с т а т и ч е с к и массив т е к с т о в ы х строк myWords. М е т о д M a i n ищет в массиве слово string searchString int index

вызывая для этого метод B i n a r y S e a r c h ;

"фазан";

М е т о д B i n a r y S e a r c h п о л у ч а е т в качестве п а р а м е т р а ссылку н а и с к о м у ю строку. Если искомая с т р о к а н е н а й д е н а , м е т о д B i n a r y S e a r c h в о з в р а щ а е т о т р и ц а т е л ь н о е з н а ч е н и е . В случае у с п е х а в о з в р а щ а е т с я индекс н а й д е н н о й с т р о к и . Н а ш а п р о г р а м м а о т о б р а ж а е т на консоли и с к о м у ю строку и ее индекс. Глава 13. Контейнеры для хранения объектов

425


Работа в многопоточном режиме Для мах, Это ным ции

того чтобы использовать массив класса A r r a y L i s t в многопоточных програм¬ н е о б х о д и м о создать не его базе так называемый синхронизированный массив. можно сделать статическим методом предусмотрен­ для этой цели в классе Существует также возможность синхрониза­ многопоточных приложений при помощи свойства S y n c R o o t .

Применение метода Synchronized Один из самых простых с п о с о б о в работы с массивами A r r a y L i s t в многопоточных приложениях предполагает создание синхронизированных массивов методом S y n c h ¬ ronized. В листинге 13.12 мы привели исходный текст многопоточной программы, исполь­ з у ю щ е й этот м е т о д на практике. Листинг using using using

13.12.

Файл

System;

namespace class

Synchronized SynchronizedApp

static

ArrayList

public

static

void поток myEnumerator

static

void

ArrayList

426

args) myWords

new A r r a y L i s t

А В Фролов, Г. В. Фролов. Язык

Самоучитель


ThreadStart Thread thr

myThreadDelegate new

new

ThreadStart

потока

Метод M a i n , работающий в рамках основного потока нашей программы, создает массив m y W o r d s класса A r r a y L i s t и затем и н и ц и а л и з и р у е т его: A r r a y L i s t myWords

new

Д а л е е наша п р о г р а м м а создает с и н х р о н и з и р о в а н н ы й м а с с и в , с т а т и ч е с к и й м е т о д Synchronized:

в ы з ы в а я для этого

myWordsSynchro В качестве единственного параметра методу передается ссылка на исходный массив. После этого программа создает новый поток для метода M y T h r e a d и запускает его: ThreadStart Thread thr

myThreadDelegate new new потока

Эта п р о ц е д у р а была нами п о д р о б н о о п и с а н а в гл. 10 этой книги. М е т о д M y T h r e a d , р а б о т а ю щ и й в р а м к а х о т д е л ь н о г о п о т о к а , о т о б р а ж а е т содержи¬ мое м а с с и в а на к о н с о л и при п о м о щ и итератора: public

static

void поток IEnumerator myEnumerator

Глава 13.

427


Свойство Для того чтобы определить, является ли массив синхронизированным или нет, можно воспользоваться свойством I s S y n c h r o n i z e d , доступным только для чтения.

Свойство SyncRoot Вы

обеспечить синхронизацию, н е о б х о д и м у ю для использования массивов в

многопоточных

приложениях,

и

без

создания

синхронизированного

массива. Для этого можно воспользоваться свойством S y n c R o o t и с х о д н о г о массива, возвращающего объект синхронизации. Поясним

использование свойства

SyncRoot

на примере программы,

исходный

текст которой представлен в листинге Листинг using

13.13.

Файл

System;

using using namespace class

SyncRoot SyncRootApp

static

ArrayList

public

static

myWords;

void поток

lock myEnumerator

static

void

myWords

428

1

args)

new

В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


ThreadStart Thread thr

myThreadDelegate new

new

потока

М е т о д M a i n этой программы создает массив m y W o r d s , инициализирует его, а затем запускает метод M y T h r e a d в отдельном потоке. В о т исходный текст этого метода: public

static

void поток

lock myEnumerator

Как в и д и т е , перед н а ч а л о м цикла и т е р а ц и и п о массиву m y W o r d s метод б л о к и р у е т объект myWords с п о м о щ ь ю к л ю ч е в о г о слова l o c k . Это н е о б х о д и м о , так как п р о ц е д у р а и т е р а ц и и по массиву не м о ж е т в ы п о л н я т ь с я в м н о г о п о т о ч н о м ре¬ ж и м е без с и н х р о н и з а ц и и . В о с т а л ь н о м н а ш а п р о г р а м м а п о х о ж а на п р е д ы д у щ у ю , ис¬ х о д н ы й т е к с т к о т о р о й был п р е д с т а в л е н в л и с т и н г е 13.12.

Недостатки массивов ArrayList Разумеется, массивам A r r a y L i s t присущи и некоторые недостатки по сравнению с м а с с и в а м и A r r a y . П о э т о м у вы д о л ж н ы и с п о л ь з о в а т ь м а с с и в ы такого типа, к о т о р ы й в н а и л у ч ш е й степени с о о т в е т с т в у е т л о г и к е р а б о т ы в а ш е й п р о г р а м м ы . Вот основные недостатки массивов A r r a y L i s t по сравнению с массивами A r r a y : •

массивы A r r a y L i s t в о т л и ч и е о т м а с с и в о в A r r a y н е м о г у т быть м н о г о м е р н ы м и ;

массивы A r r a y L i s t и при чтении д а н н ы х . Глава 13. Контейнеры для хранения

м е д л е н н е е м а с с и в о в A r r a y как при з а п и с и , так

429


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

ключи.

В к а ч е с т в е к л ю ч е й могут в ы с т у п а т ь , н а п р и м е р , т е к с т о в ы е с т р о к и . Тогда к а ж д о й такой строке м о ж н о будет п о с т а в и т ь в с о о т в е т с т в и е к а к о й - л и б о объект. П р о с т е й ш и й п р и м е р — т е л е ф о н н ы й с п р а в о ч н и к , где к а ж д о й ф а м и л и и с т а в и т с я в с о о т в е т с т в и е тот или иной н о м е р т е л е ф о н а . С

использованием

можно

создать

на

библиотеки

основе

к л а с с о в Microsoft

хеш-таблиц

класса

F r a m e w o r k такой

Hashtable.

Рассмотрим

словарь

основные

приемы и с п о л ь з о в а н и я этого класса.

Создание словаря Для с о з д а н и я с л о в а р е й ( х е ш - т а б л и ц ) в классе H a s h t a b l e п р е д у с м о т р е н о н е с к о л ь к о перегруженных

конструкторов.

К о н с т р у к т о р без п а р а м е т р о в К о н с т р у к т о р без п а р а м е т р о в п о з в о л я е т создать п у с т о й с л о в а р ь с п а р а м е т р а м и , приня¬ т ы м и п о у м о л ч а н и ю . П о л ь з о в а т ь с я и м очень п р о с т о : Hashtable

myPhones

new

Исходный текст программы, в которой словарь H a s h t a b l e создается с помощью т а к о г о к о н с т р у к т о р а , п р е д с т а в л е н в л и с т и н г е 13.14. Листинг using using

13.14.

Файл

System;

namespace class

HashTable HashTableApp

static

void

Hashtable

args) myPhones

new

Hashtable

myEnumerator

430

А В Фролов, Г В. Фролов. Язык С# Самоучитель


П о с л е з а п у с к а п р о г р а м м а создает словарь и д о б а в л я е т в него 3 з а п и с и . Д а л е е эти записи о т о б р а ж а ю т с я на к о н с о л и : Сидоров: Иванов: Петров:

8-999-323-33-44 322-44-22 8-999-323-33-33

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

Копирование словаря Если п е р е д а т ь к о н с т р у к т о р у класса H a s h t a b l e ссылку н а у ж е и м е ю щ и й с я с л о в а р ь , то все э л е м е н т ы э т о г о с л о в а р я будут с к о п и р о в а н ы в н о в ы й с л о в а р ь . Это д е м о н с т р и р у ¬ ется в п р о г р а м м е , и с х о д н ы й т е к с т которой вы н а й д е т е в л и с т и н г е 13.15. Листинг

13.15.

using using

Файл

System;

namespace class

HashTablel HashTablelApp

static

void

Hashtable

args) new

myEnumerator

Hashtable Глава

Контейнеры для хранения объектов

new

431


myEnumeratorl

Здесь мы в н а ч а л е создаем с л о в а р ь m y P h o n e s и д о б а в л я е м в него 3 з а п и с и : Hashtable myPhones

new

Hashtable

Далее с о д е р ж и м о е с л о в а р я m y P h o n e s

к о п и р у е т с я в о вновь с о з д а в а е м ы й словарь

myPhonesCopy: H a s h t a b l e myPhonesCopy

new

Hashtable

П р о г р а м м а о т о б р а ж а е т на к о н с о л и с о д е р ж и м о е и с х о д н о г о с л о в а р я и его КОПИИ.

Указание начальной емкости словаря Для у в е л и ч е н и я э ф ф е к т и в н о с т и р а б о т ы со с л о в а р е м вы м о ж е т е у к а з а т ь , с к о л ь к о при¬ м е р н о э л е м е н т о в в нем будет х р а н и т ь с я (если, к о н е ч н о , это к о л и ч е с т в о вам известно). Для у к а з а н и я н а ч а л ь н о й е м к о с т и с л о в а р я и с п о л ь з у й т е к о н с т р у к т о р класса H a s h t a b l e с о д н и м п а р а м е т р о м , как это п о к а з а н о н и ж е : Hashtable

myPhones

new

Н а ч а л ь н а я емкость д о л ж н а задаваться п о л о ж и т е л ь н ы м ч и с л о м , в п р о т и в н о м случае возникнет исключение

RangeException

Другие В

конструкторы

классе H a s h t a b l e имеются и д р у г и е к о н с т р у к т о р ы , к о т о р ы е п о д р о б н о о п и с а н ы

в д о к у м е н т а ц и и на б и б л и о т е к у к л а с с о в Microsoft Среди них

Framework.

конструктор, позволяющий при создании нового словаря скопировать в

него с о д е р ж и м о е с у щ е с т в у ю щ е г о словаря и при этом дополнительно задать так называе¬ мый фактор -загрузки (load factor).

Фактор загрузки задается как отношение количества

и м е ю щ и х с я элементов к общей емкости словаря. Чем меньше фактор загрузки, тем быст¬ рее выполняется поиск элементов в словаре и тем больше требуется памяти для хранения словаря. По у м о л ч а н и ю фактор загрузки равен 1.0, что обеспечивает наилучший компро¬ мисс между с к о р о с т ь ю работы словаря и занимаемой этим словарем памятью.

432

А В Фролов, Г. В. Фролов. Язык

Самоучитель


Другие конструкторы обеспечивают создание словарей, имеющих нестандартный а л г о р и т м в ы ч и с л е н и я х е ш - к о д о в э л е м е н т о в , словарей с заданной е м к о с т ь ю и факто¬ ром з а г р у з к и , с л о в а р е й , д о п у с к а ю щ и х запись с о д е р ж и м о г о в п о т о к в ы в о д а (об этом мы п о г о в о р и м п о з ж е ) , а т а к ж е н е с т а н д а р т н ы е а л г о р и т м ы с р а в н е н и я э л е м е н т о в , хра¬ н я щ и х с я в словаре.

Добавление новых элементов Для д о б а в л е н и я в словарь н о в ы х э л е м е н т о в вы м о ж е т е в о с п о л ь з о в а т ь с я м е т о д о м A d d : H a s h t a b l e myPhones

new "322-44-22")

В качестве п е р в о г о п а р а м е т р а э т о м у м е т о д у н у ж н о п е р е д а т ь к л ю ч , а в к а ч е с т в е в т о р о г о — з н а ч е н и е , с в я з а н н о е с этим к л ю ч о м . В н а ш е м п р и м е р е роль к л ю ч а играет ф а м и л и я , а роль значения — н о м е р т е л е ф о н а .

Чтение содержимого словаря П р о г р а м м а м о ж е т получить список всего с о д е р ж и м о г о с л о в а р я при п о м о щ и и т е р а т о р а класса П р и м е р ы и с п о л ь з о в а н и я этого и т е р а т о р а есть в л и с т и н г а х 13.14 и 13.15, приведен¬ ных нами р а н е е . В о т с о о т в е т с т в у ю щ и й ф р а г м е н т кода: I D i c t i o n a r y E n u m e r a t o r myEnumerator

Здесь м ы вначале создаем и т е р а т о р , в ы з ы в а я для этого м е т о д G e t E n u m e r a t o r . Эта о п е р а ц и я

аналогична операции

получения

итератора массива A r r a y L i s t ,

рас¬

с м о т р е н н о г о в н а ч а л е этой г л а в ы . Для п о л у ч е н и я пар к л ю ч - з н а ч е н и е мы создали ц и к л , в к о т о р о м в ы п о л н я е т с я после¬ д о в а т е л ь н о е п е р е м е щ е н и е и т е р а т о р а с п о м о щ ь ю метода M o v e N e x t . П р и н е о б х о д и м о ¬ сти вы м о ж е т е снова у с т а н о в и т ь и т е р а т о р на начало с л о в а р я , в о с п о л ь з о в а в ш и с ь мето¬ дом

Reset. извлекается с п о м о щ ь ю свойства итератора с именем Key,

а

с п о м о щ ь ю свойства и т е р а т о р а с и м е н е м V a l u e .

Поиск по ключу П о с л е д о б а в л е н и я всех э л е м е н т о в в словарь ваша п р о г р а м м а м о ж е т и з в л е к а т ь н у ж н ы е э л е м е н т ы при п о м о щ и с в о й с т в а I t e m . Это с в о й с т в о д о с т у п н о через к в а д р а т н ы е скоб­

ки Глава

Контейнеры для хранения объектов

433


Листинг

13.16.

using using

Файл

System;

namespace class

HashTable2App

static

void

args)

H a s h t a b l e myPhones

new емкость:

В этой программе мы создали словарь с фамилиями и телефонами, а потом извлек­ ли телефон Иванова, указав эту фамилию в качестве Иванова:

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

Предварительная проверка содержимого словаря При неудачной попытке извлечения из словаря элемента с несуществующим ключом будет получена ссылка n u l l . Чтобы избежать возникновения исключения в этой си­ туации, программа может предварительно проверить наличие элемента в словаре. Этот прием демонстрируется в программе, исходный текст которой приведен в листинге (Здесь и далее телефоны приведены только для примера, не пытай­ тесь Листинг using using

13.17.

Файл

System;

namespace class

HashTable3 HashTable3App

static

void

args) А В.

Г. В Фролов. Язык

Самоучитель


Hashtable

myPhones

new емкость:

Иванова: else Иванова

не

Если словарь m y P h o n e s с о д е р ж и т элемент с к л ю ч о м «Иванов», то программа и з ­ влекает соответствующее значение (номер телефона) и отображает его на консоли: Иванова: else Иванова

не

В противном случае на консоль выводится с о о б щ е н и е о т о м , что т е л е ф о н Иванова не найден. Д л я проверки с о д е р ж и м о г о словаря вы также м о ж е т е использовать свойства ContainsKey и ContainsValue Первое из них позволяет проверить, имеется ли в словаре элемент с заданным к л ю ч о м , а второй имеется ли в словаре э л е м е н т с за¬ данным значением. М е т о д C o n t a i n s K e y аналогичен только что р а с с м о т р е н н о м у м е т о д у C o n t a i n s Использование

этого

метода,

а также

метода C o n t a i n s V a l u e демонстрируется

в программе, и с х о д н ы й текст которой приведен в листинге Листинг

13.18.

using using

Файл

System;

namespace class

HashTable4App

static

void

args)

H a s h t a b l e myPhones

Глава

new

для хранения объектов

435


i

else Иванова

не

есть

в

else 322-44-22

в

словаре

не

Здесь после с о з д а н и я с л о в а р я и д о б а в л е н и я в него всех э л е м е н т о в мы вначале про¬ в е р я е м , если ли там т е л е ф о н И в а н о в а : Иванова: else Иванова

не

Если этот т е л е ф о н и м е е т с я в с л о в а р е , то мы о т о б р а ж а е м его на к о н с о л и . В против¬ ном случае в ы в о д и т с я с о о б щ е н и е о т о м , что т е л е ф о н не н а й д е н . Далее с п о м о щ ь ю м е т о д а C o n t a i n s V a l u e мы п р о в е р я е м , есть ли в с л о в а р е за¬ пись, содержащая телефон 322-44-22: 322-44-22

есть

322-44-22

в

в

else словаре

не

Удаление элементов из словаря Чтобы полностью очистить словарь H a s h t a b l e , вы можете использовать C l e a r . Для у д а л е н и я э л е м е н т а с з а д а н н ы м к л ю ч о м в о с п о л ь з у й т е с ь м е т о д о м R e m o v e . К л ю ч п е р е д а в а е м о г о э л е м е н т а п е р е д а е т с я э т о м у методу в качестве е д и н с т в е н н о г о па¬ раметра. Если в с л о в а р е имеется э л е м е н т с з а д а н н ы м к л ю ч о м , то он будет у д а л е н м е т о д о м Если же т а к о г о э л е м е н т а в словаре нет, то с о д е р ж и м о е с л о в а р я о с т а н е т с я изменным. И с п о л ь з о в а н и е м е т о д а R e m o v e д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст ко¬ торой п р е д с т а в л е н в л и с т и н г е 13.19.

436

А.

Г.

Фролов. Язык С#. Самоучитель


Листинг

13.19.

using using

Файл

System;

namespace class

HashtableRemoveApp

static

void

args)

Hashtable

myPhones

new

myEnumerator

myEnumeratorl

ми

Получив наша программа заполняет словарь фамилиями и т е л е ф о н а ­ после чего с о д е р ж и м о е словаря отображается на

H a s h t a b l e myPhones myPhones

new

Д а л е е из словаря удаляется запись с т е л е ф о н о м

Глава

для хранения

437


После этого содержимое словаря снова отображается. Теперь в нем стало на одну запись меньше: Сидоров: Иванов: Петров:

322-44-22 8-999-323-33-33

Сидоров: Петров:

8-999-323-33-44 8-999-323-33-33

Емкость и

размер словаря

При добавлении в словарь новых элементов происходит автоматическое увеличение его размера. Текущий размер словаря вы можете узнать при помощи свойства Count. Пример использования этого свойства приведен в листинге 13.20. Листинг using using

13.20.

Файл

System;

namespace

HashTable5

class static

void

args)

H a s h t a b l e myPhones

new

элементов

в

словаре:

Словари и многопоточность Если словари H a s h t a b l e используются в многопоточных программах, необходимо позаботиться о синхронизации. Синхронизация словарей H a s h t a b l e выполняется аналогично синхронизации массивов с применением статического ме­ тода или свойства S y n c R o o t . Применение метода S y n c h r o n i z e d демонстрируется в программе, исходный текст которой приведен в листинге 13.21.

438

А. В. Фролов, Г. В. Фролов. Язык С# Самоучитель


Листинг

13.21.

Файл

ch13\HashTable6\HashTable6App.cs

using using using namespace class static static

Hashtable Hashtable

public

static

myPhones;

void "MyThread:

поток myEnumerator

static

void

myPhones

args) new

ThreadStart myThreadDelegate Thread thr new

new

потока

Контейнеры для хранения объектов

439


П о л у ч и в у п р а в л е н и е , м е т о д M a i n , р а б о т а ю щ и й в р а м к а х о с н о в н о г о потока, с о з д а е т словарь m y P h o n e s и з а п о л н я е т его: myPhones

Далее

new

на

базе

Hashtable

этого

словаря

создается

новый,

синхронизированный

словарь

myPhonesSynchro Затем м е т о д M a i n з а п у с к а е т поток

о с у щ е с т в л я ю щ и й вывод содержи¬

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

И с п о л ь з о в а н и е и т е р а т о р о в в м н о г о п о т о ч н ы х п р о г р а м м а х всегда т р е б у е т синхрони¬ з а ц и и , но для этого вам не о б я з а т е л ь н о с о з д а в а т ь н о в ы й , с и н х р о н и з и р о в а н н ы й сло¬ варь. В п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 13.22, использует¬ ся о б ъ е к т с и н х р о н и з а ц и и , п о л у ч е н н ы й с п о м о щ ь ю с в о й с т в а SyncRoot. Листинг using using using

13.22.

Файл

System;

namespace class

HashTable7 HashTable7App

static

Hashtable

public

static

void поток

myEnumerator

440

В.

Г. В. Фролов. Язык

Самоучитель


static

void

args)

myPhones

new

ThreadStart Thread thr

myThreadDelegate new

new

T h r e a d S t a r t (MyThread)

потока

С в о й с т в о S y n c R o o t м ы и с п о л ь з у е м в блоке l o c k , как это п о к а з а н о н и ж е :

myEnumerator

Б л о к о г р а н и ч и в а е т ф р а г м е н т кода, внутри к о т о р о г о д о л ж н а в ы п о л н я т ь с я с и н х р о ­ низация.

Сортированный список С о р т и р о в а н н ы й с п и с о к класса S o r t e d L i s t п р е д с т а в л я е т собой г и б р и д м а с с и в а и с л о в а р я , о п и с а н н ы х ранее в этой главе. Вы м о ж е т е з а п и с ы в а т ь в этот с п и с о к э л е м е н ­ ты с к л ю ч а м и , как в с л о в а р ь , а затем извлекать эти э л е м е н т ы по ключу. Но есть и д р у ­ гая в о з м о ж н о с т ь п р о г р а м м а м о ж е т извлекать э л е м е н т ы из с о р т и р о в а н н о г о списка по и н д е к с у , как это д е л а е т с я при р а б о т е с м а с с и в а м и . Глава

для хранения

441


Создание и наполнение списка Создание и наполнение сортированного списка можно выполнять та­ ким же образом, как и в случае словаря H a s h t a b l e Чтобы создать сортированный список, воспользуйтесь одним из конструкторов. Простейший не имеет параметров и создает пустой список: SortedList

myPhones

new

Здесь мы создали пустой список телефонных номеров. Для заполнения списка вы можете воспользоваться методом ниже:

как это показано

myPhones "8-999-323-33-33")

В качестве первого параметра методу A d d передается ключ добавляемого элемен¬ та, а в качестве второго значение элемента. Здесь мы используем те же самые прие­ мы, что и при работе со словарем H a s h t a b l e . Заметим, что в классе S o r t e d L i s t предусмотрено несколько конст¬ рукторов. С их помощью вы можете создавать сортированные списки на базе других кон¬ тейнеров, задавать начальный размер словаря и определять свои собственные методы сор¬ тировки элементов. Эти конструкторы аналогичны соответствующим конструкторам клас¬ са H a s h t a b l e . При необходимости вы найдете более подробную информацию об этих в справочной системе Microsoft Visual

Извлечение данных из списка Как мы уже говорили, программа может извлекать данные из сортированного списка двумя способами — по ключу (как из словаря) и по индексу (как из массива). Оба эти способа демонстрируются в программе, исходный текст которой вы найде­ те в листинге Листинг

Файл

using using SortedListNameSpace

namespace class

Classl

static

void

SortedList

args) myPhones

new

myEnumerator

442

A.

Г.

Фролов. Язык

Самоучитель


myEnumerator

myEnumerator

Console Иванова:

i

0;

i

i++)

(l}\n",

Вначале программа с о з д а е т и наполняет сортированный с п и с о к , используя д л я э т о ­ го описанные выше приемы. Д а л е е о на отображает с о д е р ж и м о е списка с п о м о щ ь ю итератора, отображая на к о н с о л и п о л н о е с о д е р ж и м о е списка: I D i c t i o n a r y E n u m e r a t o r myEnumerator

А н а л о г и ч н у ю о п е р а ц и ю м ы выполняли ранее д л я словарей класса H a s h t a b l e . Сортированный список д о п у с к а е т использование квадратных с к о б о к для и з в л е ч е ­ н и я и изменения элементов по ключу. Мы применили этот с п о с о б д л я получения те­ л е ф о н а Иванова: Иванова: Теперь о т о м , как извлечь Этот п р и е м д е м о н с т р и р у е т с я for(int i

0;

i

Глава 13. Контейнеры для хранения

сортированного списка S o r t e d L i s t по индексу. i++)

443


З д е с ь мы в цикле все менная цикла i играет роль индекса.

сортированного списка, причем п е р е ­

Для получения ключа элемента с заданным и н д е к с о м мы и с п о л ь з у е м з д е с ь м е т о д G e t K e y , передавая е м у э т о т и н д е к с в качестве единственного параметра. Что же касается значения элемента с заданным и н д е к с о м , то е г о м о ж н о извлечь с помощью м е т о д а G e t B y В ы должны передать э т о м у м е т о д у индекс нужного элемента в качестве параметра. Д а л е е наша программа д е м о н с т р и р у е т р а б о т у со с п и с к о м как с массивом: i

i

i++)

i, З д е с ь переменная цикла i используется для прямой индексации списка. Ключ и з ­ влекается м е т о д о м G e t K e y , а значение — м е т о д о м G e t B y l n d e x . П е р е д т е м как завершить свою р а б о т у , наша программа выводит на консоль ключ и значение самого первого элемента сортированного списка элемента с нулевым

В о т что программа выведет на консоль после своего запуска: Иванов: Петров: Сидоров: Телефон

322-44-22 8-999-323-33-33 8-999-323-33-44 Иванова:

322-44-22

0. 1.

Иванов: Петров:

322-44-22 8-999-323-33-33

2.

Сидоров:

8-999-323-33-44

Иванов:

322-44-22

Определение индекса по ключу и значению В классе S o r t e d L i s t , предназначенном для создания сортированных списков, опре¬ д е л е н ы в с е м е т о д ы , рассмотренные нами р а н е е при описании словаря H a s h t a b l e . Это означает, что при р а б о т е с таким списком вам д о с т у п н ы те же самые в о з м о ж н о ­ с т и , что и при р а б о т е с о словарем Так как сортированный список S o r t e d L i s t в е д е т себя и как в классе п р е д у с м о т р е н ы и д р у г и е м е т о д ы , позволяющие обращаться к элементам списка по и н д е к с у . Два таких м е т о д а ( G e t K e y и G e t B y l n d e x ) были рассмотрены нами в п р е д ы д у щ е м р а з д е л е . Первый из них позволяет извлечь ключ для элемента с заданным и н д е к с о м , а второй — значение элемента с заданным и н д е к с о м . 444

А В . Фролов, Г.В. Фролов. ЯзыкС#. Самоучитель


Помимо этого в классе S o r t e d L i s t есть методы, позволяющие определить ин­ декс элемента по ключу и значение элемента по ключу. Первая из этих операций вы­ полняется методом I n d e x O f K e y , а методом Использование методов I n d e x O f K e y и I n d e x O f V a l u e демонстрируется в про­ грамме, исходный текст которой приведен в листинге Листинг

13.24.

using using

Файл

System;

namespace

SortedListl

class static

void

args)

S o r t e d L i s t myPhones

new

myEnumerator

i

0;

i

i++)

Получив управление, метод M a i n нашей программы создает сортированный спи­ сок и заполняет его парами ключ-значение: S o r t e d L i s t myPhones

Глава

new "322-44-22"

Контейнеры для хранения объектов

445


Д а л е е в п р о г р а м м е с о з д а е т с я и т е р а т о р для п о с л е д о в а т е л ь н о г о п р о с м о т р а списка. П р о с м о т р о с у щ е с т в л я е т с я в цикле: myEnumerator

З н а ч е н и е и н д е к с а э л е м е н т а , о т о б р а ж а е м о г о на к о н с о л и , по к л ю ч у мы п о л у ч а е м при п о м о щ и в ы р а ж е н и я m y P h o n e s

IndexOfKey

Использование метода I n d e x O f V a l u e ,

в о з в р а щ а ю щ е г о и н д е к с э л е м е н т а п о зна­

ч е н и ю , д е м о н с т р и р у е т с я в п р е д с т а в л е н н о м ниже цикле: i

Здесь

0;

i

i++)

мы

передаем

по индексу с

помощью

методу

IndexOfValue

метода G e t B y l n d e x .

значение

Очевидно,

элемента, что

индекс,

от м е т о д а I n d e x O f V a l u e , д о л ж е н быть равен п е р е м е н н о й

полученного полученный

В э т о м м о ж н о убе¬

диться, запустив программу на выполнение: 0. 1. 2.

Иванов: Петров: Сидоров:

322-44-22 8-999-323-33-33 8-999-323-33-44

0. 1. 2.

Иванов: Петров: Сидоров:

322-44-22 8-999-323-33-44

Изменение значения по индексу В ы ш е мы р а с с к а з а л и В а м , как м о ж н о извлечь из с о р т и р о в а н н о г о с п и с к а к л ю ч и значе­ ние э л е м е н т а по его индексу. П р е д у с м о т р е н а и д р у г а я в о з м о ж н о с т ь

и з м е н е н и е зна¬

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

Д а н н о м у м е т о д у н у ж н о пе¬

редать д в а п а р а м е т р а — и н д е к с и з м е н я е м о г о э л е м е н т а и новое з н а ч е н и е э л е м е н т а . Ис¬ п о л ь з о в а н и е м е т о д а S e t B y l n d e x д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст ко­ торой п р и в е д е н в л и с т и н г е 1 3 . 2 5 .

446

А. В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


Листинг

13.25.

using using

Файл

System;

namespace class static

void

SortedList

args) myPhones

new

myEnumerator

myPhones.

"+7- (095)

I D i c t i o n a r y E n u m e r a t o r myEnumeratorl

Здесь мы создаем сортированный список, используя для этого тот же прием, что и в предыдущей программе (см. листинг Затем мы изменяем значение элемен­ тов с индексами 1 и 2, вызывая для этого метод S e t B y l n d e x : myPhones.

Глава

для хранения объектов

447


П р о г р а м м а о т о б р а ж а е т на к о н с о л и и с х о д н о е содержимое списка, а т а к ж е с о д е р ж и ­ мое списка после и з м е н е н и й : 0. 1. 2.

Иванов: Петров: Сидоров:

322-44-22 8-999-323-33-33 8-999-323-33-44

Иванов: Петров: Сидоров:

322-44-22

1. 2.

Удаление элементов списка В классе S o r t e d L i s t о п р е д е л е н ы 3 м е т о д а , п р е д н а з н а ч е н н ы е для у д а л е н и я э л е м е н ­ тов из с о р т и р о в а н н о г о списка. С д в у м я из них вы у ж е з н а к о м ы по с л о в а р я м H a s h t a b l e это м е т о д C l e a r , у д а л я ю щ и й из с п и с к а все и м е ю щ и е с я там э л е м е н т ы , и м е т о д R e m o v e , с п о м о щ ь ю ко­ т о р о г о м о ж н о у д а л и т ь э л е м е н т с з а д а н н ы м к л ю ч о м . К л ю ч у д а л я е м о г о э л е м е н т а пере¬ дается методу R e m o v e в качестве единственного параметра. Третий м е т о д с н а з в а н и е м R e m o v e A t п о з в о л я е т у д а л и т ь э л е м е н т по его индексу. И н д е к с у д а л я е м о г о э л е м е н т а п е р е д а е т с я м е т о д у R e m o v e A t ч е р е з е д и н с т в е н н ы й пара­ метр. И с п о л ь з о в а н и е м е т о д а д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й вы н а й д е т е в л и с т и н г е 13.26. Листинг using using

13.26.

Файл

ch

System;

namespace class

SortedList3 SortedList3App

static

void

S o r t e d L i s t myPhones

args) new

SortedList

myEnumerator

448

А. В Фролов, Г. В Фролов. Язык С#. Самоучитель


myEnumeratorl

Вот как мы у д а л я е м э л е м е н т с и н д е к с о м myPhones

RemoveAt

П р о г р а м м а в ы в о д и т на к о н с о л ь с о д е р ж и м о е и с х о д н о г о списка, а т а к ж е с о д е р ж и м о е списка п о с л е у д а л е н и я из него элемента: 1. 2.

Иванов: Петров: Сидоров:

322-44-22 8-999-323-33-33 8-999-323-33-44

1.

Иванов: Сидоров:

322-44-22 8-999-323-33-44

О б р а т и т е в н и м а н и е , что и н д е к с э л е м е н т а с к л ю ч о м « С и д о р о в » и з м е н и л с я и стал равен е д и н и ц е . Д о б а в л е н и е э л е м е н т о в в с п и с о к или у д а л е н и е из него п р и в о д и т к и з м е н е н и ю и н д е к с о в . У ч т и т е э т о , если с о б и р а е т е с ь о б р а щ а т ь с я к э л е м е н т а м сорти¬ р о в а н н о г о списка по и н д е к с а м .

Работа в многопоточном режиме С т а т и ч е с к и е с п и с к и S o r t e d L i s t , о б ъ я в л е н н ы е как p u b l i c , м о ж н о и с п о л ь з о в а т ь в м н о г о п о т о ч н ы х п р и л о ж е н и я х без п р и н я т и я д о п о л н и т е л ь н ы х мер по с и н х р о н и з а ц и и . Если же вы создаете н е с т а т и ч е с к и е о б ъ е к т ы этого класса, то н е о б х о д и м о о б е с п е ч и т ь синхронизацию. Это м о ж н о сделать л и б о создав с и н х р о н и з и р о в а н н ы й с п и с о к м е т о д о м S y n c h ­ r o n i z e d , л и б о п о л у ч и в о б ъ е к т с и н х р о н и з а ц и и с п о м о щ ь ю с в о й с т в а S y n c R o o t . Оба способа а н а л о г и ч н ы с п о с о б а м , о п и с а н н ы м в этой главе ранее для массива A r r a y L i s t и с л о в а р я H a s h t a b l e , п о э т о м у мы не будем о с т а н а в л и в а т ь с я на этом п о д р о б н о .

Стек Stack Такой к о н т е й н е р , как стек (stack), п о з в о л я е т о р г а н и з о в а т ь х р а н е н и е д а н н ы х по п р и н ­ ципу вошел последний Так р а б о т а е т магазин а в т о м а т а К а л а ш н и ­ кова. Вы заполняете его п а т р о н а м и с о д н о й с т о р о н ы , п р и ч е м те п а т р о н ы , к о т о р ы е бы¬ ли в с т а в л е н ы в магазин п о с л е д н и м и , будут и с п о л ь з о в а н ы в п е р в у ю о ч е р е д ь . Глава 15

Язык С#

Контейнеры для хранения объектов Самоучитель

449


Для к л а с с и ч е с к о г о стека о п р е д е л е н ы как м и н и м у м две о п е р а ц и и : •

запись в стек ( p u s h ) ,

и з в л е ч е н и е из стека (pop).

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

F r a m e w o r k имеется класс

S t a c k , с п е ц и а л ь н о п р е д н а з н а ч е н н ы й для о р г а н и з а ц и и с т е к о в . Он очень у д о б е н и не¬ сложен в и с п о л ь з о в а н и и .

Создание стека Для с о з д а н и я стека класса S t a c k в ы м о ж е т е и с п о л ь з о в а т ь один и з трех конструкто¬ р о в . П р о с т е й ш и й к о н с т р у к т о р не имеет п а р а м е т р о в и п р е д н а з н а ч е н для с о з д а н и я пус¬ того стека с р а з м е р о м , з а д а н н ы м по у м о л ч а н и ю : Stack

new

И м е ю т с я т а к ж е к о н с т р у к т о р ы с о д н и м п а р а м е т р о м . П е р в ы й из них позволяет соз¬ дать стек на базе д р у г о г о к о н т е й н е р а , а второй — пустой стек з а д а н н о г о размера.

Добавление элементов в стек Ч т о б ы д о б а в и т ь э л е м е н т в стек, н е о б х о д и м о вызвать м е т о д P u s h и передать этому ме¬ тоду ч е р е з е д и н с т в е н н ы й п а р а м е т р ссылку н а д о б а в л я е м ы й э л е м е н т . Н и ж е мы с о з д а л и пустой стек и д о б а в и л и в него н е с к о л ь к о т е к с т о в ы х строк: Stack

wordsStack

new

З а м е т и м , что при н е о б х о д и м о с т и м о ж н о д о б а в и т ь в стек и пустой э л е м е н т , передав м е т о д у P u s h з н а ч е н и е null.

Извлечение элементов из стека Для того чтобы извлечь элемент из стека, воспользуйтесь методом Pop. Этот метод извле¬ кает из стека один элемент и возвращает в ы з ы в а ю щ е й программе ссылку на этот элемент: один П р и п о п ы т к е и з в л е ч е н и я э л е м е н т а и з п у с т о г о стека в о з н и к а е т и с к л ю ч е н и е I n v a l i d O p e r a t i o n E x c e p t i o n . Если при работе в а ш е й п р о г р а м м ы в о з м о ж н о воз¬ н и к н о в е н и е д а н н о й с и т у а ц и и , н е забудьте п р е д у с м о т р е т ь с о о т в е т с т в у ю щ и й обработ¬ чик и с к л ю ч е н и й .

450

Фролов, Г. В. Фролов. Язык С#. Самоучитель


Чтобы

и з б е ж а т ь в ы п о л н е н и я о п е р а ц и и и з в л е ч е н и я д а н н ы х из п у с т о г о стека, вы

также м о ж е т е к о н т р о л и р о в а т ь т е к у щ и й размер стека при п о м о щ и с в о й с т в а C o u n t : в

стеке

Проверка содержимого верхушки стека При п о м о щ и м е т о д а P e e k п р о г р а м м а м о ж е т п р о в е р и т ь с о д е р ж и м о е в е р х у ш к и стека, не и з в л е к а я из стека х р а н я щ и й с я там элемент: элемент: М е т о д P e e k в о з в р а щ а е т э л е м е н т , з а п и с а н н ы й в в е р х у ш к е стека. Если стек пуст, при в ы з о в е этого метода в о з н и к а е т и с к л ю ч е н и е I n v a l i d O p e r a t i o n E x c e p t i o n .

Просмотр стека с помощью итератора Содержимое

стека

можно

просмотреть

при

помощи

итератора

класса

Sys-

I E n u m e r a t o r , как это п о к а з а н о н и ж е : IEnumerator myEnumerator

О с о б е н н о с т ь э т о г о м е т о д а з а к л ю ч а е т с я в т о м , что э л е м е н т ы б у д у т п р о с м а т р и ¬ в а т ь с я не в п о р я д к а д о б а в л е н и я , а в о б р а т н о м п о р я д к е , к а к б у д т о они и з в л е к а ю т с я и з с т е к а . П р о ц е д у р а п р о с м о т р а с п о м о щ ь ю и т е р а т о р а , о д н а к о , н е и з м е н я е т содер¬ жимого самого стека.

Удаление элементов стека Для у д а л е н и я всех э л е м е н т о в и з стека п р е д н а з н а ч е н м е т о д C l e a r . Что ж е к а с а е т с я у д а л е н и я о т д е л ь н ы х э л е м е н т о в , то эта о п е р а ц и я в ы п о л н я е т с я при и з в л е ч е н и и элемен¬ тов из стека м е т о д о м P o p .

Другие методы и свойства класса Stack Если стек с о з д а е т с я п р о г р а м м о й , р а б о т а ю щ е й в м н о г о п о т о ч н о м р е ж и м е , н е о б х о д и м о обеспечить синхронизацию. Это м о ж н о с д е л а т ь л и б о создав м е т о д о м S y n c h o n i z e d с и н х р о н и з и р о в а н н ы й стек, л и б о п о л у ч и в о б ъ е к т с и н х р о н и з а ц и и при п о м о щ и с в о й с т в а S y n c R o o t . С в о й с т в о п о з в о л я е т о п р е д е л и т ь , я в л я е т с я л и стек с и н х р о н и з и р о в а н н ы м или нет. п р и е м ы были о п и с а н ы р а н е е , когда мы р а с с к а з ы в а л и о д р у г и х Глава 13. Контейнеры для хранения

451


С п о м о щ ь ю метода C o n t a i n s , и м е ю щ е г о один п а р а м е т р , в ы м о ж е т е о п р е д е л и т ь , х р а н и т с я ли з а д а н н ы й э л е м е н т в П р и в ы з о в е н у ж н о п е р е д а т ь э т о м у методу ссылку на э л е м е н т . Если такой э л е м е н т н а х о д и т с я в стеке, м е т о д вернет значение t r u e , в противном — значение f a l s e . При н е о б х о д и м о с т и вы м о ж е т е с к о п и р о в а т ь с о д е р ж и м о е стека в д р у г о й стек (мето¬ дом C l o n e ) или в о д н о м е р н ы й м а с с и в класса A r r a y ( м е т о д о м С о р у Т о ) .

Пример программы В листинге

13.27 мы п р и в е л и и с х о д н ы й текст п р о г р а м м ы , в ы п о л н я ю щ е й о с н о в н ы е

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

13.27.

Файл

System;

StackDemo class static

void

Stack wordsStack

args) new

в

стеке

I E n u m e r a t o r myEnumerator

элемент:

w o r d s S t a c k . Peek

один один

452

A

элемент:

Г. В. Фролов. Язык С#. Самоучитель


извлечения

в

стеке

myEnumeratorl

П о с л е запуска п р о г р а м м а создает стек и з а п и с ы в а е т в него 7 т е к с т о в ы х с т р о к фра¬ зы « к а ж д ы й о х о т н и к ж е л а е т з н а т ь , где сидит фазан». Д а л е е п р о г р а м м а о т о б р а ж а е т на к о н с о л и к о л и ч е с т в о э л е м е н т о в и с о д е р ж и м о е стека (при п о м о щ и и т е р а т о р а ) . П о с л е этого п р о г р а м м а п о к а з ы в а е т н а к о н с о л и с о д е р ж и м о е в е р х у ш к и стека, и з в л е к а е т из стека два э л е м е н т а , а затем вновь о т о б р а ж а е т весь стек: Всего

в

стеке

7

элементов:

фазан где желает каждый Первый фазан И з в л е к а е м один э л е м е н т : фазан И з в л е к а е м один э л е м е н т : сидит П о с л е и з в л е ч е н и я в с т е к е пяти

элементов:

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

Очередь Queue очереди з н а к о м о , н а в е р н о е , в с е м , кто хоть раз бывал в магазине. О ч е р е д ь ра¬ ботает по п р и н ц и п у « п е р в ы й в о ш е л — первый в ы ш е л » . Для создания очередей в б и б л и о т е к е классов Microsoft Framework предусмот­ рен класс Q u e u e . Р а с с м о т р и м о с н о в н ы е п р и е м ы его и с п о л ь з о в а н и я . Глава

Контейнеры для хранения объектов

453


Создание очереди Для создания

вы можете и с п о л ь з о в а т ь один из ч е т ы р е х п е р е г р у ж е н н ы х кон­

с т р у к т о р о в , п р е д у с м о т р е н н ы х в классе Q u e u e . П р о с т е й ш и й к о н с т р у к т о р не имеет п а р а м е т р о в . Он создает п у с т у ю очередь с ха­ рактеристиками, заданными по умолчанию: Queue

new

Другие к о н с т р у к т о р ы п р е д н а з н а ч е н ы для с о з д а н и я о ч е р е д и на базе с у щ е с т в у ю щ е г о к о н т е й н е р а , о ч е р е д и с з а д а н н ы м н а ч а л ь н ы м р а з м е р о м и ф а к т о р о м роста. Ф а к т о р рос­ та

это число р а з , в к о т о р о е у в е л и ч и в а е т с я т е к у щ а я е м к о с т ь о ч е р е д и при д о б а в л е н и и

элемента в полностью заполненную очередь.

Добавление элемента в очередь Ч т о б ы д о б а в и т ь в о ч е р е д ь н о в ы й э л е м е н т , п р и м е н я й т е м е т о д E n q u e u e . Н и ж е мы по¬ казали п р и м е р и с п о л ь з о в а н и я э т о г о метода: Queue w o r d s Q u e u e

new

П р и н е о б х о д и м о с т и вы м о ж е т е д о б а в и т ь в о ч е р е д ь з н а ч е н и е null.

Извлечение элементов из очереди С п о м о щ ь ю м е т о д а D e q u e u e п р о г р а м м а м о ж е т и з в л е ч ь один э л е м е н т и з о ч е р е д и : один

элемент:

К а к мы у ж е г о в о р и л и , в н а ч а л е из очереди и з в л е к а ю т с я те э л е м е н т ы , к о т о р ы е были туда з а п и с а н ы п е р в ы м и .

П о с л е и з в л е ч е н и я э л е м е н т а с о д е р ж и м о е очереди с д в и г а е т с я

на одну п о з и ц и ю . При

попытке

извлечения

элемента

из

пустой

очереди

возникает

исключение

Чтобы избежать выполнения операции извлечения д а н н ы х из п у с т о й о ч е р е д и , вы м о ж е т е к о н т р о л и р о в а т ь ее т е к у щ и й р а з м е р при п о м о щ и свойства

Count: в

454

очереди

В. Фролов, Г. В. Фролов. Язык

Самоучитель


Проверка содержимого начала очереди При п о м о щ и м е т о д а P e e k п р о г р а м м а м о ж е т п р о в е р и т ь с о д е р ж и м о е начала о ч е р е д и , не и з в л е к а я х р а н я щ и й с я там элемент: элемент: М е т о д P e e k в о з в р а щ а е т э л е м е н т , з а п и с а н н ы й в начале очереди и г о т о в ы й к «об¬ с л у ж и в а н и ю » . Е с л и о ч е р е д ь пустая, при в ы з о в е этого метода в о з н и к а е т и с к л ю ч е н и е

Просмотр очереди с помощью итератора Содержимое

очереди

можно

просмотреть

при

помощи

итератора

класса

t e m . C o l l e c t i o n s . I E n u m e r a t o r , как это п о к а з а н о н и ж е : IEnumerator myEnumerator

В о т л и ч и е от стека э л е м е н т ы о ч е р е д и будут п р о с м а т р и в а т ь с я в п о р я д к е добавле¬ ния. П р о ц е д у р а п р о с м о т р а с п о м о щ ь ю и т е р а т о р а не и з м е н я е т с о д е р ж и м о г о о ч е р е д и .

Удаление элементов очереди Для у д а л е н и я всех э л е м е н т о в и з очереди ваша п р о г р а м м а м о ж е т и с п о л ь з о в а т ь м е т о д Clear.

Удаление

отдельных

элементов

выполняется

при

извлечении

элементов

из очереди методом D e q u e u e .

Другие методы и свойства класса Queue Если о ч е р е д ь (как и л ю б о й д р у г о й к о н т е й н е р ) с о з д а е т с я п р о г р а м м о й , р а б о т а ю щ е й в многопоточном режиме, необходимо обеспечить синхронизацию. Это м о ж н о с д е л а т ь , создав м е т о д о м синхронизированную очередь л и б о п о л у ч и в о б ъ е к т с и н х р о н и з а ц и и при п о м о щ и с в о й с т в а S y n c R o o t . С в о й с т в о I s S y n c h r o n i z e d позволяет определить, является ли очередь синхронизированной или нет. С п о м о щ ь ю метода C o n t a i n s , и м е ю щ е г о один п а р а м е т р , в ы м о ж е т е о п р е д е л и т ь , н а х о д и т с я ли з а д а н н ы й э л е м е н т в о ч е р е д и . П р и в ы з о в е н у ж н о п е р е д а т ь этому м е т о д у с с ы л к у н а э л е м е н т . Если такой э л е м е н т н а х о д и т с я в о ч е р е д и , м е т о д C o n t a i n s в е р н е т значение t r u e , в противном значение f a l s e . П р и н е о б х о д и м о с т и вы м о ж е т е с к о п и р о в а т ь с о д е р ж и м о е о ч е р е д и в д р у г у ю о ч е р е д ь ( м е т о д о м C l o n e ) или в о д н о м е р н ы й массив класса A r r a y ( м е т о д о м С о р у Т о ) .

Глава 13. Контейнеры для хранения объектов

455


Пример

программы

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

13.28.

Файл

System;

namespace

static

void

args)

Queue w o r d s Q u e u e

new Q u e u e

в

очереди

I E n u m e r a t o r myEnumerator

"После

извлечения

в

один

элемент:

один

элемент:

очереди

myEnumeratorl

456

A.

Г. R Фролов. Язык C# Самоучитель


П о с л е запуска п р о г р а м м а создает очередь и з а п и с ы в а е т в нее 7 т е к с т о в ы х с т р о к фразы о х о т н и к ж е л а е т з н а т ь , где с и д и т Далее программа отображает на к о н с о л и к о л и ч е с т в о э л е м е н т о в и с о д е р ж и м о е очереди (при п о м о щ и и т е р а т о р а ) . По¬ сле э т о г о п р о г р а м м а п о к а з ы в а е т н а к о н с о л и с о д е р ж и м о е н а ч а л а о ч е р е д и , и з в л е к а е т из о ч е р е д и два э л е м е н т а , а затем вновь о т о б р а ж а е т всю о ч е р е д ь : Всего

в очереди

7

элементов:

каждый желает знать где фазан Первый каждый И з в л е к а е м один э л е м е н т : каждый И з в л е к а е м один э л е м е н т : охотник После и з в л е ч е н и я в о ч е р е д и пяти

элементов:

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

Битовый массив BitArray П о с л е д н и й к о н т е й н е р , к о т о р ы й мы р а с с м о т р и м в этой г л а в е , сив B i t A r r a y .

В

отличие

допускают выполнение NOT и

от обычных массивов типа b o o l

над собой

Для этого в

для хранения

это б и т о в ы й м а с ­ массивы

поразрядных операций, таких,

BitArray

как A N D ,

OR,

Bi t A r r a y п р е д у с м о т р е н ы с о о т в е т с т в у ю щ и е ме-

457


З а м е т и м , что, подобно массивам A r r a y , массивы B i t A r r a y н е д о п у с к а ю т и з м е ­ нения своих размеров после создания. П о п ы т к а з а п и с и или чтения за пределами мас­ сива

неизбежно приведет к возникновению исключения.

Н а п о м н и м , что значения л о г и ч е с к и х данных в языке п р о г р а м м и р о в а н и я С ставляются константами t r u e (истина) и

пред­

Эти значения никак не соот­

носятся с числовыми з н а ч е н и я м и , в ч а с т н о с т и со з н а ч е н и я м и 1 и 0.

Создание массива BitArray Для

создания

массива

BitArray

следует воспользоваться

одним

из

конструкторов,

определенных в классе B i t A r r a y . Простейший из этих конструкторов создает массив заданного размера, все которого

содержат значения

false.

Размер

создаваемого

массива

передается

конст¬

руктору в качестве п а р а м е т р а : BitArray Другие

byteArray

new

конструкторы

BitArray(8);

позволяют создать битовый

щ е с т в у ю щ е г о массива B i t A r r a y ,

массив

B i t A r r a y на базе су­

а т а к ж е массивов чисел b o o l , b y t e и

int.

Есть

т а к ж е в о з м о ж н о с т ь создания массива з а д а н н о г о размера, во всех ячейках к о т о р о г о из¬ начально будут записаны значения t r u e или

выбору программиста.

Инициализация ячеек массива Ячейки массива BitArray п р о щ е всего проинициализировать с помощью квадратных скобок, как это п о к а з а н о н и ж е : BitArray

byteArray

new

true; true;

false; С у щ е с т в у ю т и д р у г и е методы и н и ц и а л и з а ц и и ячеек массива. С помощью метода SetAll можно записать во все ячейки массива одно и то же например:

После в ы п о л н е н и я этой

операции

во всех ячейках массива b y t e A r r a y будет хра¬

ниться з н а ч е н и е t r u e . Чтобы у с т а н о в и т ь з н а ч е н и е для заданной я ч е й к и массива, воспользуйтесь методом Set: false); 458

А.

Фролов, Г.

Фролов. Язык

Самоучитель


В качестве п е р в о г о п а р а м е т р а этому методу п е р е д а е т с я индекс э л е м е н т а , а в каче¬ стве в т о р о г о — новое з н а ч е н и е . Таким о б р а з о м , в п р и в е д е н н о м в ы ш е п р и м е р е мы за¬ писываем значение ячейку массива с и н д е к с о м 10.

Извлечение значений из массива Для того ч т о б ы извлечь з н а ч е н и е из заданной ваться к в а д р а т н ы м и с к о б к а м и .

м а с с и в а , вы м о ж е т е воспользо¬

Эта п р о ц е д у р а вам и з в е с т н а по р а б о т е с о б ы ч н ы м и

м а с с и в а м и . К р о м е т о г о , м о ж н о извлечь з н а ч е н и е и з массива B i t A r r a y при п о м о щ и метода G e t , передав ему индекс н у ж н о г о элемента.

Просмотр массива П р о г р а м м а м о ж е т п о с л е д о в а т е л ь н о п р о с м о т р е т ь с о д е р ж и м о е всего м а с с и в а с помо¬ щью итератора IEnumerator. Это д е м о н с т р и р у е т с я н и ж е : System. C o l l e c t i o n s

I E n u m e r a t o r myEnumerator

else

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

Логические операции над массивами Н а наш

взгляд, — это

наиболее интересная

возможность,

выполнение логических операций

м а с с и в о в . В табл.

предоставляемая классом B i t ¬

над с о д е р ж и м ы м ц е л ы х б и т о в ы х

13.1 мы п е р е ч и с л и л и м е т о д ы , п р е д н а з н а ч е н н ы е для в ы п о л н е н и я по¬

р а з р я д н ы х б и т о в ы х операций над м а с с и в а м и класса B i t A r r a y . Таблица

Методы

для

выполнения

поразрядных

Операция

Метод

и

And

или

Or

ИСКЛЮЧАЮЩЕЕ

операций

ИЛИ

НЕ

Not

И с п о л ь з о в а н и е м е т о д о в для в ы п о л н е н и я п о р а з р я д н ы х о п е р а ц и й д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 13.29.

Глава

Контейнеры для хранения объектов

459


Листинг using using

13.29.

Файл

System;

namespace class static

void

args)

С BitArray

byteArray new true; false; true; true;

BitArray(8);

false;

myEnumerator

else

BitArray maskArray

maskArray maskArray maskArray maskArray maskArray maskArray maskArray

[0] [1] [2] [3] [5] [6] [7]

new

BitArray(8)

false false false true true; true; myEnumeratorl

460

А В.

Г В. Фролов. Язык

Самоучитель


else

BitArray

andArray

new

else

BitArray

orArray

new

else

Эта

программа

создает

и

инициализирует

два

битовых

и m a s k A r r a y , записывая в них различные логические значения: BitArray

byteArray new true; true;

Глава

Контейнеры для хранения объектов

массива,

byteArray


byteArray[4]

false; false; true; false;

BitArray maskArray new true; false; false;

BitArray

true

Исходные

этих массивов отображаются на консоли при помощи итерато­

ров. Вот, например, как отображается массив b y t e A r r a y : I E n u m e r a t o r myEnumerator

else

Массив m a s k A r r a y отображается аналогично. Далее программа выполняет над массивами b y t e A r r a y и m a s k A r r a y логиче­ скую операцию И, записывая ее результат в новый массив a n d A r r a y : BitArray

andArray

new

el s e

462

Фролов, Г.

Язык

Самоучитель


Результат

выполнения

операции

к о н с т р у к т о р у класса B i t A r r a y ,

с о з д а ю щ е м у массив

мы andArray.

передаем

Далее содержимое

массива a n d A r r a y о т о б р а ж а е т с я н а к о н с о л и . Аналогичным

образом

п р о г р а м м а в ы п о л н я е т над

этими

массивами

поразрядную

о п е р а ц и ю И Л И . В о т что п о я в и т с я н а к о н с о л и после з а в е р ш е н и я р а б о т ы н а ш е й п р о граммы: Число: Маска: AND: OR:

Глава

10110010 10001111 10000010 10001111

для хранения объектов

463


Глава 14. Файлы и потоки Б о л ь ш и н с т в о п р о г р а м м так или и н а ч е р а б о т а ю т с каталогами и ф а й л а м и . С р е д с т в а д л я р а б о т ы с ф а й л а м и п р е д у с м о т р е н ы п р а к т и ч е с к и в л ю б о м я з ы к е п р о г р а м м и р о в а н и я . Эти средства м о г у т быть н е п о с р е д с т в е н н о в я з ы к , а также в х о д и т ь в с т а н д а р т н ы е б и б л и о т е к и ф у н к ц и й и к л а с с о в , п о с т а в л я ю щ и х с я вместе с к о м п и л я т о р о м . П р о г р а м м ы , с о с т а в л е н н ы е на я з ы к е С # , р а б о т а ю т с каталогами и ф а й л а м и при по¬ м о щ и с п е ц и а л ь н о п р е д н а з н а ч е н н ы х для этого к л а с с о в , в х о д я щ и х в состав библиотеки классов Microsoft Framework.

Потоки данных и классы Все операции с файлами в п р о г р а м м а х С# с п о м о щ ь ю так н а з ы в а е м ы х потоков данных (data stream). п у т а й т е их с п о т о к а м и в ы п о л н е н и я (thread), о к о т о ­ рых мы р а с с к а з ы в а л и в гл. 10. П р о г р а м м ы м о г у т в ы п о л н я т ь над п о т о к а м и д а н н ы х 3 операции: •

запись д а н н ы х в п о т о к ,

ч т е н и е д а н н ы х из потока,

позиционирование.

Для п о т о к о в , с в я з а н н ы х с ф а й л а м и , о п р е д е л е н о т а к ж е т а к о е п о н я т и е , как т е к у щ а я п о з и ц и я в н у т р и файла. П е р е д н а ч а л о м о п е р а ц и й в в о д а - в ы в о д а п р о г р а м м а д о л ж н а о т к р ы т ь п о т о к . П р и этом т е к у щ а я п о з и ц и я у с т а н а в л и в а е т с я на н а ч а л о файла. П р и чтении ф а й л а или записи в файл б л о к о в д а н н ы х т е к у щ а я п о з и ц и я с д в и г а е т с я к концу файла на к о л и ч е с т в о бай¬ т о в , р а в н о е р а з м е р у п р о ч и т а н н о г о или з а п и с а н н о г о блока д а н н ы х . П р и п о м о щ и с р е д с т в п о з и ц и о н и р о в а н и я п р о г р а м м а м о ж е т у с т а н о в и т ь т е к у щ у ю п о з и ц и ю в произ¬ в о л ь н о е м е с т о ф а й л а . К о г д а р а б о т а с ф а й л о м з а к о н ч е н а , п р о г р а м м а о б я з а т е л ь н о долж¬ н а з а к р ы т ь с о о т в е т с т в у ю щ и й поток я в н ы м о б р а з о м . П р о г р а м м а С# м о ж е т р а б о т а т ь с п о т о к а м и н е с к о л ь к и х т и п о в : •

стандартными потоками ввода и вывода,

потоками, связанными с локальными файлами,

п о т о к а м и , с в я з а н н ы м и с ф а й л а м и в о п е р а т и в н о й памяти.

Р а с с м о т р и м к р а т к о классы б и б л и о т е к и Microsoft токами перечисленных выше типов.

F r a m e w o r k , с в я з а н н ы е с по¬

Стандартные потоки С т а н д а р т н ы е п о т о к и о б ы ч н о с в я з а н ы с к о н с о л ь ю и к л а в и а т у р о й , но м о г у т быть пере¬ н а п р а в л е н ы в ф а й л ы с р е д с т в а м и О С . По своему н а з н а ч е н и ю эти потоки б о л ь ш е всего н а п о м и н а ю т с т а н д а р т н ы е потоки ввода и в ы в о д а , а т а к ж е с т а н д а р т н ы й поток вывода сообщений об ошибках ОС M S - D O S .

464


П р а к т и ч е с к и все п р о г р а м м ы , и с х о д н ы е тексты к о т о р ы х п р и в о д и л и с ь в нашей кни¬ ге, р а б о т а ю т со с т а н д а р т н ы м и п о т о к а м и ввода и в ы в о д а . Для в ы в о д а д а н н ы х в стан¬ д а р т н ы й п о т о к в ы в о д а мы п р и м е н я л и м е т о д ы Write и Sys­ tem Console В в о д д а н н ы х из с т а н д а р т н о г о потока в ы п о л н я л с я с по­ мощью метода Console ReadLine.

Базовые классы для работы с файлами и потоками Количество классов, созданных для работы с файлами, достаточно велико и может при¬ вести начинающего программиста в растерянность. Прежде чем мы займемся конкретны¬ ми классами и приведем примеры п р и л о ж е н и й , р а б о т а ю щ и х с потоками и файлами, рас¬ смотрим иерархию классов, предназначенных для организации ввода и вывода.

Основные классы ввода и вывода Как и все классы в

о с н о в н ы е к л а с с ы ввода и в ы в о д а п р о и з о ш л и от класса Obj ect

(рис. 14.1).

Path

Основные

классы

для работы с

файлами

и потоками

Р а с с м о т р и м кратко их н а з н а ч е н и е . Класс B i n a r y R e a d e r предназначен для чтения блоков данных из потоков ввода на у р о в н е о т д е л ь н ы х б а й т о в . О б ы ч н о для о б ъ е к т о в из ф а й л о в , т а к и х , как стро¬ ки и ч и с л а , п р о г р а м м и с т ы и с п о л ь з у ю т д р у г и е , более м о щ н ы е классы. Класс с л у ж и т в к а ч е с т в е н и з к о у р о в н е в о г о средства записи дан¬ ных в п о т о к и в ы в о д а . Класс F i l e п р е д н а з н а ч е н для р а б о т ы с о г л а в л е н и я м и к а т а л о г о в . С п о м о щ ь ю ста¬ т и ч е с к и х м е т о д о в этого класса м о ж н о п о л у ч и т ь с п и с о к ф а й л о в и к а т а л о г о в , располо¬ ж е н н ы х в з а д а н н о м к а т а л о г е , с о з д а т ь или у д а л и т ь каталог, п е р е и м е н о в а т ь файл или каталог, а т а к ж е в ы п о л н и т ь н е к о т о р ы е д р у г и е о п е р а ц и и . С п о м о щ ь ю с т а т и ч е с к и х м е т о д о в класса D i r e c t o r y п р о г р а м м а м о ж е т п о л у ч а т ь списки к а т а л о г о в и п о д к а т а л о г о в , с о з д а в а т ь и у д а л я т ь к а т а л о г и , а т а к ж е в ы п о л н я т ь над к а т а л о г а м и д р у г и е п о д о б н ы е о п е р а ц и и . Класс P a t h о б е с п е ч и в а е т м е т о д ы и с в о й с т в а , с п о м о щ ь ю к о т о р ы х п р о г р а м м ы мо¬ гут р а б о т а т ь с и м е н а м и и п о л н ы м и путями к а т а л о г о в . Глава

14.

Файлы

и

465


Р а с с м о т р е н н ы е в п р е д ы д у щ е м разделе классы D i r e c t o r y и F i l e с о д е р ж а т т о л ь к о статические

члены

и

потому предназначены

для работы

с

файлами и

каталогами

без о б р а з о в а н и я э к з е м п л я р о в с о о т в е т с т в у ю щ и х о б ъ е к т о в . Классы

и

напротив, требуют создания экземпляров

о б ъ е к т о в , п р е д с т а в л я ю щ и х с о о т в е т с т в е н н о каталоги и ф а й л ы . Эти к л а с с ы унаследова¬ н ы о т о б щ е г о а б с т р а к т н о г о базового класса

к о т о р ы й , в с в о ю оче­

р е д ь , я в л я е т с я п р о и з в о д н ы м о т а б с т р а к т н о г о базового класса M a r s h a l B y R e f

ect

(рис. 14.2).

Рис. Абстрактный

базовый

14.2.

Классы на

бЭЗв

класс M a r s h a l B y R e f O b j e c t о б е с п е ч и в а е т в о з м о ж н о с т ь

д о с т у п а к о б ъ е к т а м через г р а н и ц ы д о м е н о в п р и л о ж е н и й , что н е о б х о д и м о в тех случа¬ ях, когда п р и л о ж е н и я в ы п о л н я ю т у д а л е н н у ю о б р а б о т к у д а н н ы х .

Классы для работы с потоками Н а базе класса

S t r e a m создано несколько производных классов,

ально п р е д н а з н а ч е н н ы х для р а б о т ы с п о т о к а м и ввода и в ы в о д а ( р и с . 14.3). Класс и вывода. Класс

S t r e a m с л у ж и т б а з о в ы м для д р у г и х к л а с с о в п о т о к о в о г о ввода как это

в и д н о и з его

н а з в а н и я , п р е д н а з н а ч е н для р а б о т ы

с ф а й л а м и ч е р е з потоки ввода и в ы в о д а . Он о б е с п е ч и в а е т п о з и ц и о н и р о в а н и е п о т о к о в ввода и

вывода методом

Seek.

Это п о з в о л я е т в ы п о л н я т ь п р я м о й д о с т у п

к файлам.

466

А В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


Рис.

14.3.

Классы для работы с

потоками

С п о м о щ ь ю класса м о ж н о о р г а н и з о в а т ь ввод и в ы в о д д а н н ы х в з а ш и ф р о в а н н о м виде. Класс п о з в о л я е т с о з д а в а т ь файлы в о п е р а т и в н о й п а м я т и . Д о с т у п к т а к и м ф а й л а м о с у щ е с т в л я е т с я очень б ы с т р о , так как не т р е б у е т с я р а б о т а т ь с мед¬ ленными дисковыми устройствами. Обычно файлы в оперативной памяти создаются д л я в р е м е н н о г о х р а н е н и я д а н н ы х , п о т о м у что п о с л е в ы к л ю ч е н и я к о м п ь ю т е р а содер¬ ж и м о е э т и х ф а й л о в исчезнет. С п о м о щ ь ю класса N e t w o r k s c r e a m м о ж н о о с у щ е с т в л я т ь у д а л е н н ы й в в о д и вы¬ вод д а н н ы х через сетевое с о е д и н е н и е . Класс B u f f о б е с п е ч и в а е т б у ф е р и з а ц и ю при работе с п о т о к а м и ввода и в ы в о д а . Б у ф е р и з а ц и я о п е р а ц и й ввода и в ы в о д а в б о л ь ш и н с т в е случаев з н а ч и т е л ь н о у с к о р я е т р а б о т у п р и л о ж е н и й , так как при ее и с п о л ь з о в а н и и с о к р а щ а е т с я к о л и ч е с т в о о б р а щ е н и й к с и с т е м е для о б м е н а д а н н ы м и с в н е ш н и м и у с т р о й с т в а м и .

Классы для работы с потоками текстовых символов С п е ц и а л ь н о д л я р а б о т ы с п о т о к а м и т е к с т о в ы х с и м в о л о в U N I C O D E в б и б л и о т е к е клас­ сов Microsoft Framework предусмотрены абстрактные классы S y s t e m . Reader и System. П е р в ы й и з этих к л а с с о в п р е д н а з н а ч е н д л я п о ­ т о к о в ввода, а в т о р о й для п о т о к о в в ы в о д а (рис. 14.4). С

помощью

класса

б а з о в ы м д л я к о т о р о г о я в л я е т с я класс п р о г р а м м а м о ж е т читать о т д е л ь н ы е байты в х о д н о г о по¬

тока символов.

Глава 14. Файлы и потоки

467


Рис.

14.4.

Классы

для работы

А н а л о г и ч н о класс

с

потоками

текстовых

созданный н а базе класса

символов System.

п о з в о л я е т писать байты с и м в о л о в в в ы х о д н о й поток. StringReader и для

и

StringWriter,

созданные

System.

на

базе

классов

Sys­

соответственно предназначены

и записи т е к с т о в ы х строк

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

Работа со стандартными потоками П р и л о ж е н и ю С# д о с т у п н ы 3 с т а н д а р т н ы х п о т о к а , к о т о р ы е всегда о т к р ы т ы : стандарт¬ ный п о т о к ввода, с т а н д а р т н ы й п о т о к в ы в о д а и с т а н д а р т н ы й п о т о к в ы в о д а с о о б щ е н и й об о ш и б к а х . п е р е ч и с л е н н ы е в ы ш е п о т о к и о п р е д е л е н ы в классе C o n s o l e . В ы м о ж е т е полу­ чить на них с с ы л к и с п о м о щ ь ю с т а т и ч е с к и х с в о й с т в I n , O u t и E r r o r .

Стандартный поток ввода С т а н д а р т н ы й п о т о к ввода, к о т о р ы й м о ж н о п о л у ч и т ь при п о м о щ и с т а т и ч е с к о г о свой¬ ства I n , о п р е д е л е н как с т а т и ч е с к и й о б ъ е к т класса T e x t R e a d e r . Э т о т класс с о д е р ж и т м е т о д ы , п р е д н а з н а ч е н н ы е для в в о д а о т д е л ь н ы х с и м в о л о в , строк и б л о к о в текста. В о т как п р о г р а м м а м о ж е т п о л у ч и т ь ссылку на с т а н д а р т н ы й п о т о к ввода: TextReader 468

А. В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


Чтобы ввести д а н н ы е из с т а н д а р т н о г о потока ввода (по у м о л ч а н и ю с в я з а н н о г о с к л а в и а т у р о й к о м п ь ю т е р а ) , и с п о л ь з у й т е один из м е т о д о в , о п р е д е л е н н ы х д л я этого в классе T e x t R e a d e r . В предыдущих примерах программ мы использовали метод ReadLine, ввод с и м в о л о в и з с т а н д а р т н о г о потока ввода. А н а л о г и ч н ы й м е т о д п р е д у с м о т р е н и в классе T e x t R e a d e r : string

s

Этот оператор вводит текстовую строку с клавиатуры и сохраняет ее в переменной s. В классе T e x t R e a d e r и м е ю т с я и д р у г и е м е т о д ы , п р е д н а з н а ч е н н ы е для ввода от¬ д е л ь н ы х с и м в о л о в и блока с и м в о л о в . R e a d , не и м е ю щ и й п а р а м е т р о в , читает один с и м в о л и в о з в р а щ а е т его как значение

типа

int.

При в о з н и к н о в е н и и

Если

символов

больше

нет,

о ш и б о к создается и с к л ю ч е н и е

метод

возвращает значение

IOException,

-1.

о б р а б о т к у кото¬

рого н е о б х о д и м о п р е д у с м о т р е т ь .

Стандартный поток вывода С т а н д а р т н ы й п о т о к в ы в о д а O u t создан н а базе класса

предназначенно¬

го для ф о р м а т и р о в а н н о г о в ы в о д а д а н н ы х р а з л и ч н о г о типа с ц е л ь ю их в и з у а л ь н о г о о т о б р а ж е н и я в виде т е к с т о в о й с т р о к и . Для того ч т о б ы п о л у ч и т ь с с ы л к у на с т а н д а р т н ы й п о т о к в ы в о д а , и с п о л ь з у й т е свой¬ ство TextWriter Далее, используя полученную ссылку, можно выводить данные с п о м о щ ь ю одного из м е т о д о в , о п р е д е л е н н ы х в классе T e x t W r i t e r , н а п р и м е р с п о м о щ ь ю х о р о ш о из­ вестного вам п о п р е д ы д у щ и м п р и м е р а м п р о г р а м м метода W r i t e L i n e : в

стандартный

поток

Этот м е т о д а н а л о г и ч е н м е т о д у C o n s o l e В классе T e x t W r i t e r о п р е д е л е н о н е с к о л ь к о р е а л и з а ц и й м е т о д а W r i t e

с пара¬

м е т р а м и р а з л и ч н ы х т и п о в . В о т н е к о т о р ы е и з них: public public public public public public public public public public public public

virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual

void void void void void void void void void void void void

Write Write Write Write Write Write Write Write

Глава 14. Файлы и потоки

469


Как в и д и т е , вы м о ж е т е записать в с т а н д а р т н ы й поток в ы в о д а т е к с т о в о е представ¬ л е н и е д а н н ы х р а з л и ч н о г о типа, в том числе и класса М е т о д W r i t e L i n e а н а л о г и ч е н методу W r i t e , о т л и ч а я с ь л и ш ь т е м , что о н добав¬ ляет к з а п и с ы в а е м о й в п о т о к строке с и м в о л п е р е х о д а на с л е д у ю щ у ю строку: public public public public public public public public public public public public

virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual

void void void void void void void void void void void void

WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine WriteLine

Р е а л и з а ц и я м е т о д а W r i t e L i n e без п а р а м е т р о в з а п и с ы в а е т т о л ь к о с и м в о л перехо¬ да на с л е д у ю щ у ю строку.

Стандартный поток вывода сообщений об ошибках С т а н д а р т н ы й п о т о к в ы в о д а с о о б щ е н и й об о ш и б к а х E r r o r , так же как и с т а н д а р т н ы й п о т о к в ы в о д а O u t , создан н а базе класса ний

об

ошибках

вы

можете

использовать

П о э т о м у для записи с о о б щ е ­ только

что

описанные

методы W r i t e

и WriteLine. Вот как п р о г р а м м а м о ж е т п о л у ч и т ь ссылку на с т а н д а р т н ы й п о т о к в ы в о д а сообще¬ ний об о ш и б к а х : TextWriter По у м о л ч а н и ю сообщения об ошибках выводятся на то же самое устройство вывода ( к о н с о л ь ) , на которое в ы в о д я т с я и с т р о к и , з а п и с ы в а е м ы е в с т а н д а р т н ы й поток вывода.

Программа StdStreams Приложение StdStreams демонстрирует способы работы со стандартными ввода, в ы в о д а и в ы в о д а с о о б щ е н и й об о ш и б к а х ( л и с т и н г 14.1). Листинг using using

14.1.

Файл

System;

namespace class

470

потоками

StdStreams StdStreamsApp

A

Фролов, Г. В. Фролов. Язык

Самоучитель


static

void

TextWriter TextWriter

args) twOut

в "Запись

в

стандартный

стандартный поток

поток

сообщений

об

TextReader любую string

строку:

s ввели

строку

s)

П о л у ч и в у п р а в л е н и е , м е т о д M a i n н а ш е й п р о г р а м м ы п о л у ч а е т с с ы л к и н а стандарт¬ ный п о т о к в ы в о д а и с т а н д а р т н ы й п о т о к в ы в о д а с о о б щ е н и й об о ш и б к а х : TextWriter TextWriter

twOut twErr

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

стандартный стандартный

поток поток сообщений

об

П о с л е этого н а ш а п р о г р а м м а п о л у ч а е т с с ы л к у на с т а н д а р т н ы й п о т о к ввода, кото¬ рой по у м о л ч а н и ю связан с к л а в и а т у р о й : TextReader О т о б р а з и в на э к р а н е с т р о к у п р и г л а ш е н и я , п р о г р а м м а в в о д и т из с т а н д а р т н о г о пото¬ ка ввода т е к с т о в у ю строку и з а п и с ы в а е т ее в п е р е м е н н у ю s: любую string

строку:

s

Далее введенная строка отображается на консоли следующим образом: ввели

строку

s)

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

Глава 14. Файлы и потоки

471


Создание потоков, связанных с файлами Если вам н у ж н о с о з д а т ь входной или в ы х о д н о й п о т о к , с в я з а н н ы й с л о к а л ь н ы м фай­ лом,

содержащим

двоичные

Writer и

данные,

следует

воспользоваться

и з б и б л и о т е к и Microsoft

ч т е н и я из файла или записи в файл

классами

Binary-

F r a m e w o r k . Что ж е

д а н н ы х , то для р е ш е н и я этой задачи

о б ы ч н о и с п о л ь з у ю т с я классы В л ю б о м случае, перед тем как читать данные из файла или записывать их в файл, не¬ обходимо

в ы п о л н и т ь о п е р а ц и ю открытия потока, связанного с файлом.

Когда работа

с файлом окончена, соответствующий поток необходимо закрыть я в н ы м образом. О б р а щ а е м в а ш е в н и м а н и е на т о , что система с б о р к и м у с о р а , в с т р о е н н а я в среду и с п о л н е н и я Microsoft

F r a m e w o r k , с л е д и т за и с п о л ь з о в а н и е м т о л ь к о о б ъ е к т о в , на¬

х о д я щ и х с я в о п е р а т и в н о й п а м я т и , т а к и х , как п е р е м е н н ы е , м а с с и в ы , э к з е м п л я р ы объ¬ е к т о в , с о з д а н н ы х на базе классов и т. п. Что же касается п о т о к о в , с в я з а н н ы х с файла¬ м и , то вы д о л ж н ы сами с л е д и т ь за их о т к р ы т и е м и з а к р ы т и е м .

Открытие потока FileStream Для

работы

ток к л а с с а

с двоичными FileStream,

файлами

ваша п р о г р а м м а д о л ж н а вначале создать

воспользовавшись

соответствующим

по¬

конструктором,

например: FileStream

fs

new

В качестве п е р в о г о п а р а м е т р а к о н с т р у к т о р у н е о б х о д и м о п е р е д а т ь п о л н ы й путь к файлу или имя ф а й л а , а в к а ч е с т в е в т о р о г о р е ж и м о т к р ы т и я потока (табл. 14.1). 14.1.

Режимы

открытия

файла

FileMode

Режим

Описание Если файл с у щ е с т в у е т , он о т к р ы в а е т с я . Т е к у щ а я п о з и ц и я уста¬ н а в л и в а е т с я на к о н е ц файла. Если у к а з а н н о г о ф а й л а нет, то он с о з д а е т с я . Этот р е ж и м м о ж н о и с п о л ь з о в а т ь т о л ь к о с о в м е с т н о с режимом доступа F i l e A c c e s s П р и п о п ы т к е ч т е н и я и з файла, о т к р ы т о г о п о д о б н ы м о б р а з о м , возникает исключение

Create

ОС д о л ж н а создать н о в ы й файл. Если у к а з а н н ы й файл уже с у щ е с т в у е т , он б у д е т п е р е з а п и с а н

CreateNew

ОС д о л ж н а

н о в ы й файл. Если у к а з а н н ы й файл уже

существует, возникнет исключение Open

IOException

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

-

FoundExcepti o 472

В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


Режим

Описание

OpenOrCreate

Если указанный файл он должен быть открыт. В противном случае ОС должна создать и открыть указанный файл

Truncate

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

В з а в и с и м о с т и от т о г о , для какой цели создается файл, вы м о ж е т е в ы б р а т ь тот или иной р е ж и м о т к р ы т и я потока. Н а п р и м е р , если п о т о к , с в я з а н н ы й с ф а й л о м , о т к р ы в а е т ­ ся т о л ь к о для ч т е н и я , в ы б и р а й т е р е ж и м В этом случае будет от¬ крыт с у щ е с т в у ю щ и й файл. Если же файл о т к р ы в а е т с я для з а п и с и , то вы м о ж е т е л и б о о т к р ы т ь с у щ е с т в у ю щ и й ф а й л , п е р е з а п и с а в его с о д е р ж и м о е или д о п и с а в в него н о в ы е д а н н ы е , а т а к ж е создать н о в ы й файл. В классе F i l e S t r e a m с у щ е с т в у е т н е с к о л ь к о к о н с т р у к т о р о в , п о з в о л я ю щ и х о п р е ­ д е л и т ь не т о л ь к о путь к файлу, но и р е ж и м его о т к р ы т и я , д о с т у п к файлу и р е ж и м с о в м е с т н о г о и с п о л ь з о в а н и я файла: FileStream FileStream

new new

О с т а н о в и м с я на р е ж и м е д о с т у п а к файлу, п е р е д а в а е м о м п р и в е д е н н ы м в ы ш е конст¬ р у к т о р а м ч е р е з третий п а р а м е т р (табл. 14.2). Р е ж и м д о с т у п а задается как с т а т и ч е с к а я к о н с т а н т а класса F i l e A c c e s s . Таблица

14.2.

Режимы

доступа

FileAccess

Режим Read

Тип

доступа

Д о с т у п т о л ь к о на чтение Д о с т у п на ч т е н и е и запись

Write

Доступ на запись

При необходимости программа может задать режим совместного использования файла, когда для одного и того же файла одновременно создается несколько потоков. Этот режим задается при помощи статических констант класса F i l e S h a r e (табл. 14.3). Таблица

14.3.

Режимы

Режим

FileShare

совместного Тип

доступа

Inheritable

Идентификатор файла может наследоваться д о ч е р н и м процессом

None

З а п р е т с о в м е с т н о г о и с п о л ь з о в а н и я файла. Л ю б ы е дополни¬ т е л ь н ы е з а п р о с ы на о т к р ы т и е файла будут з а п р е щ е н ы то тех п о р , пока файл не будет закрыт

Read

Д о п у с к а ю т с я д о п о л н и т е л ь н ы е запросы н а о т к р ы т и е файла для чтения

ReadWrite

Д о п у с к а ю т с я д о п о л н и т е л ь н ы е з а п р о с ы на ч т е н и е и запись

Wr i

Д о п у с к а ю т с я д о п о л н и т е л ь н ы е з а п р о с ы на запись

Глава 14. Файлы и потоки

473


Открытие потоков

и BinaryReader

И т а к , в а ш а п р о г р а м м а создала поток на базе файла, в о с п о л ь з о в а в ш и с ь для этого клас­ сом

Д а л е е н а базе п о л у ч е н н о г о т а к и м о б р а з о м п о т о к а н е о б х о д и м о соз­

дать потоки к л а с с о в B i n a r y W r i t e r и B i n a r y R e a d e r , п р е д н а з н а ч е н н ы е соответст¬ венно для з а п и с и в файл и чтения из файла д в о и ч н ы х д а н н ы х : BinaryWriter BinaryReader

br

new new

Р а б о т а с п о т о к а м и B i n a r y W r i t e r и B i n a r y R e a d e r будет о п и с а н а чуть позже.

Закрытие потоков П о с л е того как п р о г р а м м а з а в е р ш и л а работу с п о т о к а м и , она д о л ж н а их з а к р ы т ь . Для з а к р ы т и я п о т о к о в и с п о л ь з у й т е м е т о д C l o s e .

З а к р ы в а й т е потоки в п о р я д к е ,

о б р а т н о м о т к р ы т и ю . Если в ы вначале о т к р ы л и п о т о к F i l e S t r e a m , на его базе потоки класса B i n a r y W r i t e r и нужно в обратном

порядке:

вначале закройте

а затем создали

то з а к р ы в а т ь п о т о к и потоки B i n a r y W r i t e r

и

R e a d e r , а затем п о т о к F i l e S t r e a m : F i l e S t r e a m fs B i n a r y W r i t e r bw B i n a r y R e a d e r br Работа

с

new new new

потоками

Запись двоичных данных Д л я з а п и с и д в о и ч н ы х д а н н ы х в п о т о к B i n a r y W r i t e r п р е д у с м о т р е н о н е с к о л ь к о пе¬ регруженных методов W r i t e , public public public public public public public public public public public public

474

virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual virtual

приведенных ниже:

void void void void void void void void void void void void А. В.

Г.

Язык

Самоучитель


public public public public public

v i r t u a l void virtual void virtual void virtual void Write (byte virtual void

int, int,

int)

С п о с о б п р и м е н е н и я б о л ь ш и н с т в а п е р е ч и с л е н н ы х м е т о д о в и н т у и т и в н о я с е н : для записи д а н н ы х т о г о или иного типа в файл вам н у ж н о п е р е д а т ь ссылку на э к з е м п л я р д а н н ы х в качестве п а р а м е т р а методу W r i t e . В з а в и с и м о с т и от типа д а н н ы х компиля¬ тор в ы б е р е т н у ж н у ю п е р е г р у ж е н н у ю в е р с и ю этого метода. При з а п и с и в д в о и ч н ы й п о т о к т е к с т о в о й строки s t r i n g она будет п р е д в а р я т ь с я п р е ф и к с о м , с о д е р ж а щ и м з н а ч е н и е д л и н ы строки. С т р о к а будет з а п и с а н а в к о д и р о в к е ASCII. П о с л е д н и е два п е р е г р у ж е н н ы х м е т о д а W r i t e п о з в о л я ю т з а п и с а т ь в п о т о к м а с с и в с о о т в е т с т в е н н о д в о и ч н ы х байтов и с и м в о л о в U N I C O D E . В т о р о й п а р а м е т р м е т о д а за¬ д а е т н а ч а л ь н ы й индекс блока д а н н ы х в м а с с и в е , а т р е т и й — р а з м е р блока (соответст¬ венно в байтах и с и м в о л а х ) .

Чтение двоичных данных Чтобы прочитать данные из потока

н у ж н о и с п о л ь з о в а т ь один и з ме­

т о д о в , с п е ц и а л ь н о п р е д у с м о т р е н н ы х д л я этого в классе B i n a r y R e a d e r . М ы п р и в е л и к р а т к и й с п и с о к э т и х м е т о д о в в табл.

14.4.

Таблица

данных

14.4.

Методы

Метод

для

чтения

Тип

ReadBoolean

класса

данных,

BinaryReader

считываемых

из

потока

Один о б ъ е к т т и п а B o o l e a n

ReadByte

1 байт д а н н ы х

ReadBytes

Ч т е н и е з а д а н н о г о к о л и ч е с т в а байтов д а н н ы х Один т е к с т о в ы й с и м в о л

ReadChars

Чтение заданного количества текстовых символов Д е с я т и ч н о е з н а ч е н и е р а з м е р о м 16 байт З н а ч е н и е с п л а в а ю щ е й д е с я т и ч н о й т о ч к о й р а з м е р о м 4 байта З н а ч е н и е с п л а в а ю щ е й д е с я т и ч н о й т о ч к о й р а з м е р о м 8 байт

ReadSByte

Ч и с л о со з н а к о м р а з м е р о м 1 байт Ч и с л о со з н а к о м р а з м е р о м 2 байта Ч и с л о со знаком р а з м е р о м 4 байта Ч и с л о со з н а к о м р а з м е р о м 8 байт Ч и с л о без знака р а з м е р о м 2 байта Ч и с л о без знака р а з м е р о м 4 байта Ч и с л о без знака р а з м е р о м 8 байт

ReadString

Глава

Текстовая строка с префиксом длины

14.

Файлы

и

потоки

475


Программа Binary Программа

14.2) д е м о н с т р и р у е т н е к о т о р ы е п р и е м ы р а б о т ы с д в о и ч н ы м и

файлами при п о м о щ и классов

Листинг

14.2.

using using

System;

namespace

Binary

private static

и

const

string

testFile

void

args)

уже

существует",

return;

FileStream

fs

BinaryWriter

bw

for (uint i

0;

fs

string

for

new

i

20;

i++)

new

BinaryReader

476

new

br

new

s

(uint

i

0;

i

20;

i++)

А. В. Ф р о л о в , Г. В Ф р о л о в .


успешно

В самом начале своей работы

программа проверяет существование файла с именем

mydata.dat, и с п о л ь з у я д л я э т о г о м е т о д E x i s t s к л а с с а F i l e :

уже

Класс

существует",

предназначенный для работы с файлами и каталогами, мы рассмотрим

позже. П о к а же мы только з а м е т и м , что д а н н ы й

фрагмент кода проверяет существова­

ние файла mydata.dat в каталоге, который был т е к у щ и м при запуске нашей п р о г р а м м ы на выполнение. Если такой д ы д у щ е м запуске этой об

файл там уже существует ( н а п р и м е р , был создан при пре­

программы),

программа завершает свою работу с сообщением

ошибке. Если же в т е к у щ е м каталоге нет ф а й л а с и м е н е м

п р о г р а м м а с о з д а е т е г о , вы¬

зывая для этого конструктор класса F i l e S t r e a m с соответствующими параметрами: FileStream Так как мы

new указали

режим

создания

CreateNew,

потока

то

новый

файл будет создан в том случае, если файла с таким именем нет в т е к у щ е м каталоге. После

создания

потока

класса BinaryWriter

FileStream,

связанного

с

предназначенный для записи bw

файлом,

мы

создаем

поток bw

в файл двоичных данных:

new

На с л е д у ю щ е м шаге программа записывает в поток текстовую строку:

Далее п р о г р а м м а з а п и с ы в а е т в п о т о к bw 20 чисел типа for (uint i

0;

i

20;

После завершения записи

i+ + )

оба потока закрываются

методом

Close:

477


Н а с л е д у ю щ е м этапе наша п р о г р а м м а с ч и т ы в а е т с о д е р ж и м о е т о л ь к о что записан¬ ного файла и о т о б р а ж а е т его на к о н с о л и . Для этого она вначале о т к р ы в а е т п о т о к fs класса F i l e S t r e a m , у к а з ы в а я , что файл д о л ж е н быть о т к р ы т т о л ь к о для ч т е н и я : fs

new Режим чтения задается константой Далее н а базе п о т о к а f s п р о г р а м м а с о з д а е т поток

BinaryReader

br

класса

new

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

s

Далее она в ц и к л е с ч и т ы в а е т 20 чисел и т а к ж е о т о б р а ж а е т их на к о н с о л и : for

(uint i

0;

i

20;

i+ + )

З а в е р ш и в р а б о т у с п о т о к а м и , п р о г р а м м а з а к р ы в а е т их:

Д а л е е она в ы в о д и т на к о н с о л ь с о о б щ е н и е об у с п е ш н о м с о з д а н и и файла и ж д е т , ко¬ гда п о л ь з о в а т е л ь н а ж м е т к л а в и ш у Enter, ч т о б ы з а в е р ш и т ь с в о ю работу: успешно В о т что п о я в и т с я на консоли после з а п у с к а п р о г р а м м ы : Text string 0 1 2 3 4 5 б 7 8 9 10 11 12 13 14 15 16 17 18 19 Файл у с п е ш н о с о з д а н Содержимое файла, созданного нашей программой, показано на рис.

Рис. 478

14.5.

Содержимое

созданного

двоичного

А В Фролов, Г. В.

14.5.

файла Самоучитель


внимание, что в самом начале файла находится байт со значением ОхВ, вслед за которым идет текстовая строка в кодировке ASCII. Этот байт задает длину строки. В с л е д за строкой идут 20 ч и с е л , к а ж д о е из к о т о р ы х з а н и м а е т 4 байта.

Работа с текстовыми файлами Хотя

рассмотренные

в

предыдущем разделе

потоки

FileStream,

BinaryWriter

и B i n a r y R e a d e r м о ж н о и с п о л ь з о в а т ь для записи в файлы и чтения из ф а й л о в тек¬ с т о в ы х строк, л у ч ш е п р м е н я т ь с п е ц и а л ь н о п р е д н а з н а ч е н н ы е для э т о г о с р е д с т в а . Р е ч ь идет о п о т о к а х классов

и S t r e a m R e a d e r . Эти п о т о к и ч р е з в ы ч а й н о

п р о с т ы в и с п о л ь з о в а н и и и у д о б н ы для р а б о т ы с т е к с т о в ы м и ф а й л а м и . Основные приемы использования потоков S t r e a m W r i t e r и

S t r e a m R e a d e r де¬

м о н с т р и р у ю т с я в п р о г р а м м е , и с х о д н ы й текст которой п р и в е д е н в л и с т и н г е 14.3. Листинг

14.3.

using using

System; System.

namespace class

Файл

TextFile TextFileApp

private static

const

string

testFile

void

args)

уже

StreamWriter

sw

("Число

StreamReader

String if ( s t r

sr

охотник желает \ " П и \ " равно

з н а т ь , где примерно

сидит 3.1415926);

OpenText

str

Глава 14. Файлы и потоки

null)

479


("Файл

успешно

Как и п р е д ы д у щ а я п р о г р а м м а (см. л и с т и н г 14.2), н а ш а новая п р о г р а м м а сразу по¬ сле з а п у с к а п р о в е р я е т с у щ е с т в о в а н и е р а б о ч е г о ф а й л а в т е к у щ е м к а т а л о г е : private

const

string

testFile

if уже

Если файл с и м е н е м m y d a t a . t x t с у щ е с т в у е т в т е к у щ е м к а т а л о г е , п р о г р а м м а завер¬ шает с в о ю р а б о т у с с о о б щ е н и е м об о ш и б к е . В п р о т и в н о м случае п р о г р а м м а создает т е к с т о в ы й файл и о т к р ы в а е т п о т о к для р а б о т ы с ним класса StreamWriter Как в классе

видите, File.

sw эта

CreateText операция

Аналогичного

выполняется эффекта

методом

можно

было

CreateText, бы

достичь

определенным и

с

помощью

с л е д у ю щ е г о к о н с т р у к т о р а класса StreamWriter Первый

sw

параметр

new этого

StreamWriter ( t e s t F i l e , конструктора определяет полный

путь

к открываемому

файлу. Если з н а ч е н и е в т о р о г о п а р а м е т р а р а в н о t r u e , н о в ы е д а н н ы е б у д у т д о б а в л е н ы к файлу, а если

с о д е р ж и м о е файла будет п е р е з а п и с а н о .

Для о т к р ы т и я т е к с т о в о г о файла на запись вы м о ж е т е и с п о л ь з о в а т ь л ю б о й из этих к о н с т р у к т о р о в . З а м е т и м , что в классе р ы , п о з в о л я ю щ и е , н а п р и м е р , задать

имеются и другие конструкто­ кодировку текстовых символов,

в файл. После того как поток

открыт, программа может записывать в него

текстовые строки, пользуясь х о р о ш о известными вам ("Число

охотник желает знать, где \ " П и \ " равно примерно

и WriteLine: сидит 3.1415926);

К о г д а запись н о в ы х д а н н ы х в п о т о к з а в е р ш е н а , н е о б х о д и м о з а к р ы т ь п о т о к мето­ дом

Close:

о чтении д а н н ы х из т е к с т о в о г о файла. А В. Фролов. Г В Фролов.

Самоучитель


П р е ж д е всего п р о г р а м м а д о л ж н а о т к р ы т ь поток класса к файлу. Это м о ж н о сделать м е т о д о м StreamReader

привязав

sr

Вы м о ж е т е также о т к р ы т ь поток и п р и в я з а т ь его к файлу с п о м о щ ь ю к о н с т р у к т о р а класса

StreamReader:

StreamReader

sr

new

Далее н а ш а п р о г р а м м а с ч и т ы в а е т т е к с т о в ы е строки из файла, в ы з ы в а я в ц и к л е ме¬ тод R e a d L i n e :

String if str

str null)

Этот метод в о з в р а щ а е т п р о ч и т а н н у ю строку или з н а ч е н и е n u l l при д о с т и ж е н и и к о н ц а файла. П о с л е з а в е р ш е н и я р а б о т ы с п о т о к о м S t r e a m R e a d e r его с л е д у е т з а к р ы т ь м е т о д о м

Таким о б р а з о м , н а ш а п р о г р а м м а з а п и с ы в а е т две строки в файл, а затем ч и т а е т их оттуда и о т о б р а ж а е т на к о н с о л и : Каждый о х о т н и к ж е л а е т з н а т ь , г д е с и д и т Число "Пи" р а в н о п р и м е р н о 3 , 1 4 1 5 9 2 6 . Файл у с п е ш н о с о з д а н

фазан!

Выбор кодировки И с т о р и ч е с к и с л о ж и л о с ь так, что з а в с ю и с т о р и ю с у щ е с т в о в а н и я к о м п ь ю т е р о в для представления текстовых символов использовались самые разные кодировки. Когда ваша п р о г р а м м а создает ф а й л ы , она д о л ж н а в ы б р а т ь к а к у ю - т о к о н к р е т н у ю к о д и р о в к у текстовых символов. Те из в а с , кому, как и а в т о р а м этой к н и г и , п о с ч а с т л и в и л о с ь р а б о т а т ь на « б о л ь ш и х » к о м п ь ю т е р а х серии ЕС (аналоги к о м п ь ю т е р о в I B M 3 6 0 / 3 7 0 ) , з н а к о м ы с так н а з ы в а е ­ мой к о д и р о в к о й E B C D I C . В ней п р е д с т а в л я л и с ь л а т и н с к и е с и м в о л ы , с и м в о л ы кирил¬ л и ц ы , а также знаки п у н к т у а ц и и и у п р а в л я ю щ и е знаки. Что же касается с о в р е м е н н ы х п е р с о н а л ь н ы х к о м п ь ю т е р о в , то для п р е д с т а в л е н и я т е к с т о в ы х с и м в о л о в р а з л и ч н ы х а л ф а в и т о в в них и с п о л ь з у ю т с я кодовые страницы (Code Pages) и к о д и р о в к а U N I C O D E . Р а с с к а ж е м об этом п о д р о б н е е . Глава 16 Язык

Файлы и Самоучитель

481


Кодовые страницы До

ОС Microsoft W i n d o w s 95

и Microsoft W i n d o w s ш и р о к о

применялась

так н а з ы в а е м а я м о д е л ь к о д о в ы х с т р а н и ц . В этой модели к а ж д о м у с и м в о л у ставилось в с о о т в е т с т в и е ч и с л о в д и а п а з о н е от 0 до 2 5 5 . Таким о б р а з о м , для п р е д с т а в л е н и я одно¬ го с и м в о л а и с п о л ь з о в а л с я 1 байт. Каким способом устанавливается соответствие? Для ОС M S ­ D O S было известно по крайней мере н е с к о л ь к о таких с п о с о б о в , осно¬ ванных

на

применении

различных

таблиц

кодировок:

кодировка

ASCII,

основная

и а л ь т е р н а т и в н а я , а т а к ж е н е к о т о р ы е другие. Компания

Microsoft

стандартизовала

таблицы

кодировок,

назвав

их

кодовыми

с т р а н и ц а м и и п р и с в о и в к а ж д о й странице свой н о м е р . П е р в ы е версии M S ­ D O S работа¬ ли с р а с ш и р е н н ы м н а б о р о м с и м в о л о в I B M , в к о т о р о м о т с у т с т в о в а л и с и м в о л ы кирил¬ л и ц ы (рис. 14.6).

ее ее за

es

ez ез о

в

t 1

2

е

й

В

р V

q

к

и X

Ь г

6

а?

С S

с

S 4

t

6

а а

U

5 5

6

7

Е

г

G

и

Я

t

V.

Т

s

о 4

D T

it 3

V

У

I V

H X

i

К

F0

J

К

z

В

с

fl т

т

1

р

Г

Рис.

л А

-

V

p

1 L 11

к

9

X

N

4-

И

L Ч 1

0 •

t £

V

л

я

t

Ш

L I

Г

Г

i

J 0

ft V

т

Расширенный набор

r

I

m

ft

символов

IBM

Для т о г о ч т о б ы и с п р а в и т ь п о л о ж е н и е , в свое в р е м я было р а з р а б о т а н о немало ути¬ лит,

п р е д н а з н а ч е н н ы х д л я загрузки

символов

кириллицы

в

память

видеоадаптера,

а т а к ж е для п е р е к л ю ч е н и я р а с к л а д о к к л а в и а т у р ы . В ОС M S ­ D O S версии 4.01 в п е р в ы е п о я в и л а с ь с т а н д а р т н а я к о д о в а я с т р а н и ц а с но­ м е р о м 8 6 6 , с о д е р ж а щ а я с и м в о л ы к и р и л л и ц ы (рис. 14.7) ватели

С тех пор р о с с и й с к и е пользо¬

к о м п ь ю т е р о в не м о г у т п о ж а л о в а т ь с я на т о , что к о м п а н и я Microsoft о б х о д и т

их с в о и м в н и м а н и е м , с о з д а в а я н о в ы е ОС и п р и к л а д н ы е п р о г р а м м ы .

482

А В . Фролов, Г.


01

02

о

в

05 •

38

0

1

0 F

Й а

V.

4

Б

Е

я

S I a t А

Е X

с S с

Р й

Б

В

Г

Р

С 6

т

a

D t

§

*э в к ь

t

-4

1

0?

е и

7 Г и

е

L

1

т

f

к

h

т Е

е

т

Ё

с в

I

Г

и

X

4

Набор

14.7.

9

I

J г

К

i

J z

к

К

X В

И

Й

ч

с и

й

X

1

fl

п

3

1 L

-

'•

к

V I

э

С

1 Л

Л

*

1

a

я

в

ч В

н э

0

ь и

н

0 J

I

ь

ч

символов

н 1

1

ft J

1

а

страницы

п

э

• •

D

кодовой

П Я

я

866

З а м е т и м , что для п р е д с т а в л е н и я с и м в о л о в к и р и л л и ц ы в ОС OS/2 применяется к о д и р о в к а , с о о т в е т с т в у ю щ а я к о д о в о й с т р а н и ц е 866. П о л ь з о в а т е л и ОС U n i x и L i n u x отдают предпочтение кодировке а пользователи компьютеров Apple Macin­ своей с о б с т в е н н о й к о д и р о в к е , н е с о в м е с т и м о й ни с о д н о й из п е р е ч и с л е н н ы х в ы ш е . В И н т е р н е т е есть н е м а л о у т и л и т , п р е д н а з н а ч е н н ы х для п е р е к о д и р о в а н и я тек¬ стовых файлов, подготовленных в самых разных кодировках. п о я в и л а с ь ОС Microsoft W i n d o w s 3.1, в ней т а к ж е были п р е д у с м о т р е н ы к о ­ д о в ы е с т р а н и ц ы . К с о ж а л е н и ю , к о д о в ы е с т р а н и ц ы A N S I Microsoft W i n d o w s 3.1 были л и ш е н ы с и м в о л о в к и р и л л и ц ы (рис. 14.8). В1

ez

В7

j за

1

z

ft

в л ъ

ее аа 90

*3 с S с я

1 4 Т

а

е и

| в

*

F У

7

В

9

G

н И

I V

f

1 ¥

| I

i

Ъ

s

ь 14.8.

0

i

S

1

Кодовая

страница

t

J Е J

К

L Ч

к

1

Л

а

t |

t е

01

t

Ё

Ё

Ё

0

0 о 0 в

И

6

\ \

-

5 Е

t

0i

1

а

Ё

1

I В

t

I

а

ANSI

й

й

без

и

символов

кириллицы

Д л я ОС Microsoft W i n d o w s 3.1 было создано несколько популярных программ русифи¬ кации, устанавливающих ш р и ф т ы с символами кириллицы и переключатели клавиатур¬ ных раскладок. При этом применялась кодировка A NS I , показанная на рис. 14.9.

14. Файлы и

483


ее

06

07

7

Ос

1

10 га

в

1

р

А 0

10

Ч

г

*

4

5

6

С S с

F

Г

в

D Т А t

Е

в ь

н к

f

и

в

9

V

1

А Р а Р

ев

Рис. Программы

в

Ё Б С

с

14.9.

MS­DOS,

в т

д

*

А

Ч и

звание

страниц

Е X е

И

N

0

| в X

1 Ж ц ж

э ч а ч

И III

К ъ

й

Л ы л

ъ

страница

запущенные

оригинальных

L

0

ANSI с

под

0 ю а

П Я п

э

символами

14.6 и

производителей

н э

ь м ь

управлением

пользовались кодовыми страницами DOS Equipment

J Z J

1 1 S

У

Кодовая

У 1

X

\

ав

­

V.

3

кириллицы

ОС

Microsoft W i n d o w s ,

14.7), к о т о р ы е п о л у ч и л и на­

оборудования

OEM

(Original

Manufacturer).

С

р у с с к о й версии ОС Microsoft W i n d o w s 3.1

дартной. Соответствующая кодовая страница получила номер

эта к о д и р о в к а стала стан­ 1251.

К этому моменту было разработано огромное количество с символами

кириллицы,

коды

которых

находились

ш р и ф т о в True

в диапазоне

от СО до

FF.

И м е н н о эти ш р и ф т ы и с т а л и п о п у л я р н ы , р а с п р о с т р а н я я с ь в о с н о в н о м п и р а т с к и м и с п о с о б а м и в м е с т е с п р о г р а м м а м и р у с и ф и к а ц и и M i c r o s o f t W i n d o w s 3.1

или по от¬

дельности.

Недостатки модели кодовых страниц Х о т я на п е р в ы й в з г л я д м о ж е т п о к а з а т ь с я , что 1 байта в п о л н е д о с т а т о ч н о для п р е д с т а в ­ л е н и я всех с и м в о л о в , это не всегда так. П р е д с т а в ь т е себе, что вам н е о б х о д и м о п о д г о т о в и т ь д о к у м е н т , с о д е р ж а щ и й п о м и м о л а т и н с к и х с и м в о л о в и с и м в о л о в к и р и л л и ц ы , н а п р и м е р , г р е ч е с к и е и н е м е ц к и е симво¬ л ы , а т а к ж е д о п о л н и т е л ь н ы е знаки. О ч е в и д н о , ч т о , о с т а в а я с ь в р а м к а х к о д о в о й стра¬ н и ц ы 1251 или к а к о й ­ л и б о д р у г о й , т а к у ю задачу р е ш и т ь н е в о з м о ж н о . Ч т о б ы как­то выйти и з д а н н о й с и т у а ц и и , п р и х о д и т с я д о п о л н и т е л ь н о п р и м е н я т ь с п е ц и а л ь н ы е шриф¬ т ы , что у с л о ж н я е т п р о ц е с с о ф о р м л е н и я д о к у м е н т а . З а м е т и м т а к ж е , что в н е к о т о р ы х а л ф а в и т а х с л и ш к о м м н о г о с и м в о л о в , ч т о б ы и х коды м о ж н о было представить 1 байтом. В азиатских версиях Microsoft W i n d o w s п р и м е н я ю т с я м н о г о б а й т о в ы е н а б о р ы с и м в о л о в , где для п р е д с т а в л е н и я о д н о г о с и м в о л а и с п о л ь з у е т с я н е с к о л ь к о б а й т о в . Н а п р и м е р , в я п о н с к о й , к о р е й с к о й и ки¬ т а й с к о й в е р с и я х M i c r o s o f t W i n d o w s в ы м о ж е т е с т о л к н у т ь с я с 2 ­ б а й т о в ы м и симво¬ л а м и ( н а б о р с и м в о л о в do uble-byte character set, или DBCS), к о т о р ы е не с л е д у е т пу­ тать с с и м в о л а м и U N I C O D E .

484

А В. Фролов, Г.

Язык

Самоучитель


Стандарт UNICODE С т а н д а р т U N I C O D E был п р е д л о ж е н н е к о м м е р ч е с к о й о р г а н и з а ц и е й U N I C O D E C o n s o r о б р а з о в а н н о й в 1991 г. В этом с т а н д а р т е для п р е д с т а в л е н и я к а ж д о г о с и м в о л а ис¬ пользуется 16 бит, т. е. 2 байта. С п о м о щ ь ю U N I C O D E м о ж н о з а к о д и р о в а т ь очень б о л ь ш о е к о л и ч е с т в о с и м в о л о в и з р а з н ы х а л ф а в и т о в , б л а г о д а р я чему о т п а д а е т необхо¬ д и м о с т ь п р и м е н я т ь к о д о в ы е с т р а н и ц ы . В д о к у м е н т а х U N I C O D E могут в с т р е ч а т ь с я одновременно латинские и греческие символы, символы кириллицы и технические З н а ч е н и я кодов U N I C O D E р а з д е л е н ы н а н е с к о л ь к о о б л а с т е й . Область с кодами от 0000 до 007F п р и м е н я е т с я т а к и м же о б р а з о м , как и п е р в а я по¬ л о в и н а р а с ш и р е н н о г о набора с и м в о л о в I B M , п о к а з а н н а я на рис. 14.6. Далее идут об¬ ласти, в которых расположены греческие символы, символы кириллицы, символы п у н к т у а ц и и и т е х н и ч е с к и е с и м в о л ы , а т а к ж е з а р е з е р в и р о в а н н ы е о б л а с т и . Для симво¬ лов к и р и л л и ц ы , н а п р и м е р , в ы д е л е н ы коды в д и а п а з о н е от 0 4 0 0 до 0451.

U n i c o d e в Microsoft W i n d o w s Я д р о Microsoft W i n d o w s г р а ф и ч е с к и й и н т е р ф е й с GDI этих ОС и ф а й л о ­ вая система NTFS р е а л и з о в а н ы с и с п о л ь з о в а н и е м U N I C O D E . Тем не менее п р и л о ж е ­ ния, з а п у щ е н н ы е в среде Microsoft W i n d o w s N T / 2 0 0 0 / X P , могут работать и с одной ко¬ д о в о й с т р а н и ц е й A N S I . Она в ы б и р а е т с я а в т о м а т и ч е с к и в з а в и с и м о с т и от т о г о , какая страна была в ы б р а н а п о л ь з о в а т е л е м при у с т а н о в к е О С . Приложения, применяющие кодовую страницу ANSI, перед вызовом некоторых ф у н к ц и й п р о г р а м м н о г о и н т е р ф е й с а Microsoft W i n d o w s N T в ы п о л н я ю т п р е о б р а з о в а н и е к о д и р о в к и в U N I C O D E с у ч е т о м номера к о д о в о й с т р а н и ц ы A N S I . Для того ч т о б ы та¬ кое п р е о б р а з о в а н и е в ы п о л н я л о с ь без о ш и б о к , п о л ь з о в а т е л ь д о л ж е н п р а в и л ь н о у к а з а т ь в п р и л о ж е н и и Regional Settings, п и к т о г р а м м а к о т о р о г о н а х о д и т с я в п а п к е Con­ trol Panel. Это т а к ж е н е о б х о д и м о и для п р а в и л ь н о й р а б о т ы п р о г р а м м M S - D O S , запу¬ щ е н н ы х п о д у п р а в л е н и е м Microsoft W i n d o w s N T / 2 0 0 0 / X P и и с п о л ь з у ю щ и х к о д о в у ю страницу O E M . Очень в а ж н о , что для р а б о т ы с д о к у м е н т а м и U N I C O D E т р е б у ю т с я ш р и ф т ы U N I C O D E . О д и н файл т а к о г о ш р и ф т а с о д е р ж и т н е с к о л ь к о н а б о р о в с и м в о л о в , соответ¬ ствующих разным областям кодов U N I C O D E . Разработчик шрифта может разместить в файле не все, а только н е к о т о р ы е н а б о р ы с и м в о л о в .

U N I C O D E в Microsoft W i n d o w s 95 В о т л и ч и е от Microsoft W i n d o w s я д р о и г р а ф и ч е с к и й и н т е р ф е й с Microsoft W i n d o w s 95 не и с п о л ь з у ю т U N I C O D E , а р а б о т а ю т с к о д о в ы м и с т р а н и ц а м и . Тем не м е ­ нее в Microsoft W i n d o w s 95 п р е д у с м о т р е н а в о з м о ж н о с т ь д и н а м и ч е с к о г о п е р е к л ю ч е н и я н а б о р о в с и м в о л о в , а т а к ж е к л а в и а т у р н ы х р а с к л а д о к . Это п о з в о л я е т создавать доку¬ менты, содержащие одновременно символы из разных наборов. В составе Microsoft W i n d o w s 95 п о с т а в л я е т с я набор ш р и ф т о в U N I C O D E . Вы т а к ж е у с т а н о в и т ь в этой ОС и кириллические шрифты, разработанные для Microsoft W i n d o w s

глава 14. Файлы и

485


Кодировка текстовых потоков П о с л е т а к о г о к р а т к о г о в в е д е н и я в к о д и р о в к и с и м в о л о в мы р а с с м о т р и м с п о с о б ы выбо¬ ра к о д и р о в к и для т е к с т о в ы х п о т о к о в , с о з д а в а е м ы х в п р о г р а м м а х С#. К о д и р о в к а с и м в о л о в задается в к о н с т р у к т о р е , с о з д а ю щ е м в ы х о д н о й п о т о к д а н н ы х . Н а п р и м е р , при с о з д а н и и п о т о к а т е к с т о в ы х с т р о к S t r e a m W r i t e r м ы м о ж е м указать в т р е т ь е м п а р а м е т р е к о н с т р у к т о р а н у ж н у ю нам к о д и р о в к у : StreamWriter

Кодировка coding.

sw

new

указывается

false,

как

Возможные варианты

статическое кодировки

свойство

класса

с именами соответствующих статических

свойств п е р е ч и с л е н ы в табл. 14.5. Таблица

14.5.

Кодировки

текстовых

потоков

Кодировка

Описание

ASCII

К о д и р о в к а A S C I I без с и м в о л о в к и р и л л и ц ы , в которой для пред¬ с т а в л е н и я т е к с т о в ы х с и м в о л о в и с п о л ь з у ю т с я м л а д ш и е 7 бит байта

UNICODE

К о д и р о в к а U N I C O D E . Для п р е д с т а в л е н и я с и м в о л о в и с п о л ь з у е т с я бит (т. е. 2 байта) К о д и р о в к а U C S Transformation F o r m a t . П р и м е н я е т с я для представ¬ л е н и я с и м в о л о в U N I C O D E . В ней и с п о л ь з у ю т с я м л а д ш и е 7 бит данных То ж е , но для п р е д с т а в л е н и я с и м в о л о в U N I C O D E в ней использует¬ ся 8 бит д а н н ы х к о д и р о в к а A N S I (не п у т а й т е ее с к о д и р о в к о й ASCII). В этой к о д и р о в к е для п р е д с т а в л е н и я к а ж д о г о с и м в о л а и с п о л ь з у е т с я 8 бит д а н н ы х

З а м е т и м , что к о д и р о в к а с и м в о л о в м о ж е т быть задана т о л ь к о при с о з д а н и и потока. Если п о т о к у ж е с о з д а н , п р о г р а м м а м о ж е т о п р е д е л и т ь т е к у щ у ю к о д и р о в к у симво¬ л о в , в о с п о л ь з о в а в ш и с ь для э т о г о с в о й с т в о м E n c o d i n g : файла: Это с в о й с т в о д о с т у п н о т о л ь к о для ч т е н и я . В л и с т и н г е 14.4 мы п р и в е л и п р и м е р п р о г р а м м ы , с о з д а ю щ е й т е к с т о в ы й файл в ко¬ дировке UNICODE. Листинг using using

14.4. System;

namespace

486

Файл

Encoding

А В Фролов, Г В Фролов Язык

Самоучитель


class

EncodingApp

private static

const

string

testFile

void

args)

уже

StreamWriter

sw

new

существует",

StreamWriter ( t e s t F i l e ,

файла:

("Число

StreamReader

String if(str

о х о т н и к ж е л а е т з н а т ь , где \ " П и \ " равно примерно

сидит 3.1415926);

sr

str null)

Console

("Файл

успешно

От п р о г р а м м ы , и с х о д н ы й т е к с т к о т о р о й был п р и в е д е н р а н е е в она о т л и ч а е т с я т о л ь к о н а л и ч и е м д в у х с т р о к , п р и в е д е н н ы х н и ж е : StreamWriter

sw

new

глава 14. Файлы и потоки

StreamWriter

14.3,

false,

487


из этих с т р о к задает к о д и р о в к у с и м в о л о в U N I C O D E , а вторая ст к о д и р о в к у с и м в о л о в на к о н с о л и : Кодировка

отобража-

файла:

Каждый охотник желает знать, где сидит ф а з а н ! Число "Пи" равно примерно 3 , 1 4 1 5 9 2 6 . Файл успешно создан На рис.

14.10 мы п о к а з а л и

с о з д а н н о г о файла в виде д а м п а .

-

14.10.

Созданный

файл

в

кодировке

UNICODE

Обращаем ваше в н и м а н и е , что в самом начале файла находятся байты OxFF и OxFE. Это

п р е д н а з н а ч е н н а я для о п р е д е л е н и я п о р я д к а с л е д о в а н и я байтов в

16-

разрядных символах U N I C O D E . Д л я чего она н у ж н а ? С т а н д а р т U N I C O D E п р е д н а з н а ч е н д л я р а б о т ы н а р а з л и ч н ы х к о м п ь ю т е р н ы х плат­ ф о р м а х . О д н а к о р а з н ы е п р о ц е с с о р ы п р и м е н я ю т р а з л и ч н ы й п о р я д о к с л е д о в а н и я байтов в слове:

наиболее

значимый

байт м о ж е т

быть

расположен

как по

старшему,

так

и по м л а д ш е м у а д р е с у . Для правильной обработки текстового файла U N I C O D E на л ю б о й платформе прило¬ жение д о л ж н о знать порядок следования байтов, использованный в этом файле. Для обо¬ значения такого порядка и применяется 2-байтовая

Ее значение может быть

равно OxFEFF для прямого порядка следования байтов или OxFFEF для обратного.

Кодировка текстовых строк в двоичных потоках Как мы уже г о в о р и л и , д в о и ч н ы е потоки создаются с п о м о щ ь ю классов F i l e S t r e a m , BinaryWriter и BinaryReader.

С п о м о щ ь ю метода W r i t e программа может запи­

сывать в д в о и ч н ы й поток не только числа, но и строки класса s t r i n g . С о о т в е т с т в у ю щ и й пример п р о г р а м м ы был приведен в разделе «Программа

488

(см. листинг 14.2).

А В Фролов. Г, В. Фролов. Язык

Самоучитель


Н а п о м н и м , что при записи с т р о к s t r i n g в д в о и ч н ы й поток они с н а б ж а ю т с я пре¬ ф и к с о м д л и н ы , вслед за к о т о р ы м идут байты с и м в о л о в . По у м о л ч а н и ю для представ¬ ления с и м в о л о в п р и м е н я е т с я к о д и р о в к а о д н а к о при н е о б х о д и м о с т и п р о г р а м м а м о ж е т ее и з м е н и т ь . При создании в ы х о д н о г о потока класса руктору кодировку текстовых символов: F i l e S t r e a m fs B i n a r y W r i t e r bw

t e r м ы м о ж е м указать конст­

new F i l e S t r e a m ( t e s t F i l e , new

В д а н н о м случае мы у к а з а л и к о д и р о в к у U N I C O D E . П о с л е э т о г о запись строк в п о т о к в ы п о л н я е т с я как о б ы ч н о :

Для того ч т о б ы п р а в и л ь н о п р о ч и т а т ь т е к с т о в ы е строки из д в о и ч н о г о п о т о к а , при с о з д а н и и в х о д н о г о п о т о к а B i n a r y R e a d e r м ы т о ж е д о л ж н ы указать к о д и р о в к у : fs new F i l e S t r e a m ( t e s t F i l e , B i n a r y R e a d e r br new

FileMode.

П о с л е э т о г о м о ж н о будет п р о ч и т а т ь строку и з в х о д н о г о д в о и ч н о г о п о т о к а м е т о д о м string

s

Эта т е х н и к а д е м о н с т р и р у е т с я в л и с т и н г е 14.5. Листинг using using

14.5.

в

программе,

исходный

Файл

System;

namespace class private static

const

string

testFile

void

args)

уже

FileStream

fs

BinaryWriter

глава 14. Файлы и потоки

new bw

new B i n a r y W r i t e r

текст

которой

приведен


bw. for (uint i

fs

new

0;

for

20;

i+ + )

FileStream(testFile,

BinaryReader

string

i

br

new Encoding

s

(uint

i

0;

i

20;

i+ + )

успешно

К а к в ы г л я д и т т е к с т о в а я с т р о к а U N I C O D E в с о з д а н н о м нами д в о и ч н о м файле? В з г л я н и т е на р и с . где мы п о к а з а л и д а м п этого файла.

Рис.

490

14.11.

Текст

в

двоичном

потоке

имеет

кодировку

UNICODE

А В. Фролов, Г. В. Фролов. Язык

Самоучитель


О б р а т и т е в н и м а н и е , что п е р в ы й байт файла с о д е р ж и т д л и н у т е к с т о в о й с т р о к и . В с л е д за этим байтом идут 2 - б а й т о в ы е с и м в о л ы U N I C O D E . Если при чтении т а к о г о файла класса в ы з а б у д е т е ука­ зать к о д и р о в к у с и м в о л о в , результат м о ж е т быть н е о ж и д а н н ы м : T e x t s t r i n g О 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Файл у с п е ш н о с о з д а н В с а м о м д е л е , так как с и с т е м а не знает з а р а н е е , в какой к о д и р о в к е н а х о д я т с я сим¬ в о л ы , она будет и с п о л ь з о в а т ь к о д и р о в к у по у м о л ч а н и ю , т. е. A N S I . В р е з у л ь т а т е вме¬ сто о д н о г о с и м в о л а т е к с т о в о й строки мы у в и д и м с и м в о л и п р о б е л . Если же к о д и р о в к а у к а з а н а п р а в и л ь н о , п р о г р а м м а в ы в е д е т на к о н с о л ь с т р о к у без о ш и б о к в и с х о д н о м виде: Text string 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Файл у с п е ш н о с о з д а н

Буферизация потоков Для у с к о р е н и я о п е р а ц и й о б м е н а д а н н ы м и с д и с к о м часто п р и м е н я е т с я м е х а н и з м бу¬ ф е р и з а ц и и . Он основан на т о м , что прежде п о п а с т ь на д и с к , д а н н ы е п р е д в а р и ­ т е л ь н о н а к а п л и в а ю т с я в б у ф е р е , р а с п о л о ж е н н о м в о п е р а т и в н о й п а м я т и . З а т е м , когда к диску нет о б р а щ е н и й , д а н н ы е из буфера п е р е п и с ы в а ю т с я на диск. У с к о р е н и е д о с т и г а е т с я за счет т о г о , что ф а к т и ч е с к и п р о г р а м м а п и ш е т д а н н ы е не на д и с к , а в о п е р а т и в н у ю п а м я т ь , о б л а д а ю щ у ю в ы с о к и м б ы с т р о д е й с т в и е м . Во в р е м я про¬ стоев к о м п ь ю т е р а , когда он не з а н я т д р у г о й р а б о т о й , д а н н ы е п е р е п и с ы в а ю т с я на диск. Р а з у м е е т с я , б у ф е р и з а ц и я дает у с к о р е н и е р а б о т ы п р о г р а м м не всегда, а т о л ь к о в тех с л у ч а я х , когда п р о г р а м м а п е р и о д и ч е с к и о б р а щ а е т с я к о д н и м и тем же ф р а г м е н т а м файла или в ы п о л н я е т п о с л е д о в а т е л ь н о е чтение или з а п и с ь файла.

Буферизация двоичных потоков Н а п о м н и м , что д в о и ч н ы е потоки с о з д а ю т с я при п о м о щ и классов F i l e S t r e a m , B i n a r y W r i t e r и B i n a r y R e a d e r . Потоки F i l e S t r e a m п о умолчанию являются б у ф е р и з о в а н н ы м и . Для з а д а н и я размера б у ф е р а м ы д о л ж н ы и с п о л ь з о в а т ь соответст¬ вующий перегруженный FileStream

fs

new 10000);

Через п е р в ы й п а р а м е т р этому к о н с т р у к т о р у п е р е д а е т с я путь к файлу, с к о т о р ы м с в я з ы в а е т с я поток. В т о р о й п а р а м е т р задает р е ж и м о т к р ы т и я файла. Третий и четвер¬ тый п а р а м е т р о п р е д е л я ю т с о о т в е т с т в е н н о р е ж и м д о с т у п а к файлу и р е ж и м совместно¬ го и с п о л ь з о в а н и я файла. Что же касается р а з м е р а буфера, то он задастся п о с л е д н и м , п я т ы м п а р а м е т р о м и у к а з ы в а е т с я в байтах. Глава 14. Файлы

491


С о з д а в т а к и м с п о с о б о м поток класса основе поток bw

о б р а з о в а т ь н а его

new

Д а л е е этот п о т о к м о ж н о и с п о л ь з о в а т ь для записи как о б ы ч н о , д о б а в л я я в него дан¬ ные м е т о д о м W r i t e . Если вам нужен в ы х о д н о й б у ф е р и з о в а н н ы й д в о и ч н ы й п о т о к , то он создается так: fs

new В д а л ь н е й ш е м на базе э т о г о п о т о к а м о ж н о создать в х о д н о й поток класса B i n a r y -

BinaryReader

br

new

BinaryReader

Работа с буферизованными двоичными потоками данных демонстрируется в про­ г р а м м е , и с х о д н ы й т е к с т к о т о р о й п р и в е д е н в л и с т и н г е 14.6. Мы о с т а в л я е м ее вам на с а м о с т о я т е л ь н о е и з у ч е н и е . Листинг using using

14.6.

Файл

System;

namespace class private static

const

string

testFile

void

args)

Console

"Файл

уже

return;

FileStream

492

fs

new

BinaryWriter

bw

for ( u i n t

0;

i

new

i

20;

BinaryWriter

i+ + )

А. В Фролов, Г.

Фролов. Язык

Самоучитель


fs

fs

new 10000);

BinaryReader string

for

br

new

s

(uint i

i

20;

i+ + ) br R e a d U I n t 3 2

успешно

Буферизация текстовых потоков Потоки,

связанные с текстовыми

файлами, также

нужно воспользоваться соответствующими

можно

буферизовать.

Для

этого

п е р е г р у ж е н н ы м и к о н с т р у к т о р а м и в клас¬

сах S t r e a m W r i t e r и S t r e a m R e a d e r . В о т как создается в ы х о д н о й б у ф е р и з о в а н н ы й т е к с т о в ы й поток: StreamWriter

Первые 3

sw

new

StreamWriter

false,

п а р а м е т р а к о н с т р у к т о р а класса S t r e a m W r i t e r з а д а ю т с о о т в е т с т в е н н о

путь к файлу, флаг д о б а в л е н и я н о в ы х д а н н ы х в файл и к о д и р о в к у д а н н ы х . Р а з м е р бу¬ фера о п р е д е л я е т с я в байтах при п о м о щ и ч е т в е р т о г о п а р а м е т р а . В х о д н о й б у ф е р и з о в а н н ы й п о т о к д а н н ы х создается при п о м о щ и с л е д у ю щ е г о конст¬ р у к т о р а класса StreamReader

sr

new

StreamReader ( t e s t F i l e , UNICODE, t r u e ,

Размер буфера задается п о с л е д н и м , ч е т в е р т ы м п а р а м е т р о м к о н с т р у к т о р а . О б р а т и т е в н и м а н и е н а третий п а р а м е т р к о н с т р у к т о р а класса S t r e a m R e a d e r . Ес¬ ли этот параметр равен t r u e , при о т к р ы т и и файла его тип будет а в т о м а т и ч е с к и опре¬ д е л я т ь с я по с и г н а т у р е , т. е. по н а ч а л ь н ы м байтам файла. Н а п о м н и м , что т е к с т о в ы е глава 14. Файлы и потоки

493


файлы в к о д и р о в к е U N I C O D E с о д е р ж а т 2 - б а й т о в у ю с и г н а т у р у , о п р е д е л я ю щ у ю док

о с т а л ь н ы х байтов файла. Об этом мы р а с с к а з ы в а л и в ы ш е в разделе

«Кодировка текстовых потоков». П о л н ы й п р и м е р п р о г р а м м ы , р а б о т а ю щ е й с б у ф е р и з о в а н н ы м и т е к с т о в ы м и потока¬ м и , п р и в е д е н в л и с т и н г е 14.7. Листинг using using

14.7.

Файл

System; System.

namespace

f

class private static

const

string

testFile

void

args)

уже

StreamWriter

sw

new

("Число

sr

существует",

StreamWriter ( t e s t F i l e ,

о х о т н и к ж е л а е т з н а т ь , где \ " П и \ " равно примерно

false,

сидит 3.1415926);

new true,

String str if(str break;

null)

("Файл

494

успешно

А.

Фролов, Г.

Фролов. Язык

Самоучитель


Принудительный сброс буферов Е щ е один в а ж н ы й м о м е н т связан с б у ф е р и з о в а н н ы м и п о т о к а м и . Как мы у ж е г о в о р и л и , б у ф е р и з а ц и я у с к о р я е т работу п р и л о ж е н и й с п о т о к а м и , так как при ее и с п о л ь з о в а н и и сокращается о б р а щ е н и й к с и с т е м е в в о д а - в ы в о д а . Вы м о ж е т е п о с т е п е н н о , в течение д н я , д о б а в л я т ь в п о т о к д а н н ы е 1 байту, и только к вечеру эти д а н н ы е бу¬ д у т ф и з и ч е с к и з а п и с а н ы в файл на д и с к е . Во м н о г и х с л у ч а я х , о д н а к о , п р и л о ж е н и е д о л ж н о , не о т к а з ы в а я с ь совсем от буфери¬ з а ц и и , в ы п о л н я т ь п р и н у д и т е л ь н у ю запись б у ф е р о в в файл. Это м о ж н о с д е л а т ь с по¬ мощью метода F l u s h . Д а н н ы й м е т о д о п р е д е л е н во всех к л а с с а х , и м е ю щ и х о т н о ш е н и е к б у ф е р и з о в а н н ы м п о т о к а м . В о т как он и с п о л ь з у е т с я с б у ф е р и з о в а н н ы м и т е к с т о в ы м и п о т о к а м и д а н н ы х : StreamWriter

sw

new

StreamWriter 1000

false,

("Каждый о х о т н и к ж е л а е т з н а т ь , ("Число \ " П и \ " р а в н о п р и м е р н о

где

сидит

В этом ф р а г м е н т е кода мы создали в ы х о д н о й б у ф е р и з о в а н н ы й п о т о к д а н н ы х , запи¬ сали

в

него две строки

методом W r i t e L i n e ,

а затем

сбросили

буферы методом

F l u s h . З а к о н ч и в работу с п о т о к о м , м ы его з а к р ы л и м е т о д о м C l o s e .

Потоки в оперативной памяти ОС Microsoft W i n d o w s 95 и Microsoft W i n d o w s п р е д о с т а в л я ю т на у р о в н е своего п р о г р а м м н о г о и н т е р ф е й с а W i n 3 2 в о з м о ж н о с т ь р а б о т а т ь с о п е р а т и в н о й памя¬ т ь ю как с ф а й л о м . Это очень у д о б н о во м н о г и х с л у ч а я х , в ч а с т н о с т и , ф а й л ы , отобра¬ ж а е м ы е н а п а м я т ь , м о ж н о и с п о л ь з о в а т ь для п е р е д а ч и д а н н ы х между о д н о в р е м е н н о р а б о т а ю щ и м и з а д а ч а м и и п р о ц е с с а м и . П о д р о б н е е об этом вы м о ж е т е п р о ч и т а т ь в 2 7 - м томе системного к о т о р ы й называется « О п е р а ц и о н н а я с и с т е м а Microsoft W i n d o w s NT для п р о г р а м м и с т а . Часть 2» [12]. П р и с о з д а н и и п р о г р а м м С# вы т а к ж е м о ж е т е р а б о т а т ь с о б ъ е к т а м и о п е р а т и в н о й памяти как с ф а й л а м и , а точнее г о в о р я , как с п о т о к а м и . Ранее мы о т м е ч а л и , что в б и б л и о т е к е классов Microsoft F r a m e w o r k есть класс с п е ц и а л ь н о п р е д н а з н а ч е н н ы й для с о з д а н и я п о т о к о в в о п е р а т и в н о й памяти. Р а с с м о т р и м о с н о в н ы е п р и е м ы его и с п о л ь з о в а н и я .

Создание потока Для создания п о т о к о в в о п е р а т и в н о й п а м я т и в классе предусмотрено несколько конструкторов. П р о с т е й ш и й к о н с т р у к т о р не имеет п а р а м е т р о в и п о з в о л я е т создать поток с буфе¬ р о м , и м е ю щ и м нулевой н а ч а л ь н ы й new Глава 14


По мерс данных в этот поток размер буфера автоматически увеличивается. Вы м о ж е т е создать поток на базе м а с с и в а б а й т о в , как это показа­ но ниже: new b y t e О,

1,

2,

3,

4,

MemoryStream

5,

б,

7,

8,

9

new

С о з д а н н ы й т а к и м с п о с о б о м п о т о к будет с о д е р ж а т ь д а н н ы е массива. З а м е т и м , что размер т а к о г о п о т о к а не м о ж е т быть у в е л и ч е н (однако при н е о б х о д и м о с т и его м о ж н о уменьшить методом S e t L e n g t h ) . П р е д у с м о т р е н т а к ж е в а р и а н т к о н с т р у к т о р а , п о з в о л я ю щ и й задать н а ч а л ь н ы й р а з м е р б у ф е р а п о т о к а , к о т о р ы й будет у в е л и ч и в а т ь с я при н е о б х о д и м о с т и п о м е р е д о б а в л е н и я в поток новых данных: M e m o r y S t r e a m ms

new

Здесь мы создали п о т о к с н а ч а л ь н ы м р а з м е р о м буфера, р а в н ы м 1000 байт. П р и н е о б х о д и м о с т и м о ж н о создать п о т о к , д о с т у п н ы й т о л ь к о д л я ч т е н и я . Это мож¬ но с д е л а т ь с п о м о щ ь ю с л е д у ю щ е г о к о н с т р у к т о р а : data 0,

1,

2,

new b y t e 3,

4,

5,

б,

7,

MemoryStream

8,

9

new M e m o r y S t r e a m

false)

В д а н н о м с л у ч а е п о т о к m s R e a d O n l y будет д о с т у п е н т о л ь к о д л я ч т е н и я . П р и н е о б х о д и м о с т и м о ж н о создать п о т о к не из всего м а с с и в а , а т о л ь к о из его час¬ ти. Для этого н у ж н о у к а з а т ь к о н с т р у к т о р у н а ч а л ь н ы й и н д е к с и р а з м е р блока д а н н ы х , из к о т о р о г о б у д е т о б р а з о в а н м а с с и в : data 0,

1,

2,

new b y t e 3,

4,

5,

6,

7,

8,

9

M e m o r y S t r e a m ms new M e m o r y S t r e a m 1, MemoryStream msReadOnly new M e m o r y S t r e a m н а базе части массива d a t a м ы создаем два П е р в ы й из них д о с т у п е н для ч т е н и я и з а п и с и , а

5) 1,

5,

false)

и msReadOnly. только для чтения.

Чтение данных Если в ы с о з д а л и п о т о к M e m o r y S t r e a m н а базе п р о и н и ц и а л и з и р о в а н н о г о б а й т о в , т о м о ж е т е читать и з него д а н н ы е м е т о д а м и R e a d и R e a d B y t e .

массива

Первый из них читает блок д а н н ы х , заданных смещением от начала потока и размером, возвращая прочитанные д а н н ы е в виде массива. Текущая позиция продвигается вперед на количество байтов, равное размеру прочитанного блока. Второй метод ( R e a d B y t e ) чита¬ ет из потока 1 байт, продвигая т е к у щ у ю п о з и ц и ю вперед на 1 байт.

496

В Фролов, Г. В Фролов,

С# Самоучитель


Техника

данных из потока M e m o r y S t r e a m демонстрируется в программе,

и с х о д н ы й т е к с т к о т о р о й приведен в л и с т и н г е 14.8. Листинг

14.8.

using using

Файл

ch

System;

namespace class static

void data 0,

1,

byte[]

new b y t e

2,

3,

4,

5,

dataCopy

6,

new

MemoryStream

8,

9

byte

new 0,

foreach

7,

(byte

bt

in

9); dataCopy)

М а с с и в d a t a , н а базе к о т о р о г о будет с о з д а в а т ь с я п о т о к M e m o r y S t r e a m , п р о и н и ц и а л и з и р о в а н ц е л ы м и ч и с л а м и от 0 до 9, как это п о к а з а н о н и ж е : byte[] 0,

data 1,

2,

new b y t e 3,

4,

5,

б,

7,

8,

9

К р о м е т о г о , в п р о г р а м м е определен н е и н и ц и а л и з и р о в а н н ы й м а с с и в байтов d a t a C o p y р а з м е р о м в 10 ячеек: byte

dataCopy

new b y t e

В н а ч а л е наша п р о г р а м м а создает поток M e m o r y S t r e a m ms 14.

н а базе м а с с и в а d a t a :

new M e m o r y S t r e a m Файлы

и

потоки

497


Далее она читает содержимое всего потока в массив d a t a C o p y :

Вслед з а этим с о д е р ж и м о е массива d a t a C o p y , проинициализированного содер­ жимым потока, отображается на консоли: foreach

(byte

bt

in

dataCopy)

Завершив работу с потоком m s , программа закрывает его методом C l o s e :

Запись данных Для записи данных в поток M e m o r y S t r e a m вы можете использовать методы W r i t e и W r i t e B y t e . Первый из этих методов записывает в поток 1 байт данных, а — массив байтов или часть этого массива. В листинге 14.9 мы привели пример программы, которая сначала записывает д а н ­ ные в поток по байтам, а затем читает данные из этого потока и ото¬ бражает их на консоли. 14.9. using using

Файл

System;

namespace class static

void

args)

M e m o r y S t r e a m ms for (byte i

int

data

0;

new M e m o r y S t r e a m i

10;

i+ + )

ms -1)

break;

498

В.

Г. В. Фролов. Язык

Самоучитель


П е р в ы м д е л о м программа создает поток с п о м о щ ь ю п р о с т е й ш е г о к о н с т р у к т о р а без параметров: MemoryStream

new

Как мы у ж е г о в о р и л и , для х р а н е н и я д а н н ы х такого п о т о к а в о п е р а т и в н о й п а м я т и с о з д а е т с я буфер нулевой д л и н ы . Р а з м е р этого б у ф е р а будет а в т о м а т и ч е с к и увеличи¬ ваться по мере записи в п о т о к н о в ы х д а н н ы х . Д а л е е н а ш а п р о г р а м м а в ц и к л е з а п и с ы в а е т в п о т о к 10 ч и с е л , п о л ь з у я с ь для этого методом for (byte i

0;

i

10;

i+ + )

Д а л е е , д л я того чтобы из п о т о к а м о ж н о было ч и т а т ь , нам н е о б х о д и м о с б р о с и т ь бу¬ фер м е т о д о м F l a s h , а т а к ж е п е р е м е с т и т ь т е к у щ у ю п о з и ц и ю в начало потока:

П о д р о б н е е о п о з и ц и о н и р о в а н и и внутри п о т о к а мы р а с с к а ж е м п о з ж е , а с е й ч а с заме¬ тим т о л ь к о , что методу S e e k п е р е д а ю т с я д в а п а р а м е т р а . П е р в ы й п а р а м е т р у к а з ы в а е т с м е щ е н и е в байтах, а второй место в п о т о к е , от к о т о р о г о о т с ч и т ы в а е т с я это с м е щ е ­ ние. К о н с т а н т а задает с м е щ е н и е о т н о с и т е л ь н о н а ч а л а п о т о к а . З а м е т и м , ч т о , если бы мы просто з а к р ы л и п о т о к , его д а н н ы е были бы для нас поте¬ р я н ы , и мы не смогли бы их п р о ч и т а т ь . О д н а к о после с б р о с а буфера и у с т а н о в к и те¬ к у щ е й п о з и ц и и н а н а ч а л о п о т о к а д а н н ы е д о с т у п н ы для ч т е н и я . Н а ш а п р о г р а м м а с ч и т ы в а е т и х п о б а й т н о , в ы з ы в а я д л я этого м е т о д R e a d B y t e :

int

data

if (data

-1)

Этот метод в о з в р а щ а е т з н а ч е н и е с ч и т а н н о г о байта, п р е о б р а з о в а н н о е в тип i n t , или з н а ч е н и е если был д о с т и г н у т к о н е ц потока. П о с л е о т о б р а ж е н и я всех д а н н ы х , х р а н я щ и х с я в п о т о к е , мы з а к р ы в а е м п о т о к мето¬ дом C l o s e :

14. Файлы и

499


Turn static files into dynamic content formats.

Create a flipbook
Issuu converts static files into: digital portfolios, online yearbooks, online catalogs, digital photo albums and more. Sign up and create your flipbook.