Issuu on Google+

public xPos

class byte byte byte

yPos

ColorRectangle

width

height

0;

Rectangle

colorR; colorG;

слово s e a l e d позволяет застраховаться от ошибочного к л а с с о в , не п р е д н а з н а ч е н н ы х для в ы с т у п л е н и я в роли б а з о в ы х к л а с с о в .

А В Фролов, Г.

Фролов Язык

наследования

Самоучитель


Глава 4. Полиморфизм Н а р я д у с и н к а п с у л я ц и е й и н а с л е д о в а н и е м полиморфизм п р е д с т а в л я е т собой о д н у и з в а ж н е й ш и х к о н ц е п ц и й О О П . П р и м е н е н и е этой к о н ц е п ц и и п о з в о л я е т з н а ч и т е л ь ­ но разработку сложных программ. Термин полиморфизм имеет греческое п р о и с х о ж д е н и е и о з н а ч а е т многих Н а р я д у с п р о г р а м м и р о в а н и е м этот т е р м и н и м е е т х о ж д е н и е в б и о л о г и и и х и м и и . В о д н о м из у ч е б н и к о в по б и о л о г и и мы н а ш л и такое о п р е д е л е н и е п о л и м о р ­ физма: н а л и ч и е у одного вида н е с к о л ь к и х форм тела или т и п о в ок­ раски» [5]. В х и м и и этот т е р м и н применяется для о б о з н а ч е н и я с о е д и н е н и й , к о т о р ы е могут к р и с т а л л и з о в а т ь с я в р а з л и ч н ы х ф о р м а х . Я в л е н и е и з м е н е н и я к р и с т а л л и ч е с к о й с т р у к т у р ы о д н о г о и того же в е щ е с т в а при и з м е н е н и е в н е ш н и х у с л о в и й н а з ы в а е т с я в х и м и и п о л и м о р ф и з м о м [6]. Т и п и ч н ы й у г л е р о д , и м е ю щ и й две к р и с т а л л и ­ ческие ф о р м ы . И г р а ф и т и а л м а з я в л я ю т с я у г л е р о д о м , но какая м е ж д у ними р а з н и ц а ! В п р о г р а м м и р о в а н и и с п о л и м о р ф и з м о м тесно связаны т а к и е п о н я т и я , как абст­ рактные классы, виртуальные методы, перегрузка методов и операторов. Частично пе­ р е г р у з к а м е т о д о в у ж е была р а с с м о т р е н а в п р е д ы д у щ е й главе. Здесь же вы у з н а е т е До¬ полнительные подробности.

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

Применение классов В н а ч а л е мы п о п ы т а е м с я р е ш и т ь эту задачу и з в е с т н ы м нам на д а н н ы й м о м е н т спосо¬ бом без и с п о л ь з о в а н и я п о л и м о р ф и з м а ( л и с т и н г 4.1). Листинг

4.1.

Файл

using System; namespace Shape class

Point

i n t x; n t у public

x, x;

t

i s

га 6

Язык С#

Самоучитель

у

int

y)


public

void

x,

int

y) точки

в

x,

x; у;

class

Rectangle

int int int int

x; y; w; h;

public

x,

int y,

int

int

h)

x;

this public

void

Draw(int

x,

int

y) прямоугольника

в

У) this

class

x;

ShapeApp

static

void

Point

pt

Rectangle

Мы

будем

args) new

rect

работать

с

Point(10,

new R e c t a n g l e ( l ,

точками

в классе P o i n t , а п р я м о у г о л ь н и к и

и

4,

10,

прямоугольниками.

20)

Точки

инкапсулируются

в классе R e c t a n g l e .

в н и м а н и е , как м ы и н и ц и а л и з и р у е м поля класса P o i n t в конструкторе: А. В. Фролов, Г. В. Фролов. Язык С#. Самоучитель


public

Point ( i n t x,

int

y)

x; у; Здесь м ы н а м е р е н н о в ы б р а л и для п а р а м е т р о в к о н с т р у к т о р а имена, с о в п а д а ю щ и е с и м е н а м и с о о т в е т с т в у ю щ и х полей класса. При в ы п о л н е н и и п р и с в а и в а н и я нам н у ж н о п о м о ч ь к о м п и л я т о р у сделать р а з л и ч и е м е ж д у п а р а м е т р а м и метода и п о л я м и класса, называющимися одинаково. Эта задача р е ш а е т с я с п р и м е н е н и е м к л ю ч е в о г о слова это к л ю ч е в о е слово о з н а ч а е т т е к у щ и й о б ъ е к т класса P o i n t .

В данном контексте

Если бы п а р а м е т р ы к о н с т р у к т о р а и поля класса н а з ы в а л и с ь п о - р а з н о м у , в и с п о л ь ­ зовании к л ю ч е в о г о слова н е было б ы н и к а к о й public x у

Point (int

int

yNew)

xNew; yNew;

Для того ч т о б ы п р о г р а м м а могла н а р и с о в а т ь т о ч к у и п р я м о у г о л ь н и к , мы о п р е д е л и ­ ли в классах P o i n t и R e c t a n g l e м е т о д ы с н а з в а н и е м Draw. Эти м е т о д ы и м е ю т оди¬ н а к о в ы е п а р а м е т р ы , о д н а к о н е с к о л ь к о р а з л и ч а ю т с я п о своему д е й с т в и ю . И с х о д н ы й текст метода D r a w , п р е д н а з н а ч е н н о г о для р и с о в а н и я т о ч к и , п р е д с т а в л е н ниже: public

void

Draw(int

х,

int

у) точки

в

х,

у)

х;

С о б с т в е н н о р и с о в а н и е мы з а м е н я е м в ы в о д о м на к о н с о л ь строки с о о б щ е н и я о т о м , что т о ч к а н а р и с о в а н а в т а к о м - т о месте к о о р д и н а т н о й п л о с к о с т и . П о с л е этого м е т о д и з м е н я е т т е к у щ и е к о о р д и н а т ы т о ч к и . О б р а т и т е в н и м а н и е , что здесь м ы и с п о л ь з о в а л и к л ю ч е в о е слово t h i s , ч т о б ы к о м п и л я т о р смог сделать р а з л и ч и е м е ж д у н а з в а н и я м и п а р а м е т р о в метода и н а з в а н и я м и полей класса. Метод Draw, с помощью которого можно следующим образом: public

void

Draw(int

Console

х,

int

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

у)

("Рисование

прямоугольника

в

х,

у; От п р е д ы д у щ е г о метода он о т л и ч а е т с я т е к с т о м с о о б щ е н и я , о т о б р а ж а е м о г о на кон¬ соли. Глава 4.


когда у нас есть классы с м е т о д а м и D r a w , м о ж н о п р и с т у п а т ь к р и с о в а н и ю фигур. Н а ш а п р о г р а м м а р и с у е т ф и г у р ы , создавая по очереди о б ъ е к т ы к а ж д о г о класса и в ы з ы в а я для этих о б ъ е к т о в м е т о д Draw: pt

new

Rectangle

rect

new R e c t a n g l e ( l ,

4,

10,

20);

В а ж н о , что в п е р в о м случае в ы з ы в а е т с я метод D r a w из класса P o i n t , а во вто¬ ром — м е т о д D r a w и з класса R e c t a n g l e .

обобщения с помощью наследования В

п р и м е р е мы имели д е л о всего с двумя ф и г у р а м и — точкой и п р я м о ­

у г о л ь н и к о м . В с е г о ж е , как и з в е с т н о из ш к о л ь н о г о курса г е о м е т р и и , с у щ е с т в у е т вели¬ кое м н о ж е с т в о р а з н ы х фигур. З а д а д и м с я в о п р о с о м , м о ж н о ли о б о б щ и т ь п о в е д е н и е р а з л и ч н ы х фигур в одном ба­ зовом к л а с с е , о т р а з и в о с о б е н н о с т и к а ж д о й фигуры в с о о т в е т с т в у ю щ е м п р о и з в о д н о м классе? С о з д а д и м б а з о в ы й класс S h a p e , п р и з в а н н ы й и н к а п с у л и р о в а т ь в себе о б щ и е эле­ м е н т ы п о в е д е н и я всех ф и г у р . Н а базе класса S h a p e с о з д а д и м классы P o i n t и R e c и н к а п с у л и р у ю щ и х в себе о с о б е н н о с т и п о в е д е н и я т о ч е к и п р я м о у г о л ь н и к о в . И с х о д н ы й т е к с т п р о г р а м м ы , р е а л и з у ю щ е й д а н н у ю с т р у к т у р у к л а с с о в , представлен в л и с т и н г е 4.2. Листинг

4.2.

1

Файл

cs

System; namespace Shapel class

Shape

protected protected public xPos xPos

class

xPos; yPos; x,

int

y)

x; y;

Point

public

int int

Shape

P o i n t ( i n t x,

i n t y)

base

A

(x,

y)

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

Самоучитель


public

void

x,

int

y) точки

xPos xPos

class

в

x,

у)

x; y;

Rectangle

Shape

int int public

x,

width height public

int

int w,

int

h)

base(x,

w; h;

void

x,

int

y) прямоугольника

x, xPos yPos

class

y)

в

у) х; у;

ShapelApp

static

void

Point

pt

Rectangle

args) new

rect

new

4,

10,

20);

Что же нам у д а л о с ь о б ъ е д и н и т ь в базовом классе S h a p e т а к о г о , что я в л я е т с я ха­ р а к т е р н ы м для фигур л ю б о г о типа? К а ж д а я фигура и м е е т свои к о о р д и н а т ы н а п л о с к о с т и . П о л о ж е н и е точки о д н о з н а ч н о к о о р д и н а т а м и по осям X и Y, а п о л о ж е н и е п р я м о у г о л ь н и к а мы о п р е д е по к о о р д и н а т а м его л е в о г о н и ж н е г о угла.

Глава 4. Полиморфизм


С о о т в е т с т в е н н о в базовом классе S h a p e мы о п р е д е л и л и поля x P o s и y P o s , пред¬ н а з н а ч е н н ы е д л я х р а н е н и я к о о р д и н а т фигуры (не в а ж н о , какой и м е н н о ) н а п л о с к о с т и . В этом же классе имеется к о н с т р у к т о р , и н и ц и а л и з и р у ю щ и й д а н н ы е поля: class

Shape

p r o t e c t e d i n t xPos p r o t e c t e d int yPos; public S h a p e ( i n t x, xPos xPos

int

y)

x; y;

Что же касается м е т о д а , в ы п о л н я ю щ е г о р и с о в а н и е ф и г у р , то он для к а ж д о й фигуры свой. П о э т о м у мы не с м о ж е м о п р е д е л и т ь м е т о д D r a w в б а з о в о м к л а с с е , а в ы н у ж д е н ы создавать свой м е т о д D r a w в к а ж д о м п р о и з в о д н о м классе. Р а с с м о т р и м класс P o i n t , с о з д а н н ы й н а базе класса S h a p e : class

Point

public

Shape

P o i n t ( i n t x,

int y)

public

x,

base

(x,

int точки

xPos xPos

y)

в

x,

x; y;

Как в и д и т е , в нем о п р е д е л е н к о н с т р у к т о р , в ы з ы в а ю щ и й к о н с т р у к т о р базового ме¬ тода с д в у м я п а р а м е т р а м и . Н а л и ч и е т а к о г о к о н с т р у к т о р а н е о б х о д и м о для т о г о , ч т о б ы о т м е н и т ь вызов к о н с т р у к т о р а по у м о л ч а н и ю базового класса без п а р а м е т р о в и в ы з в а т ь н у ж н ы й к о н с т р у к т о р базового класса с д в у м я п а р а м е т р а м и . М е т о д D r a w класса P o i n t и м и т и р у е т р и с о в а н и е точки в ы в о д о м н а консоль соот¬ ветствующего сообщения. А н а л о г и ч н о у с т р о е н и класс R e c t a n g l e : class int int

Rectangle

Shape

width;

public width height

166

x, -

int

y,

int

int

h)

base(x,

y)

w; h; Г,

Фролов.

С#. Самоучитель


public

void

x,

Console xPos x; yPos

int

y) прямоугольника

в

x,

Этот класс также создан на базе класса S h a p e . По с р а в н е н и ю с б а з о в ы м к л а с с о м мы д о б а в и л и в него два н о в ы х п о л я , х р а н я щ и х р а з м е р ы п р я м о у г о л ь н и к а , и з м е н и л и к о н с т р у к т о р и метод Draw. К о н с т р у к т о р п р о и з в о д н о г о класса R e c t a n g l e вначале в ы з ы в а е т к о н с т р у к т о р ба¬ зового класса, с о х р а н я я в с о о т в е т с т в у ю щ и х полях ( x P o s и x P o s ) к о о р д и н а т ы распо¬ л о ж е н и я п р я м о у г о л ь н и к а н а п л о с к о с т и . Д а л е е этот к о н с т р у к т о р и н и ц и а л и з и р у е т поля w i d t h и h e i g h t , хранящие размеры прямоугольника. П о с л е п р и м е н е н и я н а с л е д о в а н и я с п о с о б р а б о т ы с к л а с с а м и , о п р е д е л е н н ы м и в на¬ шей п р о г р а м м е , никак не и з м е н и л с я по с р а в н е н и ю с п р е д ы д у щ е й п р о г р а м м о й (см. ли¬ стинг 4.1): Point

pt

Rectangle

new

rect

new

4,

10,

П о - п р е ж н е м у для р и с о в а н и я фигур нам н у ж н о создавать два о б ъ е к т а р а з н ы х клас¬ сов, в ы з ы в а я для к а ж д о г о из них свой м е т о д D r a w . П р и м е н е н и е н а с л е д о в а н и я д о н е к о т о р о й степени у л у ч ш и л о структуру н а ш е й про¬ г р а м м ы , так как нам у д а л о с ь в ы н е с т и в базовый класс все о б щ е е , что есть у н а ш и х фи¬ гур. О д н а к о на этом с о в е р ш е н с т в о еще не д о с т и г н у т о — мы не м о ж е м и с п о л ь з о в а т ь базовый класс для р и с о в а н и я фигур.

Виртуальные методы В п р е д ы д у щ е й главе мы р а с с к а з ы в а л и вам о в о з м о ж н о с т и п е р е о п р е д е л е н и я м е т о д о в базового класса в п р о и з в о д н о м классе. Тогда же мы у п о м и н а л и к л ю ч е в о е слово n e w , с п о м о щ ь ю к о т о р о г о н у ж н о о т м е т и т ь в п р о и з в о д н о м классе п е р е о п р е д е л е н н ы й м е т о д , чтобы он и с п о л ь з о в а л с я в м е с т о м е т о д а с тем же и м е н е м , но о б ъ я в л е н н ы м в б а з о в о м М о ж н о ли и с п о л ь з о в а т ь д а н н у ю т е х н и к у , ч т о б ы р и с о в а т ь ф и г у р ы с п о м о щ ь ю ме¬ тода D r a w , о б ъ я в л е н н о г о в б а з о в о м классе? Н е т , так как м е т о д д о ч е р н е г о класса, о б ъ я в л е н н ы й с к л ю ч е в ы м словом n e w , полно¬ стью с к р ы в а е т о д н о и м е н н ы й м е т о д базового класса. Н а м же т р е б у е т с я , чтобы про¬ грамма как-то и с п о л ь з о в а л а м е т о д D r a w базового класса чернего класса. Глава 4. Полиморфизм

для р и с о в а н и я фигуры до¬


Рассмотрим следующий кода, и с п о л ь з у ю щ и й н у ж н у ю нам ф у н к ц и о н а л ь ­ ность (здесь м ы п р е д п о л а г а е м , что п р о и з в о д н ы е классы P o i n t и R e c t a n g l e о б р а з о ­ ваны о т о д н о г о базового класса S h a p e ) : Shape

pt

new

Shape

rect

25);

new

4,

10,

20);

О б р а т и т е в н и м а н и е , что мы создаем два объекта дочерних к л а с с о в , з а п и с ы в а я ссылки на эти о б ъ е к т ы в п е р е м е н н у ю с т и п о м базового класса. Такая о п е р а ц и я вполне д о п у с т и м а в я з ы к е С#. Далее мы и с п о л ь з у е м о б ъ е к т ы базового класса для р и с о в а н и я р а з н ы х ф и г у р , вызы¬ вая м е т о д D r a w , о б ъ я в л е н н ы й в б а з о в о м классе. Чтобы т а к о е стало в о з м о ж н ы м , н е о б х о д и м о в б а з о в о м и д о ч е р н е м классах объ­ явить метод D r a w с п е ц и а л ь н ы м о б р а з о м ( л и с т и н г 4.3). Листинг

4.3.

using System; namespace ShapeVirtual class

Shape

protected protected public

S h a p e ( i n t x,

xPos xPos

public

class

-

int

y)

x;

virtual

Point

void

x,

int

y)

Shape

public

P o i n t ( i n t x,

public

override

xPos

168

int int

void

i n t y)

base

Draw(int

x,

(x,

y)

int

y)

точки

в

x,

y;

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

Самоучитель


class

Rectangle

int

Shape

width;

public

x,

width height

public

int y,

int w,

int

h)

void

Draw(int

x,

int

y)

прямоугольника

class

y)

w; h;

override

x,

base(x,

в

У)

XPOS

X;

yPos

у;

ShapeVirtualApp

static

void

args)

Shape

pt

new 7);

Shape

rect

new R e c t a n g l e ( l ,

allShapes

4,

10,

20)

new

new new new new

3, 2,

currentShape in 0);

1, 3,

allShapes)

П р е ж д е всего нам н у ж н о о п р е д е л и т ь б а з о в ы й класс S h a p e , п р е д с т а в л я ю щ и й собой общие и

свойства

фигур.

Rectangle.

Глава 4. Полиморфизм

От

этого

класса

будут

наследоваться

классы

Point


В б а з о в о м классе S h a p e , п о м и м о полей x P o s и y P o s , нам н у ж н о о б ъ я в и т ь конст¬ р у к т о р и так н а з ы в а е м ы й class

Draw:

Shape

protected protected public

xPos; yPos;

Shape ( i n t x,

xPos

int

y)

y;

public

Тело

int int

virtual

void

виртуального

x,

метода

Draw,

int

объявленного

с

помощью

ключевого

слова

не с о д е р ж и т ни о д н о г о о п е р а т о р а . Во в р е м я в ы п о л н е н и я п р о г р а м м ы этот в и р т у а л ь н ы й м е т о д б у д е т п о д м е н я т ь с я на о д н о и м е н н ы й м е т о д из п р о и з в о д н ы х клас¬ сов P o i n t и R e c t a n g l e . Н и ж е м ы п р и в е л и о б ъ я в л е н и е п р о и з в о д н о г о класса P o i n t : class

Point

Shape

public

P o i n t ( i n t x,

public

override

i n t y)

base

void

x,

(x,

y)

int точки в

xPos

x,

у;

О б р а т и т е в н и м а н и е , что в этом к л а с с е , так же как и в б а з о в о м классе S h a p e , име¬ ется м е т о д D r a w , п е р е о п р е д е л я ю щ и й м е т о д базового класса. О д н а к о э т о т м е т о д о б ъ я в л е н в п р о и з в о д н о м классе с и с п о л ь з о в а н и е м к л ю ч е в о г о слова override. А н а л о г и ч н ы й м е т о д о б ъ я в л е н и в п р о и з в о д н о м классе R e c t a n g l e : public

override

void

Draw(int

x,

int

y)

прямоугольника xPos yPos

в

x,

X; у;

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

СМ. Самоучитель


Н а п о м н и м , что ранее в п р и в е д е н н ы х п р и м е р а х п р о г р а м м мы у ж е п е р е о п р е д е л я л и м е т о д ы базового класса, п о л ь з у я с ь для этого к л ю ч е в ы м словом n e w . В р е з у л ь т а т е к о м п и л я т о р скрывал о т п р о г р а м м ы п е р е о п р е д е л е н н ы й метод б а з о в о г о класса. П р и м е н е н и е к л ю ч е в о г о слова o v e r r i d e д а е т д р у г о й э ф ф е к т . В и р т у а л ь н ы й ме¬ тод базового класса п о д м е н я е т с я с о о т в е т с т в у ю щ и м м е т о д о м п р о и з в о д н о г о класса не во время к о м п и л я ц и и , а во время работы программы. Таким о б р а з о м , в с л е д у ю щ е м ф р а г м е н т е кода в и р т у а л ь н ы й м е т о д D r a w б а з о в о г о класса S h a p e п о д м е н я е т с я м е т о д о м D r a w п р о и з в о д н о г о класса P o i n t : Shape

pt

new 7

Point(10,

25);

Это п р о и с х о д и т п о т о м у , что мы создали о б ъ е к т класса P o i n t и з а п и с ы в а е м ссыл¬ ку на него в п е р е м е н н у ю , и м е ю щ у ю тип базового класса S h a p e . Во в р е м я своего вы¬ п о л н е н и я п р о г р а м м а д и н а м и ч е с к и о п р е д е л я е т тип с с ы л к и , х р а н я щ е й с я в п е р е м е н н о й p t , и в ы з ы в а е т для нее м е т о д D r a w и з с о о т в е т с т в у ю щ е г о п р о и з в о д н о г о к л а с с а ( в дан¬ ном случае и з класса P o i n t ) . А н а л о г и ч н о в с л е д у ю щ е м ф р а г м е н т е кода вместо в и р т у а л ь н о г о метода D r a w базо¬ вого класса будет вызван м е т о д D r a w п р о и з в о д н о г о класса R e c t a n g l e : Shape

rect

new

4,

20);

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

следующем фрагменте

объектов

п р о г р а м м ы мы

создали

массив для хранения четырех

базового класса S h a p e : allShapes

new

В с о з д а н н ы й таким о б р а з о м массив мы п о м е щ а е м д в е точки и два п р я м о у г о л ь н и к а : allShapes[l] allShapes[2]

new new R e c t a n g l e ( 2 , new new

3, 2,

1, 3,

5) 4);

В р е з у л ь т а т е наш массив б у д е т с о д е р ж а т ь о б ъ е к т ы д в у х р а з н ы х п р о и з в о д н ы х клас¬ сов, и м е ю щ и х о б щ и й базовый к л а с с S h a p e . Д о п у с т и м , нам н у ж н о н а р и с о в а т ь все ф и г у р ы , х р а н я щ и е с я в м а с с и в е . П р и исполь¬ зовании п о л и м о р ф и з м а это м о ж н о сделать п р о с т ы м в ы з о в о м в и р т у а л ь н о г о м е т о д а D r a w базового класса S h a p e : in

Глава 4.

allShapes)

171


Здесь для п р о с т о т ы все фигуры р и с у ю т с я в о д н о й т о ч к е с к о о р д и н а т а м и (0, 0), но вы м о ж е т е легко и з м е н и т ь цикл таким о б р а з о м , чтобы к а ж д а я фигура была н а р и с о в а н а на своем месте. О б р а щ а е м в а ш е в н и м а н и е н а с л е д у ю щ и й и н т е р е с н ы й факт. О б р а б а т ы в а я и с х о д н ы й текст п р о г р а м м ы , к о м п и л я т о р не м о ж е т з н а т ь , объекты ка¬ кого из п р о и з в о д н ы х классов и в какой п о с л е д о в а т е л ь н о с т и з а п и с а н ы в массиве. Эта и н ф о р м а ц и я с т а н о в и т с я д о с т у п н о й т о л ь к о в о время в ы п о л н е н и я п р о г р а м м ы . в и р т у а л ь н ы х функций как раз и п о з в о л я е т программе о п р е д е л и т ь тип о ч е р е д н о г о о б ъ е к т а , и з в л е к а е м о г о в цикле из массива. В з а в и с и м о с т и от т о г о , какой о б ъ е к т на о ч е р е д н о й итерации цикла п р о г р а м м а и з в л е к л а из м а с с и в а (точку или пря¬ м о у г о л ь н и к ) , она в ы з ы в а е т м е т о д D r a w и з с о о т в е т с т в у ю щ е г о п р о и з в о д н о г о класса ( P o i n t или R e c t a n g l e ) .

Раннее и позднее связывание В п р о г р а м м и р о в а н и и и с п о л ь з у ю т с я два т е р м и н а , п о д х о д я щ и е под о п и с а н и е случаев перегрузки м е т о д о в с и с п о л ь з о в а н и е м к л ю ч е в ы х слов n e w и o v e r r i d e . При переопределении метода базового класса о д н о и м е н н ы м методом производного класса, и м е ю щ и м такой же список параметров и объявленным с п о м о щ ь ю ключевого сло¬ ва new, п р о и с х о д и т так называемое раннее связывание binding). Оно называется ранним, так как выполняется еще на стадии компиляции исходного текста программы. К о г д а же з а д е й с т в о в а н м е х а н и з м в и р т у а л ь н ы х ф у н к ц и й и в х о д е р а б о т ы програм¬ мы в и р т у а л ь н а я ф у н к ц и я базового класса п е р е о п р е д е л я е т с я ф у н к ц и е й п р о и з в о д н о г о класса, о б ъ я в л е н н о й как o v e r r i d e , в ы п о л н я е т с я позднее связывание (late binding). И м е н н о п о з д н е е с в я з ы в а н и е и п о з в о л я е т нам в ы з ы в а т ь в и р т у а л ь н ы е м е т о д ы базо¬ вого класса для о б р а б о т к и о б ъ е к т о в р а з л и ч н ы х п р о и з в о д н ы х к л а с с о в . К р о м е т о г о , п р и м е н я я п о л и м о р ф и з м и в и р т у а л ь н ы е м е т о д ы , вы м о ж е т е п р и м е н я т ь п р о г р а м м н ы й к о д , р а с с ч и т а н н ы й на и с п о л ь з о в а н и е б а з о в ы х к л а с с о в , для о б р а б о т к и о б ъ е к т о в произ¬ водных классов.

Абстрактные классы Если все м е т о д ы класса я в л я ю т с я в и р т у а л ь н ы м и , т. с. о б ъ я в л е н ы с к л ю ч е в ы м словом v i r t u a l , т о т а к о й класс н а з ы в а е т с я абстрактным. К а ж д ы й метод абст��актного класса должен быть переопределен соответствующим ме¬ тодом производного класса. Вы не можете создать объект абстрактного класса, так как фактически такой класс содержит только шаблоны методов, но не сами методы. В л и с т и н г е 4.4 мы п р и в е л и н о в ы й в а р и а н т п р о г р а м м ы , п р и м е н я ю щ е й полимор¬ физм для о б р а б о т к и о б ъ е к т о в , х р а н я щ и х с я в м а с с и в е . Листинг

4.4.

Файл

using System; namespace ShapeAbstract abstract class Shape


p r o t e c t e d int xPos; protected int yPos; p u b l i c S h a p e ( i n t x,

y)

x; у

x P о s abstract

class

int

public

Point

void

x,

int y)

Shape

public

Point(int

public

override

x,

int

y)

base

void

x,

(x,

int точки

xPos xPos

class int int

y)

y) в

x,

у)

base(x,

y)

x; y;

Rectangle

Shape

width;

public

x,

width height public

int y,

int

int

h;

override

void

x,

int

y)

прямоугольника xPos yPos

-

у) x; y;

class static

void

args) allShapes new new new new

4. Полиморфизм

new P 3, 2,

1, 3,

5) 4)

в


in

allShapes)

Теперь м ы о б ъ я в и л и класс S h a p e а б с т р а к т н ы м , у к а з а в при его о б ъ я в л е н и и к л ю ч е ­ вое слово a b s t r a c t : abstract

class

Shape

protected int xPos; protected int yPos public S h a p e ( i n t x, xPos xPos

int

y)

x; y;

abstract

public

void

Draw(int

x,

int

y)

К р о м е т о г о , м ы о б ъ я в и л и а б с т р а к т н ы м и м е т о д D r a w н а ш е г о а б с т р а к т н о г о базово­ го

класса.

О б р а т и т е в н и м а н и е , что

объявление абстрактного

метода з а к а н ч и в а е т с я

т о ч к о й с запятой и не и м е е т тела, о г р а н и ч е н н о г о ф и г у р н ы м и с к о б к а м и . И с п о л ь з о в а н и е к л ю ч е в о г о слова a b s t r a c t при о б ъ я в л е н и и м е т о д а и с к л ю ч а е т не­ о б х о д и м о с т ь ( а т а к ж е в о з м о ж н о с т ь ) у к а з а н и я к л ю ч е в ы х слов o v e r r i d e и v i r t u a l . К р о м е т о г о , это к л ю ч е в о е с л о в о н е с о в м е с т и м о с к л ю ч е в ы м с л о в о м

Перегрузка операторов Рассмотренную

в

предыдущей

из с р е д с т в р е а л и з а ц и и С#.

Другое

такое

главе

полиморфизма,

средство

перегрузку

методов

можно

предусмотренных в языке

перегрузка

считать

одним

программирования

операторов.

Мы у ж е г о в о р и л и , что в ч и с л о в ы х и с т р о к о в ы х в ы р а ж е н и я х м о ж н о и с п о л ь з о в а т ь т а к и е о п е р а т о р ы , как о п е р а т о р с л о ж е н и я , в ы ч и т а н и я , у м н о ж е н и я , р а в е н с т в а и т. п. Я з ы к С # п о з в о л я е т вам п е р е о п р е д е л я т ь д е й с т в и е м н о г и х о п е р а т о р о в таким о б р а з о м , ч т о б ы их м о ж н о было и с п о л ь з о в а т ь при р а б о т е с л ю б ы м и т и п а м и д а н н ы х , в том числе с т и п а м и д а н н ы х , с о з д а в а е м ы х на о с н о в е р а з р а б о т а н н ы х вами к л а с с о в . Н и ж е п е р е ч и с л е н ы б и н а р н ы е о п е р а т о р ы , к о т о р ы е м о ж н о п е р е г р у ж а т ь в я з ы к е С#: -

*

I

А это с п и с о к п е р е г р у ж а е м ы х у н а р н ы х о п е р а т о р о в : true

false

Фролов, Г.

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


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

теория комплексных чисел Чтобы продемонстрировать использование перегруженных операторов, мы выбрали с п о с о б , п р е д л а г а е м ы й м н о г и м и у ч е б н и к а м и п о п р о г р а м м и р о в а н и ю . М ы с о з д а д и м соб­ с т в е н н ы й класс п р е д н а з н а ч е н н ы й для р а б о т ы с к о м п л е к с н ы м и ч и с л а м и . П о л н о е о п р е д е л е н и е к о м п л е к с н о г о ч и с л а м о ж н о найти в л ю б о м с п р а в о ч н и к е по м а т е м а т и к е , н а п р и м е р в [7], п о э т о м у мы о г р а н и ч и м с я т о л ь к о с а м ы м и н е о б х о д и м ы ¬ ми с в е д е н и я м и . Согласно [7], числом н а з ы в а е т с я число вида a + b i , где аи b п р е д ­ с т а в л я ю т собой д е й с т в и т е л ь н ы е числа. С и м в о л i я в л я е т с я о б о з н а ч е н и е м мнимой еди­ ницы, у д о в л е т в о р я ю щ е й у с л о в и ю i2 = - l . Ч и с л а а и b н а з ы в а ю т с я с о о т в е т с т в е н н о действительной и мнимой ч а с т ь ю числа. Для к о м п л е к с н ы х чисел о п р е д е л е н ы о п е р а ц и и с р а в н е н и я , с л о ж е н и я , в ы ч и т а н и я , у м н о ж е н и я и д е л е н и я . И м е н н о эти о п е р а ц и и нас будут и н т е р е с о в а т ь в п е р в у ю о ч е р е д ь . Д а д и м н е с к о л ь к о н е о б х о д и м ы х о п р е д е л е н и й о п е р а ц и й , в ы п о л н я е м ы х над ком¬ плексными числами.

Сравнение Два к о м п л е к с н ы х ч и с л а равны между с о б о й , когда равны их д е й с т в и т е л ь н ы е и м н и ­ м ы е части. Н а п р и м е р , если у нас есть два числа a 2 + b 2 i , т о они будут р а в ­ н ы , если в ы п о л н я е т с я с л е д у ю щ е е у с л о в и е :

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

Сложение Ч т о б ы с л о ж и т ь два к о м п л е к с н ы х числа, надо с л о ж и т ь по о т д е л ь н о с т и их д е й с т в и т е л ь ¬ ные и м н и м ы е части:

Вычитание В ы ч и т а н и е к о м п л е к с н ы х чисел в ы п о л н я е т с я п о с л е д у ю щ е м у правилу: -

Умножение Для у м н о ж е н и я к о м п л е к с н ы х чисел п р и м е н я е т с я с л е д у ю щ а я формула: *

Глава

Полиморфизм

-

175


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

Класс для представления комплексных чисел Для п р е д с т а в л е н и я к о м п л е к с н ы х

чисел

мы р а з р а б о т а л и

класс с

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

и

именем

Complex.

предназначенные

с о о т в е т с т в е н н о для х р а н е н и я д е й с т в и т е л ь н о й и м н и м о й части к о м п л е к с н о г о числа, к о н с т р у к т о р , а т а к ж е два м е т о д а , A d d и S u b , для с л о ж е н и я и в ы ч и т а н и я к о м п л е к с н ы х чисел: class public public

double double

public

Complex ( d o u b l e r,

re im

public

double

i)

i

static

return

public

re;

Complex

x2)

Complex

Complex

x2)

new

static

return

Complex

new

-

-

Конструктор копирует значения своих параметров в соответствующие поля класса. Первый параметр задает д е й с т в и т е л ь н у ю часть комплексного числа, а второй — мнимую. С т а т и ч е с к и е м е т о д ы A d d и S u b в о з в р а щ а ю т н о в ы й о б ъ е к т класса C o m p l e x , соз¬ д а н н ы й при п о м о щ и о п е р а т о р а n e w . К о н с т р у к т о р а м этих о б ъ е к т о в п е р е д а ю т с я пара¬ метры, вычисленные в соответствии с правилами сложения и вычитания комплексных чисел. П р и в е д е м п р и м е р р а б о т ы с этим к л а с с о м : Complex C o m p l e x c2

new new C o m p l e x (4,

"c2

c2 В.

im)

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


Complex

sum c2

Complex

sub

Sub

c2);

- c2 В этом ф р а г м е н т е кода м ы сначала создали два к о м п л е к с н ы х числа и 5 Д а л е е мы вывели эти числа на к о н с о л ь в виде 2 i и (4, 5 И на­ к о н е ц , мы по очереди вызвали м е т о д ы A d d и S u b , о т о б р а з и в на к о н с о л и р е з у л ь т а т в ы ­ полнения операций и в ы ч и т а н и я к о м п л е к с н ы х чисел. О д н а к о было бы н а м н о г о у д о б н е е и с п о л ь з о в а т ь д л я с л о ж е н и я и в ы ч и т а н и я к о м ­ чисел не м е т о д ы A d d и S u b , а п р и в ы ч н ы е о п е р а т о р ы и например: C o m p l e x sum Complex sub

cl cl

-

с2; c2;

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

Перегрузка бинарных операторов Для перегрузки о п е р а т о р о в нам н у ж н о д о б а в и т ь в наш класс с т а т и ч е с к и е м е т о д ы спе¬ ц и а л ь н о г о вида, п р и ч е м этот вид зависит от т о г о , будет п е р е г р у ж а т ь с я б и н а р н ы й или унарный оператор. Сначала мы рассмотрим перегрузку бинарных операторов. Н и ж е м ы привели и с х о д н ы й т е к с т м е т о д а класса C o m p l e x , п е р е г р у ж а ю щ е г о опе¬ ратор Сложения: public

static

return

Complex

operator

(Complex

Complex

new

Как в и д и т е , он очень п о х о ж на о п и с а н н ы й в ы ш е метод A d d , в ы п о л н я ю щ и й ту же с а м у ю о п е р а ц и ю . О т л и ч и е з а к л ю ч а е т с я в т о м , что в качестве имени м е т о д а в м е с т о A d d м ы и с п о л ь з о в а л и к о н с т р у к ц и ю вида o p e r a t o r А н а л о г и ч н ы м образом перегружаются операторы вычитания, умножения и деления: public

static

r e t u r n new

public

static

return xl

Глава

Complex

operator

Complex ( x l

Complex

xl,

-

operator

-

(Complex

Complex x2

xl,

im)

Complex

new re - xl

Полиморфизм

im*x2

x2

im)


public

static

return

Complex

operator

Complex

new -

Д е й с т в и я в ы п о л н я ю т с я э т и м и о п е р а т о р а м и в с о о т в е т с т в и и с п р а в и л а м и вычисле¬ ний над к о м п л е к с н ы м и

ч и с л а м и , о п и с а н н ы м и ранее в р а з д е л е « К р а т к а я теория ком¬

плексных чисел». Д о б а в и в в к л а с с C o m p l e x п е р е ч и с л е н н ы е в ы ш е м е т о д ы , п е р е о п р е д е л я ю щ и е ос¬ н о в н ы е а р и ф м е т и ч е с к и е о п е р а ц и и , мы м о ж е м з а п и с ы в а т ь в ы р а ж е н и я с к о м п л е к с н ы м и ч и с л а м и более е с т е с т в е н н ы м о б р а з о м : Complex Complex

cl c2

Complex

sural

new new

( 1 , 2) 5)

cl

c2; c2

Complex

cl

-

c2; - c2

Complex mul

c2 Complex Console

c2; c2

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

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

и

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

операторов. П р и это�� мы будем с ч и т а т ь , что для и н к р е м е н т а к о м п л е к с н о г о числа н у ж н о приба¬ вить е д и н и ц у к его д е й с т в и т е л ь н о й и м н и м о й части, а для д е к р е м е н т а — в ы ч е с т ь еди¬ ницу из д е й с т в и т е л ь н о й и м н и м о й ч а с т и . М е т о д ы для п е р е г р у з к и у н а р н ы х о п е р а ц и й п о л у ч а е т т о л ь к о один п а р а м е т р : public

static

r e t u r n new

Complex

operator

x)

1,

1);

В

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

Самоучитель


public

static

Complex

operator

r e t u r n new

-

1,

x) -

1);

В о т как м ы м о ж е м и с п о л ь з о в а т ь п е р е г р у ж е н н ы е т а к и м с п о с о б о м о п е р а ц и и инкре¬ мента и д е к р е м е н т а : C o m p l e x cl C o m p l e x c2 Complex

new C o m p l e x ( l , new

incr

+ + cl;

Complex Как в и д и т е , теперь мы м о ж е м в ы п о л н я т ь у н а р н ы е о п е р а ц и и с к о м п л е к с н ы м и чис¬ лами т а к и м же с п о с о б о м , каким они в ы п о л н я ю т с я и с о б ы ч н ы м и д е й с т в и т е л ь н ы м и числами.

Перегрузка операторов сравнения Для того ч т о б ы с р а в н и в а т ь о б ъ е к т ы , с о з д а н н ы е на базе р а з р а б о т а н н ы х вами к л а с с о в , н у ж н о п е р е г р у з и т ь все или н е к о т о р ы е о п е р а т о р ы с р а в н е н и я , т а к и е , как а т а к ж е м е т о д ы б а з о в о г о класса o b j e c t с и м е н а м и E q u a l s и GetHashCode. З а м е т и м , что если вы п е р е г р у ж а е т е о п е р а т о р р а в е н с т в а в м е с т е с ним н у ж ­ но обязательно перегружать и оператор неравенства перегрузке операторов и нужно дополнительно перегружать операторы и В о т п р и м е р п е р е г р у з к и о п е р а т о р о в р а в е н с т в а и н е р а в е н с т в а д л я н а ш е г о класса public

static

return else return public

static

return else return

public

operator

Complex

x2)

Complex

x2)

true; false; bool

operator

!=

xl,

true; false;

override

return

(Complex

bool

true;

Глава 4. Полиморфизм

o)


public

override

return

int

0;

Как в и д и т е , м е т о д ы o p e r a t o r и operator п о л у ч а ю т два параметра (ссылки н а с р а в н и в а е м ы е о б ъ е к т ы ) в о з в р а щ а ю т значение t r u e , если дей¬ с т в и т е л ь н ы е и м н и м ы е части с р а в н и в а е м ы х к о м п л е к с н ы х чисел с о о т в е т с т в е н н о сов¬ п а д а ю т или не с о в п а д а ю т . Здесь все п о н я т н о , так как в н е ш н е эти м е т о д ы очень похо¬ жи на т о л ь к о что р а с с м о т р е н н ы е м е т о д ы п е р е г р у ж е н н ы х б и н а р н ы х о п е р а т о р о в . Н о н е о б х о д и м о с т ь перегрузки м е т о д о в E q u a l s и G e t H a s h C o d e т р е б у е т допол¬ нительного пояснения. Мы уже у п о м и н а л и , что в я з ы к е п р о г р а м м и р о в а н и я С# все классы н а с л е д у ю т с я от о д н о г о б а з о в о г о класса O b j e c t . В этом классе о б ъ я в л е н о н е с к о л ь к о м е т о д о в , кото¬ рые иногда п р и х о д и т с я п е р е о п р е д е л я т ь в п р о и з в о д н ы х классах. В частности, метод E q u a l s позволяет сравнить два объекта, а метод G e t H a s h C o d e нужен для получения так называемого хеш-кода объекта. Х е ш - к о д однозначно идентифи¬ цирует каждый объект класса и применяется для быстрого поиска объектов по ключу. М е т о д ы E q u a l s и G e t H a s h C o d e в з а и м о с в я з а н ы в том с м ы с л е , что д в у м объек¬ т а м , к о т о р ы е м е т о д E q u a l s с ч и т а е т о д и н а к о в ы м и , м е т о д G e t H a s h C o d e д о л ж е н воз­ вращать о д и н а к о в ы е з н а ч е н и я Н а ш а р е а л и з а ц и я м е т о д а E q u a l s с ч и т а е т р а в н ы м и л ю б ы е п е р е д а в а е м ы е е й объек¬ т ы , в о з в р а щ а я всегда з н а ч е н и е t r u e . Что ж е касается м е т о д а G e t H a s h C o d e , т о о н всегда в о з в р а щ а е т о д и н а к о в о е з н а ч е н и е 0 . Для того ч т о б ы с р а в н и в а т ь к о м п л е к с н ы е числа класса C o m p l e x , такая р е а л и з а ц и я м е т о д о в E q u a l s и G e t H a s h C o d e вполне пригодна.

Пример программы В л и с т и н г е 4.5 мы п р и в е л и и с х о д н ы й текст п р о г р а м м ы , в которой для работы с ком¬ п л е к с н ы м и ч и с л а м и п р и м е н я ю т с я как о б ы ч н ы е м е т о д ы , о п р е д е л е н н ы е в классе C o m p l e x , так и п е р е г р у ж е н н ы е о п е р а т о р ы . Все п р и е м ы , п р и м е н е н н ы е нами в этой п р о г р а м м е , у ж е были о п и с а н ы ранее, по¬ этому мы о с т а в л я е м эту п р о г р а м м у вам для с а м о с т о я т е л ь н о г о и з у ч е н и я . Листинг

4.5.

Файл

using System; n a m e s p a c e ComplexNum class

Complex

public public

double double

public

Complex (double

re im

re; im; r,

double

i)

r i А В.

Г. В.

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


public

static

r e t u r n new

public

static

return

public

public

public

Complex ( x l . r e

xl.im

Complex

xl,

operator

Complex

operator

new

static

static

static

return

x2)

-

Complex

(Complex x l ,

Complex

xl,

Complex

Complex

x2)

Complex

x2)

-

operator

x)

1, Complex

operator

r e t u r n new public

Complex

-

r e t u r n new public

Complex

new

static

return

Add ( C o m p l e x

new

static

return

Complex

Complex

x)

1,

-

operator

1);

* (Complex x l ,

new -

xl.re*x2.im

public

static

return

x2

Complex

im)

operator

Complex

new -

public

static

return else return

Глава 4. Полиморфизм

bool

true; false;

operator

(Complex

Complex

x2)


public

static

bool

operator

(Complex

Complex

im return else return

public

true; false;

override

o)

return public

override

return

static

int

0;

void

args)

Complex Complex

cl c2

Complex

sum

new

c2); c2

-

Complex sub Complex

suml

c2

cl c2

Complex subl Console

cl

-

c2; -

c2

C o m p l e x mul c2 Complex div Console Complex Console

cl c2

incr

Complex Console A,

Г.

Фролов. Язык

Самоучитель


if(cl

c2)

else

Класс Ранее мы у ж е н е о д н о к р а т н о у п о м и н а л и класс от к о т о р о г о автома¬ т и ч е с к и н а с л е д у ю т с я все о б ъ е к т ы в п р о г р а м м а х С#. К а к вы у в и д и т е в д а л ь н е й ш е м , на¬ л и ч и е е д и н о г о к о р н я в д е р е в е н а с л е д о в а н и я классов С# дает н е м а л ы е п р е и м у щ е с т в а . Н а п р и м е р , о б ъ е к т ы л ю б о г о класса м о ж н о х р а н и т ь в г о т о в ы х к о н т е й н е р а х , предостав¬ л я е м ы х б и б л и о т е к о й классов Microsoft Framework. В п р е д ы д у щ е м р а з д е л е мы р а с с к а з ы в а л и в а м о м е т о д а х E q u a l s и п е р в ы й из к о т о р ы х и м е е т о т н о ш е н и е к с р а в н е н и ю о б ъ е к т о в , а второй п р е д н а з н а ч е н для п о л у ч е н и я н а з ы в а е м о г о х е ш - к о д а , о д н о з н а ч н о и д е н т и ф и ц и р у ю щ е г о объект. П о м и м о этих м е т о д о в , для р а з р а б о т ч и к о в п р о г р а м м С # м о г у т п р е д с т а в л я т ь и н т е р е с ToString, и М е т о д T o S t r i n g позволяет получить имя объекта. Переопределяя это и м я в произ¬ водных классах, можно использовать перегруженную версию этого метода для предостав¬ ления расширенной информации об объекте, не ограничиваясь одним только именем. Что касается м е т о д а G e t T y p e , то с его п о м о щ ь ю м о ж н о п о л у ч и т ь д о п о л н и т е л ь ¬ н у ю и н ф о р м а ц и ю о типе объекта. Э т о т м е т о д мы будем у п о м и н а т ь в р а з д е л е нашей книги, посвященной атрибутам. И н а к о н е ц , п е р е о п р е д е л е н и е метода F i n a l i z e н у ж н о для о с в о б о ж д е н и я какихл и б о р е с у р с о в ( ф а й л о в , с о е д и н е н и й с базами д а н н ы х и т. п.) перед т е м , как о б ъ е к т бу¬ дет у н и ч т о ж е н с и с т е м о й сборки мусора. В л и с т и н г е 4.6 мы п р и в е л и п р и м е р п р о г р а м м ы , д е м о н с т р и р у ю щ е й и с п о л ь з о в а н и е метода T o S t r i n g . Листинг

4.6.

Файл

using System; namespace SysObj class

Point

public public

double double

public

P o i n t (double x,

xPos

Глава

x;

Полиморфизм

xPos; yPos; double

y)


public

void

x,

double

y)

точки yPos

x,

Point

class public

PointSmart (double x,

public

override

double

y)

base

(x,

y)

string

return

class

в

X,у;

at

xPos,

yPos)

SysObjApp

static

void

int

args) 38; toString:

mylnt,

ToString

toString:

Point

pt

new

Point(l,

2); toString:

pt.xPos, PointSmart

pt.yPos, pts

new

PointSmart toString:

П е р в о е , что

наша п р о г р а м м а ,

это с о з д а с т п е р е м е н н у ю m y l n t типа

int

и вызывает для нес м е т о д

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

Язык

Самоучитель


38; toString: М ы у ж е г о в о р и л и в а м , что такие типы д а н н ы х , как i n t , н а самом деле я в л я ю т с я классами С # , поэтому в записи о String нет н и ч е г о н е о б ы ч н о г о . Что же будет п о к а з а н о на консоли в

р а б о т ы этих строк п р о г р а м м ы ?

Там появится текстовая строка 3 8 , которая п р е д с т а в л я е т объект m y l n t : mylnt

38,

toString:

38

А н а л о г и ч н о н а ш а п р о г р а м м а в ы з ы в а е т метод T o S t r i n g для п е р е м е н н о й типа

bool:

bool

myBool

true; toString:

myBool,

ToString

На к о н с о л и будет о т о б р а ж е н а с л е д у ю щ а я строка: myBool

True,

toString:

True

Как в и д и т е , т е к с т о в о е п р е д с т а в л е н и е о б ъ е к т о в п р о с т е й ш и х т и п о в д а н н ы х просто з н а ч е н и е , х р а н я щ е е с я в этих о б ъ е к т а х . А как в ы г л я д и т т е к с т о в о е п р е д с т а в л е н и е класса Point?

это

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

pt

new

(1, toString:

pt.xPos,

pt.yPos,

ToString

З а п у с т и в п р о г р а м м у н а в ы п о л н е н и е , м о ж н о у б е д и т ь с я , что для класса P o i n t ме¬ тод T o S t r i n g в ы в о д и т не к о о р д и н а т ы т о ч к и (как это м о ж н о было бы п о д у м а т ь ) , а на¬ звание класса и п р о с т р а н с т в а и м е н , в к о т о р о м этот класс о п р е д е л е н : pt

(1,

toString:

В классе P o i n t S m a r t м ы п е р е о п р е д е л и л и метод T o S t r i n g т а к и м о б р а з о м , что¬ бы он о т о б р а ж а л на к о н с о л и и м е н н о к о о р д и н а т ы т о ч к и , а не н а з в а н и е класса: public

override

string

return

at

yPos);

Наш в а р и а н т метода T o S t r i n g в о з в р а щ а е т строку с к о о р д и н а т а м и т о ч к и , сфор¬ м и р о в а н н у ю при

п о м о щ и класса

среды в ы п о л н е н и я Microsoft

F o r m a t , входящего в библиотеку классов Framework.

Вот как м ы в ы з ы в а е м п е р е о п р е д е л е н н ы й м е т о д T o S t r i n g в н а ш е й п р о г р а м м е : PointSmart

pts

new

PointSmart toString:

Глава 4. Полиморфизм


Как и с л е д о в а л о о ж и д а т ь , новый в а р и а н т метода T o S t r i n g в ы в о д и т н а экран то, что нам н у ж н о , а именно к о о р д и н а т ы т о ч к и : pts

(3,

4),

toString:

Point

at

(3,

4)

П е р е о п р е д е л я я в с о з д а в а е м ы х к л а с с а х метод T o S t r i n g , вы м о ж е т е наделить его ф у н к ц и я м и , п о л е з н ы м и , н а п р и м е р , для о т л а д к и или для о т о б р а ж е н и я т е к у щ е г о со¬ стояния для п о л у ч е н и я л ю б о й и н ф о р м а ц и и о б о б ъ е к т е , к о т о р у ю м о ж н о п р е д с т а в и т ь в виде текста.

А.

Фролов. Г.

Фролов. Язык

Самоучитель


Глава 5. Преобразование типов объектов К а к мы у ж е г о в о р и л и , п р о г р а м м ы С# р а б о т а ю т с л о к а л ь н ы м и п е р е м е н н ы м и и п о л я м и классов р а з л и ч н ы х т и п о в . Это м о г у т быть с т а н д а р т н ы е т и п ы С # ( ч и с л о в ы е , с т р о к о в ы е , л о г и ч е с к и е и т. д.), а т а к ж е т и п ы д а н н ы х , о п р е д е л е н н ы х при п о м о щ и классов (напри¬ м е р , р а с с м о т р е н н ы е в п р е д ы д у щ е й главе к о м п л е к с н ы е ч и с л а ) . П р и с о с т а в л е н и и в ы р а ж е н и й , в к л ю ч а ю щ и х в себя п е р е м е н н ы е и поля р а з л и ч н ы х типов,

выполняется

м о ж е т быть явной,

операция

преобразования

типов

(приведения

типов). Эта

операция

когда п р о г р а м м и с т сам у к а з ы в а е т , к к а к о м у типу н у ж н о п р и в е с т и

д а н н ы й тип, а т а к ж е неявной.

В п о с л е д н е м случае к о м п и л я т о р в ы п о л н я е т преобразо¬

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

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

Листинг

Файл

using System; namespace class static

void

int intNuber double double r e s u l t

args) 5 2.5; doubleNumber

intNuber; сложения 5 2.5


мы объявили две

переменные

и

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

пер­ для х р а н е н и я чи¬

сел с п л а в а ю щ е й т о ч к о й . Н а ш а п р о г р а м м а п р е д п р и н и м а е т п о п ы т к у с л о ж е н и я чисел р а з н о г о типа с з а п и с ь ю результата в п е р е м е н н у ю т и п а d o u b l e : double

result

intNuber;

В п р о ц е с с е к о м п и л я ц и и п р о г р а м м ы в ы п о л н я е т с я а в т о м а т и ч е с к о е н е я в н о е преобра¬ з о в а н и е типа п е р е м е н н о й i n t N u b e r в тип d o u b l e , в р е з у л ь т а т е чего с л о ж е н и е вы¬ полняется п р а в и л ь н о . В о т что н а ш а п р о г р а м м а в ы в о д и т н а к о н с о л ь : Результат

сложения

5

2.5

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

исходные преобразуемые данные

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

Числа без знака С х е м ы в о з м о ж н ы х н е я в н ы х п р е о б р а з о в а н и й чисел без знака и чисел со з н а к о м отли­ чаются

друг

от д р у г а .

Сначала

мы

рассмотрим

преобразования

чисел

без

знака.

На рис. 5.1 мы показали схему д о п у с т и м ы х п р е о б р а з о в а н и й для таких чисел.

float

J

j

Неявные

decimal

преобразования

J чисел

без знака

Как в и д и т е , з н а ч е н и я типа b y t e могут быть п р и в е д е н ы к типу uint,

а также к

short, ushort,

d e c i m a l . Что ж е касается а в т о м а т и ч е с к о г о приве¬

д е н и я т и п а u i n t к типу b y t е или u s h o r t , т о оно н е в о з м о ж н о .

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


Числа со знаком н е я в н ы е п р е о б р а з о в а н и я для чисел со знаком п о к а з а н ы на рис. 5.2.

Рис.

5.2.

Неявные

преобразования

чисел

со знаком

Как видите, транслятор не может автоматически преобразовать числа со знаком в ч и с л а без з н а к а . Т а к и м о б р а з о м , с л е д у ю щ и й ф р а г м е н т кода т р а н с л и р о в а т ь с я не будет: int i u i n t ui uint

5; 6 ui

i;

Текстовые символы char Как

мы

уже говорили,

текстовые

символы

c h a r языка С#

хранятся

в

кодировке

U N I C O D E , п р и ч е м для к а ж д о г о с и м в о л а т р е б у е т с я 2 байта п а м я т и . П о э т о м у тип c h a r м о ж е т быть

автоматически преобразован в типы u s h o r t

в д р у г и е т и п ы в с о о т в е т с т в и и с рис. 5.1 и 5.2.

Глава 5. Преобразование

объектов

и

i n t (рис.

5.3), а т а к ж е


Числа с плавающей точкой П р о щ е всего с х е м а п р е о б р а з о в а н и й в ы г л я д и т для типа

для

п р е д с т а в л е н и я чисел с п л а в а ю щ е й т о ч к о й (рис. 5.4).

Рис. Тип д а н н ы х

5.4.

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

типа

float

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

Явное преобразование числовых типов В некоторых случаях необходимо использовать явное преобразование типов. Если, н а п р и м е р , у вас есть и с х о д н а я п е р е м е н н а я т и п а u l o n g , но вы т о ч н о з н а е т е , что в ней х р а н я т с я з н а ч е н и я , н е п р е в о с х о д я щ и е 2 5 5 , т о м о ж н о в ы п о л н и т ь с л е д у ю щ е е преобра¬ зование: ulongNuber resultByte

11;

О б р а т и т е в н и м а н и е , что п е р е д и м е н е м п е р е м е н н о й u l o n g N u b e r м ы у к а з а л и в к р у г л ы х с к о б к а х т и п , к к о т о р о м у н у ж н о п р и в е с т и и с х о д н ы й тип д а н н ы х . Это так на¬ зываемый оператор приведения типа. О п е р а т о р п р и в е д е н и я типа з а с т а в л я е т к о м п и л я т о р в ы п о л н и т ь з а д а н н о е преобразо¬ вание т и п о в , д а ж е если при э т о м и с х о д н о е з н а ч е н и е числа о к а ж е т с я и с к а ж е н н ы м . Р а с с м о т р и м п р и м е р п р о г р а м м ы ( л и с т и н г 5.2), д е м о н с т р и р у ю щ е й т а к о е и с к а ж е н и е . Листинг 5.2.

Файл

ch05\Cast\CastApp.cs

using System; namespace Cast class

CastApp

static

void

args)

ulongNuber byte r e s u l t B y t e ushort resultUshort uint r e s u l t U i n t число

190

типа

В Фролов, Г.

ulong

Фролов

Самоучитель


типа

byte\t=

типа типа

Эта п р о г р а м м а п о с л е д о в а т е л ь н о п р и с в а и в а е т з н а ч е н и е 0 x 1 1 2 2 3 3 4 4 5 5 , х р а н я щ е е ­ ся

в

исходной

переменной

типа

ulong,

переменным

типа

byte,

u s h o r t и u i n t . Д а л е е п р о г р а м м а о т о б р а ж а е т и с х о д н о е з н а ч е н и е и р е з у л ь т а т выпол¬ нения о п е р а ц и й п р и с в а и в а н и я в ш е с т н а д ц а т е р и ч н о м виде: Исходное

число

типа

ulong

Р е з у л ь т а т типа b y t e Р е з у л ь т а т типа u s h o r t Р е з у л ь т а т типа u i n t

1122334455

55 4455 22334455

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

Проверка преобразования числовых типов Для того ч т о б ы з а с т р а х о в а т ь с я от о ш и б о к , в о з н и к а ю щ и х в п р о ц е с с е п р и в е д е н и я т и п о в , в языке С# предусмотрена специальная конструкция c h e c k e d . В о т п р и м е р ее и с п о л ь з о в а н и я : ulong ulongNuber ushort checked resultUshort

В блок c h e c k e d п о м е щ а ю т с я о п е р а т о р ы с п р и в е д е н и е м типа, при в ы п о л н е н и и ко¬ торых м о ж е т п р о и с х о д и т ь потеря с т а р ш и х р а з р я д о в и с х о д н о г о з н а ч е н и я . П р о в е р к а в ы п о л н я е т с я во время р а б о т ы п р о г р а м м ы , так как на этапе к о м п и л я ц и и з н а ч е н и е и с х о д н о й п е р е м е н н о й м о ж е т быть н е и з в е с т н о . Глава

Преобразование типов объектов

191


В п р и в е д е н н о м выше ф р а г м е н т е кода при в ы п о л н е н и и п р и с в а и в а н и я потеря с т а р ш и х р а з р я д о в и с х о д н о г о з н а ч е н и я . Так как эта н а х о д и т с я в блоке c h e c k e d , в результате возникнет исключение Об и с к л ю ч е н и я х мы р а с с к а ж е м п о з ж е , в гл. 9. П о к а вам д о с т а т о ч н о знать, что про­ г р а м м а м о ж е т « п е р е х в а т ы в а т ь » и с к л ю ч е н и я и о б р а б а т ы в а т ь о ш и б о ч н ы е ситуации Ес¬ ли же о б р а б о т к а и с к л ю ч е н и я не п р е д у с м о т р е н а , то п р о г р а м м а з а в е р ш и т с в о ю работу а в а р и й н о и в ы в е д е т с о о т в е т с т в у ю щ е е с о о б щ е н и е . В нашем случае это будет сообще¬ ние, п р и в е д е н н о е н и ж е : exception Additional

information:

of

type

occurred

Arithmetic

operation

resulted

in

an

in

overflow.

Преобразования типов и классы Мы уже г о в о р и л и , что в я з ы к е С# все д а н н ы е (даже числа и с и м в о л ы ) п р е д с т а в л я ю т с я как объекты соответствующих классов, унаследованных от корневого класса У п р а в л е н и е т и п а м и в Microsoft Framework универ­ система типов Common Type System ( C T S ) , о п р е д е л я ю щ а я типы и вила их п р и в е д е н и я . Н а л и ч и е C T S о б е с п е ч и в а е т и с п о л ь з о в а н и е единой системы типов в р а м к а х л ю б ы х я з ы к о в п р о г р а м м и р о в а н и я , р а з р а б о т а н н ы х для п л а т ф о р м ы Microsoft Framework. Это, в частности, позволяет создавать отдельные компоненты и объекты создаваемой программы на разных языках программирования.

Псевдонимы типов данных В я з ы к е С# все ч и с л о в ы е т и п ы д а н н ы х п р е д с т а в л я ю т собой к л а с с ы , о б ъ я в л е н н ы е в п р о с т р а н с т в е имен S y s t e m . Для п р о с т о т ы к о м п и л я т о р С # д о п у с к а е т и с п о л ь з о в а н и е в м е с т о имен этих у ж е з н а к о м ы х вам п с е в д о н и м о в . В табл. 5.1 мы привели п с е в д о н и м ы ч и с л о в ы х т и п о в , у п о м и н а в ш и х с я в гл. 1. Таблица

Псевдонимы

для

имен

классов

Псевдоним byte

Имя System

ushort uint

��исловых

типов

данных

класса

Byte Uintl6

System

ulong

Uint32 Uint64 Sbyte

sby te short

System

Intl6

int

System

Int32

long

System

Int64 Single Double

double decimal

System

Decimal

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

Самоучитель


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

5.2.

Псевдонимы

для

имен

классов

прочих типов

Псевдоним

Имя

bool

Boolean

char

Char

string

System

object

Sys

данных

класса

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

5.3.

Файл

using System; namespace class static

void

UInt32

args)

ulongNuber resultByte resultUshort resultUint ч

типа

ulong

ulongNuber)

resultUint)

типа

byte\t=

типа

ushort\t=

типа

uint\t=

Обратите внимание, что здесь мы заменили псевдонимы именами соответствую¬ щих классов. В следующих двух строках программы мы не указали пространство имен S y s t e m , так как его просмотр задан в начале программы оператором u s i n g : Глава 5. Преобразование типов объектов 7

Язык

Самоучитель


UInt32 К о г д а н а з в а н и я классов и с п о л ь з о в а т ь у д о б н е е , чем п с е в д о н и м ы ? Н а п р и м е р , когда вам н у ж н о следить за р а з р я д н о с т ь ю п е р е м е н н ы х . Н а п р и м е р , с м о ж е т е ли вы сразу в с п о м н и т ь , какова р а з р я д н о с т ь п е р е м е н н о й типа (т. е. с к о л ь к о р а з р я д о в д а н н ы х з а н и м а ю т з н а ч е н и я этого т и п а ) ? Н а з в а н и е класса S y s t e m . I n t l 6 сразу д а е т ответ на этот д а н н ы е зани­ м а ю т в п а м я т и 16 р а з р я д о в . Т а к и м о б р а з о м , п р и м е н е н и е н а з в а н и й к л а с с о в , п е р е ч и с ­ л е н н ы х в табл. 5.1 и 5.2, вместо с о о т в е т с т в у ю щ и х п с е в д о н и м о в п о з в о л я е т в н е к о т о р ы х случаях у п р о с т и т ь для п р о г р а м м и с т а к о н т р о л ь и с п о л ь з о в а н и я т и п о в д а н н ы х . Для п о в ы ш е н и я п р о и з в о д и т е л ь н о с т и при р а б о т е с о б ъ е к т а м и , п р е д с т а в л я ю щ и м и числа и с и м в о л ы , в языке С# п р и м е н я е т с я т е х н о л о г и я упаковки ( b o x i n g ) . Эта т е х н о л о ­ гия п р е о б р а з о в а н и е о б ъ е к т о в к л а с с о в в о б ы ч н ы е пере­ м е н н ы е , р а з м е щ а е м ы е в о п е р а т и в н о й п а м я т и к о м п ь ю т е р а , когда это д о п у с т и м о . Об¬ р а т н о е п р е о б р а з о в а н и е т а к ж е в ы п о л н я е т с я а в т о м а т и ч е с к и . Д л я п р о г р а м м и с т а все эти д е й с т в и я а б с о л ю т н о п р о з р а ч н ы , п о э т о м у у п а к о в к а н е в ы з ы в а е т д о п о л н и т е л ь н ы х с л о ж н о с т е й при с о з д а н и и п р о г р а м м .

Приведение производных и базовых классов Р а с с к а з ы в а я о п о л и м о р ф и з м е в п р е д ы д у щ е й г л а в е , в о д н о м из п р и м е р о в п р о г р а м м (см. л и с т и н г 4.4) м ы с о з д а в а л и б а з о в ы й класс S h a p e

с абстрактным методом Draw,

а затем п о л у ч а л и о т него п р о и з в о д н ы е классы P o i n t и R e c t a n g l e : abstract

class

abstract

class

public

Point

public

Shape

void

x,

int

x,

int

Shape

override

void

точки в xPos xPos

class

Rectangle

public

x,

у)

x; y;

Shape

override

Console xPos yPos

void

Draw(int

x,

int

y)

прямоугольника в

x,

x;

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

Самоучитель


В о о р у ж и в ш и с ь этими классами, в методе M a i n мы проделали следующие операции: allShapes allShapesCO]

new

new P o i n t ( l , 1, 3,

allShapes[3]

new

4); in

Обратите

внимание,

что

здесь

allShapes) мы

объявили

массив,

созданный

из

объектов

класса S h a p e , н о записали в него ссылки н а о б ъ е к т ы п р о и з в о д н ы х к л а с с о в P o i n t и Rectangle. А н а л о г и ч н ы е д е й с т в и я в ы п о л н я л и с ь и в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й был п р и в е д е н в л и с т и н г е 4.3: Shape

pt

Shape

rect

new

new

Point

25);

Rectangle(l,

4,

10,

Здесь в л о к а л ь н ы е п е р е м е н н ы е pt и r e c t , с о з д а н н ы е на базе класса S h a p e , т о ж е з а п и с ы в а ю т с я ссылки н а о б ъ е к т ы п р о и з в о д н ы х классов P o i n t и R e c t a n g l e . Т а к и м о б р а з о м , в процессе п р и с в а и в а н и я в ы п о л н я е т с я н е я в н о е п р е о б р а з о в а н и е т и п а ссыл¬ ки — тип с с ы л о к на п р о и з в о д н ы е классы п р и в о д и т с я к типу ссылки на б а з о в ы й к л а с с . Такое п р и в е д е н и е т и п о в н а з ы в а е т с я восходящим (upcast). И с п о л ь з у я п о л и м о р ф и з м , мы м о ж е м вызвать м е т о д ы п р о и з в о д н ы х к л а с с о в при по¬ мощи в и р т у а л ь н ы х или а б с т р а к т н ы х м е т о д о в базового класса. Что же касается нисходящего (downcast) п р и в е д е н и я с с ы л о к на б а з о в ы й к л а с с к ти­ пу ссылки на п р о и з в о д н ы й к л а с с , то оно н е д о п у с т и м о . Если мы в этом случае приме¬ ним явное п р и в е д е н и е т и п о в , к о м п и л я т о р не в ы в е д е т с о о б щ е н и е об о ш и б к е , но в про¬ цессе р а б о т ы п р о г р а м м ы м о ж е т в о з н и к н у т ь и с к л ю ч е н и е .

Операторы is и as Для того ч т о б ы в п р о ц е с с е п р е о б р а з о в а н и я т и п о в не в о з н и к л о и с к л ю ч е н и е , п е р е д вы¬ п о л н е н и е м этой о п е р а ц и и следует п р о в е р и т ь ее д о п у с т и м о с т ь . Для п р о в е р к и возмож¬ ности в ы п о л н е н и я п р е о б р а з о в а н и я т и п о в во в р е м я р а б о т ы п р о г р а м м ы в я з ы к е С# пре¬ д у с м о т р е н ы о п е р а т о р ы is и a s . О п е р а т о р a s в ы п о л н я е т я в н о е п р е о б р а з о в а н и е т и п о в . Если такое п р е о б р а з о в а н и е з а к о н ч и л о с ь н е у д а ч н о , в м е с т о ссылки на о б ъ е к т о п е р а т о р as в о з в р а щ а е т пустое зна¬ чение n u l l . Что ж е касается о п е р а т о р а i s , т о о н в о з в р а щ а е т t r u e или от т о г о , м о ж н о ли в ы п о л н и т ь п р е о б р а з о в а н и е или нет. Глава 5 Преобразование типов объектов

f a l s e в зависимости

195


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

в листинге 5 . 4 . 5.4.

using System; namespace class

Point

public public

int int

x;

public x У

class

xO,

yO)

x,

i n t y,

xO

ColorPoint

public

int

Point

color;

public

class

int

int

c)

base(x,

y)

AsIsApp

static

void

Point pt ColorPoint

args) new cpt

Point(l, new

Point xPoint cpt; ColorPoint xColorPoint

2);

pt

2,

as

3);

ColorPoint; xPoint.x,

xPoint.y);

null)

else

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

СМ. Самоучитель


new new P o i n t ( 3 , new

1

pointArray

(object i f (obj

else

is

if(obj

obj

in

pointArray)

ColorPoint)

is

В этой п р о г р а м м е м ы о п р е д е л и л и базовый класс P o i n t , п р е д с т а в л я ю щ и й точки на п л о с к о с т и , и п р о и з в о д н ы й от него класс ц в е т н ы х т о ч е к C o l o r P o i n t . По сравне¬ н и ю с б а з о в ы м классом п р о и з в о д н ы й класс C o l o r P o i n t д о п о л н е н полем c o l o r для х р а н е н и я з н а ч е н и я цвета. Все самое и н т е р е с н о е в этой п р о г р а м м е д е л а е т м е т о д M a i n . П р е ж д е всего он создает два о б ъ е к т а pt и c p t классов P o i n t и

C o l o r P o i n t со¬

ответственно: P o i n t pt ColorPoint

new cpt

new

2,

м ы з а п и с ы в а е м ссылку н а объект c p t в п е р е м е н н у ю x P o i n t , и м е ю щ у ю тип базового класса P o i n t : Point

xPoint

cpt;

Так как о п е р а ц и я п р и в е д е н и я ссылки к типу б а з о в о г о класса в ы п о л н я е т с я автома¬ т и ч е с к и , эта строка будет всегда в ы п о л н е н а без о ш и б о к . Далее н а ш а п р о г р а м м а смо¬ жет о т о б р а з и т ь к о о р д и н а т ы точки на к о н с о л и : xPoint.x, x P o i n t . y ) ;

Глава 5. Преобразование типов объектов


Но мы выполнить прямо противоположную операцию приведения типов з а п и с а т ь в п е р е м е н н у ю x C o l o r P o i n t с т и п о м д о ч е р н е г о класса ссылку на о б ъ е к т pt б а з о в о г о класса: ColorPoint Если появится что т а к о е записано

xColorPoint

pt

as

ColorPoint;

в н и м а н и е , что здесь м ы п р и м е н и л и о п е р а т о р a s . п р е о б р а з о в а н и е будет в ы п о л н е н о у с п е ш н о , в п е р е м е н н о й x C o l o r P o i n t н у ж н а я нам ссылка. Если же в п р о ц е с с е р а б о т ы п р о г р а м м ы в ы я с н и т с я , п р е о б р а з о в а н и е в ы п о л н и т ь н е в о з м о ж н о , в п е р е м е н н у ю x C o l o r P o i n t будет значение n u l l .

П е р е д тем как п ы т а т ь с я и с п о л ь з о в а т ь с о д е р ж и м о е п е р е м е н н о й x C o l o r P o i n t , его н е о б х о д и м о п р о в е р и т ь . Если в этой п е р е м е н н о й н а х о д и т с я п у с т о е з н а ч е н и е n u l l , на¬ ша п р о г р а м м а не будет о т о б р а ж а т ь к о о р д и н а т ы и цвет т о ч к и на к о н с о л и , а выведет туда с о о т в е т с т в у ю щ е е с о о б щ е н и е : if (xColorPoint

null)

Запуская н а ш у п р о г р а м м у для п р о в е р к и , вы у в и д и т е , что о п и с а н н о е в ы ш е преобра¬ з о в а н и е в ы п о л н и т ь так и не у д а л о с ь . В э т о м , о д н а к о , нет ничего у д и в и т е л ь н о г о . В с а м о м д е л е , мы создали о б ы ч н у ю , не¬ ц в е т н у ю точку pt н п ы т а е м с я з а п и с а т ь ссылку на нее в п е р е м е н н у ю с типом C o l o r P o i n t , п р е д п о л а г а ю щ и м д о п о л н и т е л ь н о х р а н е н и е и н ф о р м а ц и и о цвете. Если бы нам у д а л о с ь создать « ц в е т н у ю » ссылку на « ч е р н о - б е л ы й » о б ъ е к т , то по¬ п ы т к а п р и м е н е н и я этой с с ы л к и , н а п р и м е р , для п о л у ч е н и я и н ф о р м а ц и и о цвете т о ч к и , з а к о н ч и л а с ь бы н е у д а ч е й . Д е л о в т о м , что в и с х о д н о м о б ъ е к т е класса P o i n t нет ин¬ формации о цвете! Далее наша программа демонстрирует использование оператора i s . Мы создаем м а с с и в базового класса P o i n t , с о с т о я щ и й из д в у х я ч е е к , а затем запи¬ сываем в него с с ы л к и на о б ъ е к т ы базового и п р о и з в о д н о г о к л а с с о в : Point

pointArray

new

new new мы о б р а б а т ы в а е м этот массив в цикле: foreach if(obj

(object is

obj

in

pointArray)

ColorPoint)

A

Фролов, Г. R

Самоучитель


else

(obj

is

Point)

Здесь мы ПО очереди и з в л е к а е м все э л е м е н т ы м а с с и в а и з а п и с ы в а е м ссылку на них в п е р е м е н н у ю o b j класса

Класс

б а з о в ы м д л я всех к л а с с о в

в п р о г р а м м а х С # , поэтому такая о п е р а ц и я всегда з а к о н ч и т с я у с п е х о м . И з в л е к а я о ч е р е д н о й э л е м е н т массива, п р о г р а м м а не знает, какой тип и м е е т этот элемент. У нас в о з м о ж н ы д в а случая

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

класс P o i n t или п р о и з в о д н ы й класс C o l o r P o i n t . С п о м о щ ь ю о п е р а т о р а is мы м о ж е м о п р е д е л и т ь тип с с ы л к и и в ы п о л н и т ь соответ­ с т в у ю щ е е д е й с т в и е . Е с л и это с с ы л к а на о р д и н а т ы и цвет, а если « ч е р н о - б е л а я »

т о ч к у , мы в ы в о д и м на экран ее ко­ о г р а н и ч и в а е м с я только к о о р д и н а т а м и .

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

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

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

[7]. При этом д е й с т в и ­

т е л ь н а я ч а с т ь к о м п л е к с н о г о ч и с л а с т а в и т с я в с о о т в е т с т в и е к о о р д и н а т о е п о оси X , а мнимая

по оси Y.

Р а н е е м ы у ж е создавали для п р е д с т а в л е н и я к о м п л е к с н ы х чисел класс C o m p l e x . В этом классе мы о п р е д е л и л и все н е о б х о д и м ы е п е р е г р у ж е н н ы е о п е р а т о р ы . К р о м е то¬ го, мы с о з д а в а л и и н е о д н о к р а т н о и с п о л ь з о в а л и в п р и м е р а х п р о г р а м м класс P o i n t , с о б о й точки на п л о с к о с т и . Возникает вопрос, можно ли приводить друг к другу ссылки на объекты этих классов? Если да, то мы с м о ж е м в ы п о л н я т ь п р и с в а и в а н и е с с ы л о к на о б ъ е к т ы этих к л а с с о в , как это с д е л а н о н и ж е : Point pt Complex с

new new

Complex r e s u l t l Point r e s u l t 2 Глава

Point(l,

2);

pt;

Преобразование типов объектов


З д е с ь мы два объекта классов C o m p l e x и P o i n t , а т а к ж е две локаль¬ ные п е р е м е н н ы е этих же классов. Затем п р о г р а м м а з а п и с ы в а е т в п е р е м е н н у ю класса C o m p l e x ссылку на о б ъ е к т класса P o i n t , а т а к ж е в п е р е м е н н у ю класса P o i n t ссылку н а о б ъ е к т класса C o m p l e x . На п е р в ы й в з г л я д т а к о е п р и с в а и в а н и е д о п у с т и м о , так как с у щ е с т в у е т о д н о з н а ч н о е с о о т в е т с т в и е м е ж д у д е й с т в и т е л ь н о й и м н и м о й частями к о м п л е к с н о г о числа и коор¬ д и н а т а м и (X, Y) т о ч к и . Однако компилятор не о существовании подобного соответствия. Он не обладает никакой информацией о том, как нужно преобразовывать объекты класса C o m p l e x в объ¬ екты класса P o i n t , а также о том, как выполнить обратное преобразование. Чтобы п р и в е д е н н ы й в ы ш е ф р а г м е н т кода т р а н с л и р о в а л с я без о ш и б о к , мы д о л ж н ы определить в классах C o m p l e x и P o i n t соответствующие методы, не¬ с т а н д а р т н о е п р е о б р а з о в а н и е между о б ъ е к т а м и этих классов. Н и ж е м ы п р и в е л и и с х о д н ы й текст м е т о д а , в ы п о л н я ю щ е г о п р и в е д е н и е типа ссылки н а о б ъ е к т класса P o i n t к типу с с ы л к и н а о б ъ е к т класса C o m p l e x : public

static

return

implicit

operator

Complex (Point

pt)

new

О б ъ я в л е н и е этого метода д о л ж н о н а х о д и т ь с я в классе C o m p l e x . Н а ш м е т о д с о з д а е т н о в ы й о б ъ е к т класса C o m p l e x , и н и ц и а л и з и р у я его соответст¬ в у ю щ и м о б р а з о м : к о о р д и н а т а по оси X з а п и с ы в а е т с я в р е а л ь н у ю часть к о м п л е к с н о г о числа, а к о о р д и н а т а по оси Y — в м н и м у ю часть к о м п л е к с н о г о числа. О б р а т и т е в н и м а н и е , что здесь м ы и с п о л ь з о в а л и к л ю ч е в ы е слова i m p l i c i t и operator. С ключевым словом

o p e r a t o r вы у ж е з н а к о м ы — оно и с п о л ь з у е т с я для пере¬

грузки о п е р а т о р о в (см. гл. 4). Что же касается i m p l i c i t , то это к л ю ч е в о е слово ука¬ зывает на допустимость неявного преобразования типов. Вместо

i m p l i c i t при о б ъ я в л е н и и н е с т а н д а р т н о г о п р е о б р а з о в а н и я м о ж н о указы¬

вать к л ю ч е в о е с л о в о e x p l i c i t . В этом с л у ч а е п р и д е т с я задавать тип п р е о б р а з о в а н и я Point pt Complex с

new new

Point(l, (3,

Complex r e s u l t l P o i n t result2 Хотя о п е р а т о р ы явного п р е о б р а з о в а н и я н е с к о л ь к о з а г р о м о ж д а ю т и с х о д н ы й текст, их п р и м е н е н и е п о з в о л и т с о к р а т и т ь к о л и ч е с т в о о ш и б о к при н а п и с а н и и п р о г р а м м с не¬ с т а н д а р т н ы м п р е о б р а з о в а н и е м т и п о в . Если н е с т а н д а р т н о е п р е о б р а з о в а н и е о б ъ я в л е н о как программист н е сможет п о ошибке выполнить неправильное приведе­ ние т и п о в — к о м п и л я т о р не даст ему этого сделать. Чтобы можно было приводить ссылку на объект класса C o m p l e x к ссылке типа P o i n t , в классе P o i n t необходимо объявить метод приведения типа следующего вида: 200

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


public

static

return

implicit

operator

new

P o i n t (Complex

c)

c.im);

Этот создает новый объект класса в качестве к о о р д и н а т ы по оси X и с п о л ь з у е т с я д е й с т в и т е л ь н а я часть к о м п л е к с н о г о числа, а в качестве коор¬ д и н а т ы по оси Y м н и м а я часть к о м п л е к с н о г о числа. В л и с т и н г е 5.5 мы привели и с х о д н ы й текст п р о г р а м м ы , д е м о н с т р и р у ю щ е й приме¬ нение о п и с а н н ы х в ы ш е н е с т а н д а р т н ы х п р е о б р а з о в а н и й . Листинг using

5.5.

Файл

System;

namespace class

Complex

public public

double double

public

Complex ( d o u b l e

re im

public

static

Complex

Complex

Sub(Complex -

Complex

operator

Complex

static

return Глава 5.

(Complex

operator

new

static

new

Complex

xl.im

xl,

Complex

-

operator

r e t u r n new public

x2)

Complex xl.im

new

static

return public

i)

Complex

new

static

return

public

double

new

static

return public

r,

r i

return public

re; im;

1, Complex

operator re -

типов объектов

1,

x) x.im

1); x)

Complex

x2)


public

static

return

Complex

operator

* (Complex xl,

Complex

operator

xl,

Complex

new -

public

static

return

Complex

x2)

new -

public

static

return else return public

static

operator

xl,

Complex

xl,

Complex

true; false; bool

operator

! = x2.im)

return else return public

false;

override

return public

true;

bool

o)

true;

override

int

return

public

static

return

public

public

operator

implicit

operator

Complex ( P o i n t

pt)

new

static

return

implicit

new

static

Complex(d,

implicit

Complex

d)

d o u b l e (Complex

c)

0);

operator

return

202

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


class

Point

public public

double double

public

P o i n t (double x,

xPos

public

xPos;

double

y)

x;

void

x,

double

y)

точки xPos yPos public

в

x,

у)

x; y; static

return

public

new

static

return public

new

static

return

implicit

operator

Point

P o i n t (Complex

c)

im)

implicit

operator

Point(d,

0);

implicit

operator

Point(double

double(Point

d)

pt)

pt.xPos;

class static

void

Point pt Complex с

args) new new

Complex Point r e s u l t 2

Point

Глава

pt

result3

Преобразование типов объектов

203


double

result4

с; result4);

О б р а т и т е в н и м а н и е , что в классе

мы дополнительно определили преоб­

р а з о в а н и е д е й с т в и т е л ь н о г о ч и с л а типа d o u b l e в к о м п л е к с н о е ч и с л о , а

преобра¬

зование к о м п л е к с н о г о числа в д е й с т в и т е л ь н о е ч и с л о : public

static

return

public

new

static

implicit

operator

Complex(d,

implicit

Complex (double

d)

d o u b l e (Complex

c)

0)

operator

return

При в ы п о л н е н и и п р е о б р а з о в а н и я к о м п л е к с н о г о числа в д е й с т в и т е л ь н о е наш метод и г н о р и р у е т с у щ е с т в о в а н и е м н и м о й части числа. П р и о б р а т н о м п р е о б р а з о в а н и и мни¬ мая часть ч и с л а с т а н о в и т с я равной н у л ю . А н а л о г и ч н ы е м е т о д ы о п р е д е л е н ы и в классе P o i n t : public

static

implicit

r e t u r n new P o i n t ( d ,

public

static

return

Заметим,

implicit

operator

Point(double

d)

0)

operator

double(Point

pt)

pt.xPos;

что

с помощью нестандартных преобразований

вы

можете выполнить

п р и в е д е н и е л ю б ы х т и п о в о б ъ е к т о в , если, к о н е ч н о , это и м е е т с м ы с л и вы знаете алго¬ ритм т а к о г о п р и в е д е н и я . Хотя

классы

в С # н а с л е д у ю т с я о т о б щ е г о класса

все они

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

204

А В

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


Глава 6. Свойства объектов До сих пор мы р а с с м а т р и в а л и о б ъ е к т ы классов С#

как о б ъ е д и н е н и е п о л е й д а н н ы х

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

объяснения

назначения

свойств

мы

вернемся

к

описанию

класса

v i s i o n S e t , в п е р в ы е у п о м я н у т о г о нами в разделе « П е р в ы е шаги к О О П » гл. 3. На¬ п о м н и м , что этот класс п р е д с т а в л я л собой п р о г р а м м н у ю модель т е л е в и з о р а , к о т о р ы й м о ж н о было в к л ю ч а т ь и в ы к л ю ч а т ь , п е р е к л ю ч а т ь с канала на канал. К р о м е т о г о , мож¬ но было р е г у л и р о в а т ь г р о м к о с т ь звука. Н и ж е для у д о б с т в а мы в о с п р о и з в е л и в с о к р а щ е н н о м виде и с х о д н ы й текст класса

class

TelevisionSet

bool byte byte byte

Определить public bool

включен или выключен максимальный н о м е р к а н а л а текущий н о м е р к а н а л а текущая г р о м к о с т ь з в у к а

с��стояние

телевизора

205

включен

или

выключен


П е р е к л ю ч и т ь с я на прием з а д а н н о г о к а н а л а public bool SetChannel (byte channel)

Получить public byte

номер

текущего

канала

Установить громкость public void SetVolume ( b y t e volume)

Получить public byte

текущий

уровень

громкости

М ы м о ж е м п р е д с т а в и т ь себе т е л е в и з о р как о б ъ е к т , и м е ю щ и й с л е д у ю щ и е а т р и б у т ы : •

с о с т о я н и е т е л е в и з о р а ( в к л ю ч е н или в ы к л ю ч е н ) ,

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

т е к у щ и й н о м е р канала,

т е к у щ и й у р о в е н ь г р о м к о с т и звука.

З н а ч е н и е п е р е ч и с л е н н ы х в ы ш е а т р и б у т о в х р а н и т с я в п о л я х класса v i s i o n S e t , а д л я и з м е н е н и я этих з н а ч е н и й и для п о л у ч е н и я т е к у щ и х значений мы предусмотрели соответствующие методы. Например, текущий уровень громкости можно установить методом S e t V o l u m e , а определить методом G e t V o l u m e . Что­ б ы п е р е к л ю ч и т ь т е л е в и з о р н а н у ж н ы й канал, с л е д у е т вызвать м е т о д S e t C h a n n e l , для т о г о , ч т о б ы у з н а т ь н о м е р канала, п р и н и м а е м о г о т е л е в и з о р о м в н а с т о я щ и й мо¬ мент, — м е т о д G e t C h a n n e l . З а м е т и м , что в ы п о л н и т ь все п е р е ч и с л е н н ы е в ы ш е д е й с т в и я м о ж н о было бы и п о д р у г о м у , н а п р и м е р о б р а т и в ш и с ь н а п р я м у ю к полям класса, о б ъ я в л е н н ы м как p u b l i c . О д н а к о , д е й с т в у я п о д о б н ы м о б р а з о м , легко д о п у с т и т ь о ш и б к у . В с а м о м д е л е , ничто н е п о м е ш а е т п р о г р а м м е з а п и с а т ь в поле c u r r e n t C h a n n e l , х р а н я щ е е н о м е р текуще¬ го канала, с л и ш к о м б о л ь ш о е п р е в ы ш а ю щ е е м а к с и м а л ь н о д о п у с т и м о е . В ре¬ зультате п р о г р а м м а п р е д п р и м е т п о п ы т к у п е р е к л ю ч и т ь т е л е в и з о р н а н е с у щ е с т в у ю щ и й канал.

206

А В

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

Самоучитель


С д р у г о й с т о р о н ы , п р я м о е о б р а щ е н и е к полям у п р о щ а е т и с х о д н ы й текст п р о г р а м ­ мы и д е л а е т его н а г л я д н е е , так как для и з м е н е н и я а т р и б у т о в о б ъ е к т а в м е с т о в ы з о в а методов можно воспользоваться оператором присваивания. М е х а н и з м с в о й с т в , р е а л и з о в а н н ы й в я з ы к е п р о г р а м м и р о в а н и я С# и о п и с а н н ы й в этой главе нашей к н и г и , п о з в о л я е т с к о м б и н и р о в а т ь д о с т о и н с т в а и с п о л ь з о в а н и я ме¬ т о д о в для д о с т у п а к полям класса и п р я м о г о о б р а щ е н и я к этим п о л я м . Он п о з в о л я е т создать так н а з ы в а е м ы е поля (smart fields), д о с т у п к к о т о р ы м к о н т р о л и р у е т с я с п о м о щ ь ю п р о ц е д у р с п е ц и а л ь н о г о вида.

Объявление свойства К а ж д ы й класс С# м о ж е т с о д е р ж а т ь в себе о б ъ я в л е н и я о д н о г о или н е с к о л ь к и х с в о й с т в . С в о й с т в а п р е д с т а в л я ю т собой м е т о д ы с п е ц и а л ь н о г о вида, п р и ч е м для к а ж д о г о свойст¬ ва п р и м е н я ю т с я два т а к и х метода. Один м е т о д и с п о л ь з у е т с я для ч т е н и я з н а ч е н и я свойства, а д р у г о й — для з а п и с и . Для того чтобы объявить свойство, нам потребуется конструкция следующего вида: <Тип>

<Имя

get return

<Возвращаемое

значение>;

set <Поле

value;

Здесь в к а ч е с т в е м о д и ф и к а т о р а м о ж н о и с п о л ь з о в а т ь уже з н а к о м ы е вам к л ю ч е в ы е слова p u b l i c , p r o t e c t e d , p r i v a t e , i n t e r n a l , s t a t i c и n e w . Вот п р и м е р о б ъ я в л е н и я с в о й с т в а C h a n n e l в классе T e l e v i s i o n S e t : class

TelevisionSet

private public

byte byte

Channel

set if ( v a l u e currentchannel

value

0)

value;

get return

Глава 6. Свойства объектов

207


мы

указали м о д и ф и к а т о р д о с т у п а свойства p u b l i c , р а з р е ш а ю щ и й д о с т у п

к свойству C h a n n e l л ю б ы м Блок с в о й с т в а C h a n n e l

методам

программы.

Тип

byte, а

с о д е р ж и т две процедуры доступа (accessor).

О д н а из этих

п р о ц е д у р , о б ъ я в л е н н а я с к л ю ч е в ы м словом g e t , п р е д н а з н а ч е н а для п о л у ч е н и я значе¬ ния с в о й с т в а , а д р у г а я , о б ъ я в л е н н а я с к л ю ч е в ы м словом s e t , — для у с т а н о в к и значе¬ ния свойства.

Процедура доступа set С п о м о щ ь ю процедуры доступа s e t программа может установить значение свойства, используя обычный оператор присваивания. Рассмотрим следующий фрагмент программы: TelevisionSet TelevisionSet tvSmall tvLarge

tvSmall; tvLarge;

new new 5;

Здесь м ы с о з д а л и д в а о б ъ е к т а класса T e l e v i s i o n S e t , с о д е р ж а щ е г о о б ъ я в л е н и е свойства

Channel

(номер

текущего

канала).

Далее

один из этих т е л е в и з о р о в на 5-й к а н а л , а д р у г о й

наша

программа переключила

на 52-й к а н а л , в ы п о л н и в установку

свойства C h a n n e l в соответствующих объектах. Как в и д и т е , с с ы л к а на с в о й с т в а о б ъ е к т а в ы п о л н я е т с я т о ч н о т а к и м же о б р а з о м , как и на поле о б ъ е к т а . О д н а к о д е й с т в и я , в ы п о л н я е м ы е в п р о ц е с с е п р и с в а и в а н и я значения с в о й с т в у , п о л н о с т ь ю о т л и ч а ю т с я о т д е й с т в и й , в ы п о л н я е м ы х при и з м е н е н и и значения поля. К о г д а п р о г р а м м а и з м е н я е т с в о й с т в о о б ъ е к т а , п р и с в а и в а я ему з н а ч е н и е , выпол¬ няется с о о т в е т с т в у ю щ а я п р о ц е д у р а д о с т у п а .

Она,

например,

м о ж е т п р о в е р и т ь при¬

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

п р и м е р е п р о ц е д у р а д о с т у п а s e t с в о й с т в а C h a n n e l о б ъ я в л е н а в классе

TelevisionSet

следующим

образом:

set if ( v a l u e maxChannel value currentChannel value;

0)

О б р а т и т е в н и м а н и е н а и с п о л ь з о в а н и е к л ю ч е в о г о слова v a l u e .

Это слово обозна­

чает н е я в н ы й п а р а м е т р , п е р е д а в а е м ы й п р о ц е д у р е д о с т у п а s e t . Этот п а р а м е т р содер¬ жит з н а ч е н и е , к о т о р о е п р о г р а м м а п ы т а е т с я п р и с в о и т ь свойству.

208

А. В

Г В.

Язык

Самоучитель


Наша п р о ц е д у р ы д о с т у п а s e t свойства C h a n n e l п р о в е р я е т допусти¬ мость в ы п о л н е н и я о п е р а ц и и п р и с в а и в а н и я , сравнивая з н а ч е н и е п а р а м е т р а v a l u e с максимально допустимым значением, а также с нулем. Изменение значения свойства п р о и с х о д и т только в том случае, если п р о г р а м м а задает д о п у с т и м ы й н о м е р канала. В п р о т и в н о м случае т е к у щ и й номер канала не и з м е н я е т с я . Д р у г о й способ о б р а б о т к и о ш и б о к , п р е д п о л а г а ю щ и й п р и м е н е н и е и с к л ю ч е н и й , будет р а с с м о т р е н п о з ж е в г л а в е , посвященной исключениям.

Процедура доступа get Процедура доступа g e t

п р е д н а з н а ч е н а для

получения текущего

значения свойства.

Она о б я з а т е л ь н о д о л ж н а в о з в р а щ а т ь это з н а ч е н и е с п о м о щ ь ю о п е р а т о р а r e t u r n . Вот п р и м е р о п р е д е л е н и я п р о ц е д у р ы д о с т у п а g e t с в о й с т в а го в классе

Channel,

объявленно­

TelevisionSet:

return З а м е т и м , что з н а ч е н и е с в о й с т в а м о ж е т х р а н и т ь с я в поле (как в п р и м е р е , приведен¬ ном в ы ш е ) или в ы ч и с л я т ь с я д и н а м и ч е с к и во время р а б о т ы п р о г р а м м ы ( н а п р и м е р , на основе с о д е р ж и м о г о базы д а н н ы х или как-то еще). Ч т е н и е свойства в ы п о л н я е т с я а н а л о г и ч н о ч т е н и ю о б ы ч н о г о поля класса: канал "Включен"

из громкость "Выключен",

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

Свойства только для чтения и только для записи Е с л и при о б ъ я в л е н и и с в о й с т в а о п р е д е л и т ь д в е п р о ц е д у р ы д о с т у п а s e t и g e t , то т а к о е с в о й с т в о б у д е т д о с т у п н о и д л я з а п и с и и для ч т е н и я . Но и н о г д а б ы в а е т по¬ л е з н о о б ъ я в и т ь т а к о е с в о й с т в о , к о т о р о е м о ж н о т о л ь к о ч и т а т ь или в к о т о р о е м о ж н о только писать. В я з ы к е С# очень л е г к о о б ъ я в и т ь с в о й с т в а , д о с т у п н ы е т о л ь к о для чтения или толь¬ к о для записи. Е с л и , н а п р и м е р , вам н у ж н о с в о й с т в о , к о т о р о е м о ж н о т о л ь к о ч и т а т ь , следует о п р е д е л и т ь м е т о д д о с т у п а g e t и н е о п р е д е л я т ь м е т о д д о с т у п а s e t . Объявле¬ ние свойства, д о с т у п н о г о т о л ь к о для з а п и с и , д о л ж н о , н а о б о р о т , с о д е р ж а т ь определе¬ ние о д н о г о только м е т о д а д о с т у п а s e t . Глава 6. Свойства объектов

209


В о т п р и м е р о б ъ я в л е н и я с в о й с т в а M a x C h a n n e l , д о с т у п н о г о т о л ь к о д л я чтения: class

TelevisionSet

private public

byte byte

MaxChannel

get return

Это с в о й с т в о х р а н и т м а к с и м а л ь н ы й н о м е р канала, к о т о р ы й м о ж е т п р и н и м а т ь теле¬ в и з о р . И н и ц и а л и з а ц и я поля m a x C h a n n e l , х р а н я щ е г о этот н о м е р , в ы п о л н я е т с я один раз к о н с т р у к т о р о м при с о з д а н и и объекта. П о с л е того как о б ъ е к т с о з д а н , п р о г р а м м а н е с м о ж е т и з м е н и т ь м а к с и м а л ь н ы й номер п р и н и м а е м о г о к а н а л а , так как поле m a x C h a n n e l о б ъ я в л е н о с м о д и ф и к а т о р о м досту¬ па p r i v a t e . П р и м е н е н и е с в о й с т в , д о с т у п н ы х только д л я ч т е н и я или т о л ь к о д л я з а п и с и , позво¬ ляет количество ошибок, допускаемых программистом в ис¬ п о л ь з о в а н и я о п е р а т о р о в п р и с в а и в а н и я и д р у г и х с р е д с т в д о с т у п а к д а н н ы м объекта.

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

Файл

System; namespace TvProperties class

TelevisionSet

Конструктор

класса

TelevisionSet

public Устанавливаем исходное false; maxChannel

состояние

телевизора выключен количество

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

Самоучитель


currentchannel Volume

Свойство private public

PowerOn

bool bool

1; 10;

при включении п о к а з ы в а т ь канал г р о м к о с т ь при включении - 10%

включен

или

выключен

isPowerOn; PowerOn

get return

isPowerOn;

set isPowerOn

Свойство private public

value;

MaxChannel

byte

максимальный

номер

maxChannel

byte

MaxChannel

get

Свойство private public

Channel

текущий

номер

канала

byte byte

Channel

get return set if ( v a l u e

Глава 6. Свойства объектов

maxChannel currentchannel

value 0) value;

канала

1


Свойство private public

Volume

текущий

уровень

громкости

byte byte

Volume

get return set 0 value currentVolume

100) value;

currentVolume

class

0;

TvPropertiesApp

static

void

args)

TelevisionSet TelevisionSet tvSmall tvLarge

tvSmall;

new T e l e v i s i o n S e t ( 6 ) new

50; true; 27;

Console

tvSmall:

PowerOn

канал "Включен"

из громкость "Выключен",

tvLarge: канал "Включен"

А В.

из

Г. В.

громкость

С# Самоучитель


80; 39;

tvSmall tvLarge

false;

Console

tvSmall:

Console PowerOn

канал "Включен"

из громкость "Выключен",

tvLarge:

PowerOn

канал "Включен"

из громкость "Выключен",

В п р о г р а м м е о б ъ я в л е н класс T e l e v i s i o n S e t , п р е д с т а в л я ю щ и й в и р т у а л ь н ы й те¬ л е в и з о р . Все у п р а в л е н и е этим в и р т у а л ь н ы м т е л е в и з о р о м р е а л и з о в а н о ч е р е з свойства. К о н с т р у к т о р класса T e l e v i s i o n S e t в ы п о л н я е т н а ч а л ь н у ю лей класса, у с т а н а в л и в а я тем самым и с х о д н о е с о с т о я н и е телевизора: public isPowerOn maxChannel

false;

currentVolume Все

поля,

выключен количество каналов при включении п о к а з ы в а т ь к а н а л г р о м к о с т ь при включении - 10%

1; 10;

инициализируемые

конструктором,

объявлены

в

классе

1

Televi-

как p r i v a t e , п о э т о м у объекты п р о г р а м м ы , в н е ш н и е п о о т н о ш е н и ю к дан¬ ному классу, не и м е ю т к этим п о л я м н и к а к о г о д о с т у п а . Чтобы п р о г р а м м а могла в к л ю ч а т ь и в ы к л ю ч а т ь в и р т у а л ь н ы й т е л е в и з о р , в классе TelevisionSet private public

bool bool

определено свойство isPowerOn;

PowerOn

get Глава 6. Свойства объектов

PowerOn:


return

isPowerOn;

set isPowerOn

value;

Ч т о б ы в к л ю ч и т ь т е л е в и з о р , в это с в о й с т в о н у ж н о записать л о г и ч е с к о е значение t r u e , а чтобы выключить — логическое значение f a l s e . Так как в м е т о д е P o w e r O n о п р е д е л е н ы две п р о ц е д у р ы д о с т у п а , s e t и g e t , про¬ г р а м м а м о ж е т не т о л ь к о в к л ю ч а т ь или в ы к л ю ч а т ь т е л е в и з о р , но и определять его те¬ к у щ е е с о с т о я н и е ( в к л ю ч е н или в ы к л ю ч е н ) . В ы ш е в этой главе мы уже р а с с к а з ы в а л и про с в о й с т в о M a x C h a n n e l , х р а н я щ е е м а к с и м а л ь н о д о п у с т и м ы й н о м е р канала: private public

byte byte

maxChannel; MaxChannel

get return

maxChannel;

Так как в этом с в о й с т в е о б ъ я в л е н а т о л ь к о одна п р о ц е д у р а д о с т у п а g e t , п р о г р а м м а с м о ж е т ч и т а т ь д а н н о е с в о й с т в о , но не з а п и с ы в а т ь в него н о в ы е з н а ч е н и я . С в о й с т в о C h a n n e l д о с т у п н о как для ч т е н и я , так и для записи: private public

byte byte

Channel

get return set if ( v a l u e maxChannel value currentChannel value;

0)

При ч т е н и и д а н н о г о с в о й с т в а п р о г р а м м а п о л у ч и т т е к у щ и й н о м е р канала, храня¬ щ и й с я в поле c u r r e n t C h a n n e l . Что же касается п о п ы т к и у с т а н о в и т ь н о в ы й н о м е р канала, то она будет т о л ь к о в том с л у ч а е , если п р о г р а м м а з а п и с ы в а е т в с в о й с т в о C h a n n e l д о п у с т и м о е

А. В

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

Самоучитель


з н а ч е н и е ( б о л ь ш е е нуля и м е н ь ш е е м а к с и м а л ь н о д о п у с т и м о г о н о м е р а к а н а л а ) . Попыт¬ ки п е р е к л ю ч и т ь т е л е в и з о р на н е д о п у с т и м ы й канал б у д у т п р о и г н о р и р о в а н ы . Аналогичным образом работает и свойство V o l u m e , хранящее текущую громкость звука: private public

byte

Volume

get return set 0 currentVolume else currentVolume

value

100)

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

м ы р а с с м о т р и м м е т о д M a i n , п о л у ч а ю щ и й у п р а в л е н и е при з а п у с к е про¬

граммы. С в о ю работу этот м е т о д н а ч и н а е т с т о г о , что создает два т е л е в и з о р а , один из кото¬ рых с п о с о б е н п р и н и м а т ь 6 к а н а л о в , а д р у г о й TelevisionSet TelevisionSet tvSmall tvLarge

new new

40 к а н а л о в :

tvLarge T e l e v i s i o n S e t (6)

Д а л е е п р о г р а м м а в к л ю ч а е т п е р в ы й т е л е в и з о р , п е р е к л ю ч а е т его на 5-й канал и у с ­ т а н а в л и в а е т у р о в е н ь г р о м к о с т и , р а в н ы й 50 % от м а к с и м а л ь н о г о у р о в н я :

tvSmall

50,-

О б р а т и т е в н и м а н и е , что все эти д е й с т в и я в ы п о л н я ю т с я при п о м о щ и п р о с т о г о опе¬ ратора присваивания.

Программа записывает нужные значения

в

свойства объекта,

а м е т о д ы д о с т у п а s e t д е л а ю т все н е о б х о д и м ы е п р о в е р к и и д р у г и е д е й с т в и я . Д а л е е наша п р о г р а м м а в к л ю ч а е т второй т е л е в и з о р , п е р е к л ю ч а е т его на 27-й канал и у с т а н а в л и в а е т у р о в е н ь г р о м к о с т и 30

6. Свойства объектов

215


true;

Т е к у щ е е с о с т о я н и е п е р в о г о т е л е в и з о р а о т о б р а ж а е т с я на консоли:

PowerOn

канал "Включен"

из "Выключен",

громкость

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

PowerOn

канал (1) и з громкость "Включен" "Выключен",

На в т о р о м этапе своей работы п р о г р а м м а и з м е н я е т с о с т о я н и е т е л е в и з о р о в , пере¬ к л ю ч а я их на д р у г и е к а н а л ы : 3,80; Channel

39,60;

п р о г р а м м а в ы к л ю ч а е т оба т е л е в и з о р а : false; И н а к о н е ц , н о в о е с о с т о я н и е т е л е в и з о р о в о т о б р а ж а е т с я на к о н с о л и : tvSmall:

PowerOn

канал "Включен"

PowerOn

канал "Включен"

из громкость "Выключен",

(1)

из громкость "Выключен",

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

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

Самоучитель


tvSmall: tvLarge: tvSmall: tvLarge:

Телевизор Телевизор Телевизор

Включен, к а н а л 5 из 6, г р о м к о с т ь 50 Включен, канал 2 7 и з 4 0 , г р о м к о с т ь 3 0 Выключен, к а н а л 3 из б, г р о м к о с т ь 80 Выключен, к а н а л 3 9 и з 4 0 , г р о м к о с т ь 6 0

Как видите, механизм свойств позволил заменить все методы класса и свести выполне¬ ние всех действий над виртуальными телевизорами к простым операциям присваивания. Стоит ли вам в своих п р о г р а м м а х методы свойствами? О ч е в и д н о , что нет. П р и м е н е н и е свойств о п р а в д а н о в тех с л у ч а я х , когда н е о б х о д и м о к о н т р о л и р о в а т ь з н а ч е н и е к а к о г о - л и б о а т р и б у т а о б ъ е к т а , т а к о г о , как н о м е р канала т е л е в и з о р а или уро¬ вень г р о м к о с т и . С в о й с т в о связывается т о л ь к о с о д н и м з н а ч е н и е м . М е т о д ы же более у н и в е р с а л ь н ы . Вы м о ж е т е , н а п р и м е р , п е р е д а в а т ь м е т о д а м не¬ с к о л ь к о п а р а м е т р о в по ссылке или по з н а ч е н и ю , что н е в о з м о ж н о при и с п о л ь з о в а н и и с в о й с т в . Как и л ю б о е с р е д с т в о языка п р о г р а м м и р о в а н и я С#, свойства д о л ж н ы приме¬ няться т а м , где они д е й с т в и т е л ь н о н е о б х о д и м ы и п о м о г а ю т у п р о с т и т ь п р о г р а м м у , а т а к ж е у л у ч ш и т ь ее структуру.

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

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

чтобы

объявить

виртуальное

свойство,

используйте

ключевое

слово

как это п о к а з а н о н и ж е : public

virtual

int

X

set xPos

value;

return

xPos;

get

О б ъ я в л е н и е в и р т у а л ь н о г о свойства м о ж е т с о д е р ж а т ь в себе одну или две процеду¬ ры д о с т у п а . О б ъ я в л е н и е а б с т р а к т н о г о метода д о с т у п а в ы п о л н я е т с я с п р и м е н е н и е м к л ю ч е в о г о

слова

abstract:

public

abstract

int

Y

Глава 6. Свойства объектов

217


О б р а щ а е м ваше в н и м а н и е н а т о , что п р о ц е д у р ы д о с т у п а в и р т у а л ь н о г о с в о й с т в а д о л ж н ы быть о п р е д е л е н ы в п р о и з в о д н о м классе. Р а с с м о т р и м и с х о д н ы й текст п р о г р а м м ы , д е м о н с т р и р у ю щ е й н а с л е д о в а н и е свойств ( л и с т и н г 6.2). Листинг

6.2.

Файл

using System; namespace abstract

class

Shape

protected int xPos; protected int yPos; p u b l i c Shape ( i n t x, xPos xPos

int

y)

X; y;

public

abstract

public

virtual

void int

x,

int

y)

X

set xPos

value;

return

xPos;

get

public

abstract

int

Y

set;

class

Point

Shape

public

P o i n t ( i n t x,

public

override

void

i n t y)

base

(x,

x, точки

t h i X this.Y

218

y)

в

x y;

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

С#. Самоучитель


public

override

int

X

set xPos

value;

return

xPos;

get

public

override

int

Y

set yPos

value;

return

class

Rectangle

Shape

int width; int public width height public

x,

int y,

int w,

int h)

void

x,

int

Р и с о в а н и е прямоугольника

public

y)

w; h;

override

this.Y

base(x,

в

X; y;

override

int

Y

set yPos

value;

return

yPos;

get

6. Свойства объектов

219


class static

void

args)

Shape

pt

new

Shape

rect

25);

new

4,

allShapes new new new new

allShapes[3]

new

10,

20);

[4] (1, 3, 2,

Point (31,

currentShape

1, 3,

4);

4);

in

allShapes)

В этой п р о г р а м м е о б ъ я в л е н а б с т р а к т н ы й класс S h a p e и п р о и з в о д н ы е от него клас­ сы P o i n t и R e c t a n g l e . В а б с т р а к т н о м классе мы о б ъ я в и л и два п о л я , x P o s ф и г у р ы — о б ъ е к т а класса, с о з д а н н о г о на базе класса и н и ц и а л и з и р у ю щ и й эти поля при с о з д а н и и объекта. К р о м е т о г о , в этом классе о б ъ я в л е н а б с т р а к т н ы й для р и с о в а н и я ф и г у р ы в з а д а н н о й т о ч к е к о о р д и н а т н о й ва, X и Y, х р а н я щ и е т е к у щ и е к о о р д и н а т ы ф и г у р ы . Н и ж е м ы п р и в е л и и с х о д н ы й т е к с т класса P o i n t : class

Point

P o i n t ( i n t x,

public

override

void

int y)

Draw(int

base

x,

(x,

int точки

public

метод Draw, предназначенный п л о с к о с т и , а т а к ж е два свойст¬

Shape

public

this.X this.Y

и y P o s , хранящие координаты S h a p e , а также конструктор,

y)

y) в

x; y;

override

int

X

set xPos 220

А В.

Г В

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


get return

public

xPos;

override

int

Y

set yPos get return

yPos;

К о н с т р у к т о р класса P o i n t в ы з ы в а е т к о н с т р у к т о р базового класса. Метод Draw, объявленный с ключевым словом o v e r r i d e , выводит сообщение о р и с о в а н и и точки в з а д а н н ы х к о о р д и н а т а х , а затем с о х р а н я е т эти к о о р д и н а т ы в свойствах о б ъ е к т а X и Y. В классе P o i n t мы о п р е д е л и л и с в о й с т в а X и Y с к л ю ч е в ы м с л о в о м o v e r r i d e , так как они п е р е о п р е д е л я ю т с о о т в е т с т в у ю щ и е свойства базового класса, п е р в о е из к о т о р ы х я в л я е т с я в и р т у а л ь н ы м , а в т о р о е абстрактным. В классе R e c t a n g l e мы п е р е о п р е д е л и л и только а б с т р а к т н о е с в о й с т в о Y (абст­ р а к т н о е с в о й с т в о должно быть п е р е о п р е д е л е н о в п р о и з в о д н о м классе). Ч т о же касает¬ ся с в о й с т в а X, то здесь и с п о л ь з у е т с я с о о т в е т с т в у ю щ е е с в о й с т в о базового класса. М е т о д M a i n нашей п р о г р а м м ы д е м о н с т р и р у е т н е к о т о р ы е п р и е м ы р а б о т ы с объек¬ тами базового класса и п р о и з в о д н ы х к л а с с о в . всего о н создаст н о в ы й о б ъ е к т класса P o i n t , сохраняя ссылку н а этот о б ъ е к т в п е р е м е н н о й базового класса Shape. Д а л е е этот о б ъ е к т р и с у е т с я при п о м о щ и метода D r a w : Shape

pt

new

Point(10,

В б а з о в о м классе м е т о д D r a w о б ъ я в л е н как а б с т р а к т н ы й . В д а н н о м случае исполь¬ зуется р е а л и з а ц и я этого м е т о д а из класса А н а л о г и ч н ы е д е й с т в и я в ы п о л н я ю т с я для о б ъ е к т а класса R e c t a n g l e : Shape

new R e c t a n g l e ( l , 12); своей р а б о т ы

и

4,

10,

20);

п р о г р а м м а создает массив о б ъ е к т о в классов

Point

Rectangle: allShapes

allShapes[l]

new

new new R e c t a n g l e ( 2 , new new

Глава 6. Свойства объектов

3, 2,

1, 3,

221


currentShape Содержимое

этого

in

allShapes)

массива рисуется

с помощью

виртуального

м е т о д а базового

класса D r a w .

Статические свойства С п о м о щ ь ю к л ю ч е в о г о слова s t a t i c м о ж н о о п р е д е л и т ь с т а т и ч е с к и е свойства. Пра¬ вила п р и м е н е н и я т а к и х с в о й с т в а н а л о г и ч н ы п р а в и л а м п р м е н е н и я с т а т и ч е с к и х полей и м е т о д о в . Д л я с с ы л к и на с т а т и ч е с к и е с в о й с т в а н е о б х о д и м о и с п о л ь з о в а т ь имя класса, а не с с ы л к у на о б ъ е к т класса. К р о м е т о г о , в о б ъ я в л е н и и с т а т и ч е с к о г о с в о й с т в а нельзя у п о т р е б л я т ь к л ю ч е в ы е слова v i r t u a l , a b s t r a c t и o v e r r i d e . В л и с т и н г е 6.3 мы п р и в е л и и с х о д н ы й т е к с т п р о г р а м м ы , в к о т о р о й д е м о н с т р и р у е т с я использование статических свойств. Листинг

6.3.

using System; namespace PropStatic class static ScrWidth ScrHeight

public

static

640; 480;

string

Version

get return

"Microsoft

private s t a t i c uint ScrWidth; public s t a t i c uint ScreenWidth get return

ScrWidth;

private s t a t i c uint ScrHeight; public static uint ScreenHeight get 222

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

Фролов. Язык

Самоучитель


return

ScrHeight;

class static

void

args) система: разрешение:

x

Здесь мы о б ъ я в и л и класс S y s l n f в к о т о р о м есть с т а т и ч е с к и й к о н с т р у к т о р и три статических ScreenWidth и В задачу с т а т и ч е с к о г о к о н с т р у к т о р а в х о д и т и н и ц и а л и з а ц и я д в у х с т а т и ч е с к и х по¬ л е й , х р а н я щ и х т е к у щ е е р а з р е ш е н и е экрана м о н и т о р а :

ScrWidth ScrHeight

640; 480;

В н а ш е м п р о с т е й ш е м п р и м е р е мы и н и ц и а л и з и р у е м эти поля ч и с л а м и , о д н а к о ре¬ альная п р о г р а м м а м о ж е т о п р е д е л и т ь ф а к т и ч е с к о е р а з р е ш е н и е и с о х р а н и т ь его в п о л я х класса. С в о й с т в а S c r e e n W i d t h и S c r e e n H e i g h t п о з в о л я ю т п о л у ч и т ь т е к у щ е е разре¬ ш е н и е с о о т в е т с т в е н н о по г о р и з о н т а л и и в е р т и к а л и : p r i v a t e s t a t i c u i n t ScrWidth; public s t a t i c uint ScreenWidth get return

ScrWidth;

private static uint public s t a t i c uint ScreenHeight get return

ScrHeight;

Глава 6. Свойства объектов


З а м е т и м , что п р о г р а м м а не с м о ж е т ределили т о л ь к о п р о ц е д у р у д о с т у п а g e t .

з н а ч е н и е этих с в о й с т в , так как мы оп­

А н а л о г и ч н ы м о б р а з о м объявлен с т а т и ч е с к и й метод V e r s i o n : public

static

string

Version

get return

"Microsoft

Единственная процедура доступа позволяет извлечь название ОС. Для того ч т о б ы о т о б р а з и т ь з н а ч е н и е п е р е ч и с л е н н ы х выше с в о й с т в , мы использова­ ли в м е т о д е M a i n н а ш е й п р о г р а м м ы с л е д у ю щ и е строки: разрешение:

х

В о т что п о я в и т с я на экране в р е з у л ь т а т е р а б о т ы этих строк: Операционная Экранное р а з р е ш е н и е :

Microsoft 640 х 480

Windows

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

224

А В

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

Самоучитель


Глава 7. Массивы и индексаторы Если программа должна работать с набором объектов одинакового типа, во многих случа¬ ях удобно образовать из этих объектов структуру данных, массивом (array). К а ж д ы й э л е м е н т массива имеет свой н о м е р ( и н д е к с ) и х р а н и т один объект. Зная и н д е к с э л е м е н т а м а с с и в а , п р о г р а м м а с м о ж е т извлечь или о б н о в и т ь н у ж н ы й ей объект. З а м е т и м , что в о д н о м массиве м о г у т х р а н и т ь с я о б ъ е к т ы базового класса и произ¬ водных к л а с с о в . Р а н е е в нашей книге мы у ж е п р и в о д и л и п о д о б н ы е п р и м е р ы , д е м о н с т ­ р и р у ю щ и е в о з м о ж н о с т и п о л и м о р ф и з м а в я з ы к е С#. В я з ы к е п р о г р а м м и р о в а н и я С# м а с с и в ы о б о з н а ч а ю т с я с п о м о щ ь ю к в а д р а т н ы х ско­ бок, р а с п о л о ж е н н ы х справа о т о б о з н а ч е н и я типа о б ъ е к т о в , с о с т а в л я ю щ и х м а с с и в . Н и ­ же мы п р и в е л и п р и м е р о п р е д е л е н и я м а с с и в а из 10 ц е л ы х чисел: bonus

new

Здесь о б ъ я в л е н а ссылка b o n u s н а о д н о м е р н ы й для х р а н е н и я ц е л ы х чисел с о з н а к о м т и п а

массив,

содержащий

10 ячеек

Обратите внимание, что мы не просто объявили ссылку на массив, а сразу же создали массив оператором n e w , указав размер массива. Без этого программа не сможет использо­ вать ссылку для работы с массивом, так как в ней будет храниться значение При о п р е д е л е н и и массива не р е з е р в и р у е т с я п а м я т ь , п о э т о м у в о б ъ я в л е н и и ссылки размеры м а с с и в а не у к а з ы в а ю т с я . П о с л е в ы п о л н е н и я р е з е р в и р о в а н и я п а м я т и операто¬ ром n e w р а з м е р м а с с и в а с т а н о в и т с я ф и к с и р о в а н н ы м . В том с л у ч а е , когда п р о г р а м м и с т заранее не знает р а з м е р ы м а с с и в а , м о ж н о у к а з а т ь эти р а з м е р ы динамически во в р е м я работы п р о г р а м м ы , н а п р и м е р : int

BonusArraySize bonus2 new

5;

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

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

Одномерные массивы Одномерный м о ж н о п р е д с т а в и т ь себе в виде л и н е й н о й п о с л е д о в а т е л ь н о с т и ячеек, к а ж д а я из к о т о р ы х и м е е т свой н о м е р . С а м а я первая ячейка имеет н о м е р 0, вто¬ рая — 1 и т. д.

225 8

Язык

Самоучитель


На рис. 7.1 держащий

мы с х е м а т и ч е с к и п о к а з а л и о д н о м е р н ы й м а с с и в с и м е н е м A r r a y , со­ э л е м е н т о в . Эти э л е м е н т ы нумеруются о т 0 д о

|

7.1.

Одномерный массив Array

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

new 5; 7 10;

Здесь мы с о з д а л и о д н о м е р н ы й м а с с и в b o n u s , з а п и с а л и в 3 п е р в ы е я ч е й к и этого м а с с и в а ч и с л а 5, 7 и а затем в ы в е л и с о д е р ж и м о е п р о и н и ц и а л и з и р о в а н н ы х т а к и м способом ячеек на консоль. В д а н н о м с л у ч а е б ы л а и с п о л ь з о в а н а так н а з ы в а е м а я динамическая инициализация массива. П р о г р а м м а и н и ц и а л и з и р у е т м а с с и в , з а п и с ы в а я о б ъ е к т ы в его я ч е й к и . В о з м о ж н а т а к ж е с т а т и ч е с к а я и н и ц и а л и з а ц и я м а с с и в а , когда с о д е р ж и м о е его я ч е е к о п р е д е л я е т с я в м о м е н т к о м п и л я ц и и п р о г р а м м ы . В о т п р и м е р с т а т и ч е с к о й инициализа¬ ции массива: int[]

bonusl

2,

Здесь п о с л е о п е р а т о р а п р и с в а и в а н и я мы у к а з а л и список инициализации массива, заключенн��й в фигурные скобки. О б р а т и т е в н и м а н и е , что при с т а т и ч е с к о й и н и ц и а л и з а ц и и нет н и к а к о й н е о б х о д и м о ¬ сти у к а з ы в а т ь р а з м е р м а с с и в а , так как он о п р е д е л я е т с я а в т о м а т и ч е с к и и с х о д я из количества элементов в списке инициализации. М а с с и в ы С# у д о б н ы т е м , что в них м о ж н о х р а н и т ь о б ъ е к т ы л ю б о г о типа. Н и ж е мы о б ъ я в и л и два с т р о к о в ы х м а с с и в а : Words new HelloWords

"C#",

М а с с и в H e l l o W o r d s п р о и н и ц и а л и з и р о в а н с т а т и ч е с к и , а его с о д е р ж и м о е отобра¬ ж а е т с я на к о н с о л и .

226

А В Фролов, Г.

Язык

Самоучитель


Многомерные массивы М н о г о м е р н ы е м а с с и в ы м о ж н о п р е д с т а в и т ь себе в виде м н о г о м е р н о й м а т р и ц ы , в узлах которой хранятся объекты. Н а р и с . 7.2 м ы показали п р и м е р д в у м е р н о г о м а с с и в а A r r a y . Столбцы Array Array[1,1] Аггау[2,0]

Array[2,1]

Array[2,2]

Array[3,1] Аггау[4,0]

Array[4,1]

j

Ячейка строка 3, столбец 2

7.2.

Двумерный

массив Array

Такой м а с с и в м о ж н о п р е д с т а в и т ь себе в виде строк и

столбцов. Для адресации

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

new

int

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

T w o D i m A r r a y н а д в у м е р н ы й мас¬

сив. К р о м е т о г о , в о п е р а т о р е n e w мы у к а з а л и к о л и ч е с т в о строк и с т о л б ц о в создавае¬ мого д в у м е р н о г о массива. Если н у ж н о о б ъ я в и т ь м н о г о м е р н ы й м а с с и в , и с п о л ь з у й т е н е с к о л ь к о з а п я т ы х : new Количество

запятых

должно

быть

равно

размерности

массива,

уменьшенной

на единицу. Таким образом, в п р е д ы д у щ е м примере мы создали ч е т ы р е х м е р н ы й массив. М н о г о м е р н ы е м а с с и в ы м о ж н о и н и ц и а л и з и р о в а т ь д и н а м и ч е с к и или с т а т и ч е с к и . Ниже приведен пример динамической инициализации двумерного массива: int

TwoDimArray 0]

new 5;

1]

2]

52 32;

Глава 7. Массивы и индексаторы

227


1], 2], П о с л е и н и ц и а л и з а ц и и с о д е р ж и м о е массива в ы в о д и т с я н а к о н с о л ь . Вот п р и м е р с т а т и ч е с к о й и н и ц и а л и з а ц и и д в у м е р н о г о м а с с и в а C r o s s Z e r o , кото­ рый м о ж н о и с п о л ь з о в а т ь , н а п р и м е р , для с о з д а н и я п р о г р а м м ы известной игры в крес¬ тики-нолики: CrossZero

0

В этом м а с с и в е х р а н я т с я с и м в о л ы типа c h a r .

Массивы массивов В я з ы к е С# д о п у с к а е т с я создавать м а с с и в ы м а с с и в о в , н а з ы в а е м ы е также несиммет­ м а с с и в а м и . Д р у г и е названия для н е с и м м е т р и ч н ы х м а с с и в о в , в с т р е ч а ю щ и е с я в л и т е р а т у р е и в д о к у м е н т а ц и и на я з ы к С # , — ступенчатые или (jagged) массивы. На рис. 7.3 мы показали массив, содержащий 5 одномерных массивов разного размера:

Аггау[1,3]

Аггау[3,1] |

|

Array[4,1]

Рис.

|

7.3.

|

|

|

Массив массивов Array

Первый массив содержит 2 ячейки, второй 4, т р е т и й 1 и т. д. И з о б р а ж е н и е та­ кого м а с с и в а п о х о ж е на л е с т н и ц у , о т с ю д а , в и д и м о , и п р о и з о ш е л п е р е в о д слов array как « с т у п е н ч а т ы й м а с с и в » . При н е о б х о д и м о с т и вы м о ж е т е о б ъ е д и н я т ь в м а с с и в ы не только о д н о м е р н ы е , но и м н о г о м е р н ы е м а с с и в ы . О д н а к о р а б о т а с такими о б ъ е к т а м и п о т р е б у е т от вас не¬ дюжинного пространственного воображения. О б ъ я в л е н и е м а с с и в а м а с с и в о в выполняется при п о м о щ и н е с к о л ь к и х пар квадрат­ ных скобок. Ниже, н а п р и м е р , мы о б ъ я в и л и массив м а с с и в о в , с о д е р ж а щ и х т е к с т о в ы е

new 228

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

Самоучитель


в н и м а н и е , что м ы указали р а з м е р н о с т ь н а ш е г о м а с с и в а м а с с и в о в , р а в н у ю д в у м . С о о т в е т с т в е н н о нам н е о б х о д и м о и н и ц и а л и з и р о в а т ь два м а с с и в а строк: new new После инициализации доступ к элементам массивов выполняется следующим образом: [0] [0]

"Hello,

-

C#";

"It"; "is"; "C#";

При п о м о щ и первой пары к в а д р а т н ы х скобок мы у к а з ы в а е м и н д е к с м а с с и в а , а при п о м о щ и второй и н д е к с э л е м е н т а в м а с с и в е . Я з ы к С# д о п у с к а е т с о з д а н и е д о в о л ь н о с л о ж н ы х м н о г о м е р н ы х м а с с и в о в , с о д е р ж а щ и х в себе д р у г и е м н о г о м е р н ы е м а с с и в ы . Н а п р и м е р , ниже мы объявили ссылку на двумерный массив, содержащий четырехмер¬ ный массив, который, в свою очередь, содержит трехмерный массив текстовых строк: VeryComplexArray; М ы , о д н а к о , н е р е к о м е н д у е м увлекаться с о з д а н и е м п о д о б н ы х к о н с т р у к ц и й без крайней на то н е о б х о д и м о с т и . Они могут запутать п р о г р а м м у и сделать ее исход¬ ный текст н е п о н я т н ы м не т о л ь к о д л я п о с т о р о н н и х л ю д е й , но и для самого разработчи¬ ка п р о г р а м м ы .

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

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

выполне¬

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

7.1.

Файл

u s i n g System; namespace class static

void bonus

1 J

new

10;

Глава 7. Массивы и индексаторы

229


int

BonusArraySize bonus2 new

bonusl Console ]= string[]

5;

2, = bonusl[l],

bonusl[0],

Words new HelloWords

"C#",

TwoDimArray 0] 0] 1] 1] 2]

new

int

5; 15; 5; 52;

JaggedArray new new

new

"Hello,

C#";

"is";

230

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

Самоучитель


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

с

(индекс).

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

Однако

таких

ч а щ е всего

операторов,

обработка массивов

как

for,

while,

do

выполняется и

foreach.

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

Обработка одномерного массива чисел В н а ч а л е м ы р а с с м о т р и м самый п р о с т о й прием ц и к л и ч е с к о й о б р а б о т к и о д н о м е р н о г о м а с с и в а , о с н о в а н н ы й н а п р м е н е н и и о п е р а т о р а f o r ( л и с т и н г 7.2). Этот п р и е м и с п о л ь ­ зуется во м н о г и х я з ы к а х п р о г р а м м и р о в а н и я , в ч а с т н о с т и в я з ы к а х С и С + + . Листинг

7.2.

Файл

using System; namespace ArrayLoop class

ArrayLoopApp

static

void

args)

Numbers int

for(i

new

i; 0;

i

1;

i

10;

i+ + )

i++)

В н у т р и м е т о д а M a i n м ы о б ъ я в и л и м а с с и в N u m b e r s , с о д е р ж а щ и й п е р е м е н н ы е ти­ па i n t :

Здесь мы не у к а з ы в а е м размер м а с с и в а , так как при о б ъ я в л е н и и массива память д л я него не р е з е р в и р у е т с я . Глава

Массивы и индексаторы

231


В

строке мы р е з е р в и р у е м п а м я т ь для х р а н е н и я 10 п е р е м е н н ы х типа Ссылку н а эту память м ы з а п и с ы в а е м в п е р е м е н н у ю N u m b e r s , з а в е р ш а я созда­ ние массива: Numbers

new

int

Д о п о л н и т е л ь н о мы о б ъ я в л я е м п е р е м е н н у ю i, которая будет п р и м е н я т ь с я для ин­ д е к с а ц и и массива: int

i;

Запись з н а ч е н и й в я ч е й к и массива (т. е. и н и ц и а л и з а ц и я массива) в ы п о л н я е т с я про¬ стым п р и с в а и в а н и е м в цикле: for(i

0;

i

10; i;

i++)

П о с л е з а в е р ш е н и я этого хранящиеся в массиве:

процесса

i

п р о г р а м м а о т о б р а ж а е т на консоли

значения,

i++)

WriteLine Здесь мы в н а ч а л е в ы в о д и м на к о н с о л ь с о д е р ж и м о е п е р в о г о э л е м е н т а массива, а за¬ тем и остальных: Numbers: О б р а т и т е в н и м а н и е на т о , как мы п р о в е р я е м у с л о в и е в ы х о д а за п р е д е л ы массива. П а р а м е т р цикла i с р а в н и в а е т с я со з н а ч е н и е м N u m b e r s Length. Что это за з н а ч е н и е ? Все м а с с и в ы в С# я в л я ю т с я о б ъ е к т а м и б и б л и о т е ч н о г о класса По­ л е L e n g t h этого класса с о д е р ж и т р а з м е р массива.

Обработка одномерного массива строк В л и с т и н г е 7.3 мы п р и в е л и п р и м е р п р о г р а м м ы , о т о б р а ж а ю щ е й на к о н с о л и содержи¬ мое о д н о м е р н о г о м а с с и в а строк, п р о и н и ц и а л и з и р о в а н н о г о с т а т и ч е с к и . 7.3.

Файл

using System; namespace ArrayLoopl ArrayLooplApp static

void

args) -

232

А

Г.

Фролов. Язык

Самоучитель


i i

i++)

О т о б р а ж е н и е э л е м е н т о в м а с с и в а на к о н с о л и в ы п о л н я е т с я в цикле и не ких о с о б е н н о с т е й : f o r ( i = l;

i

ника¬

i+ + )

В н а ч а л е на к о н с о л ь в ы в о д и т с я самый п е р в ы й э л е м е н т м а с с и в а с н у л е в ы м индек¬ сом, а затем все о с т а л ь н ы е : This

is

С#

Параметр

string

array

цикла и з м е н я е т с я о т е д и н и ц ы

до размера массива, равного

значению

Использование оператора foreach Хотя для ц и к л и ч е с к о й о б р а б о т к и м а с с и в о в м о ж н о и с п о л ь з о в а т ь л ю б ы е ц и к л и ч е с к и е о п е р а т о р ы (и д а ж е цикл, о р г а н и з о в а н н ы й с п о м о щ ь ю н е р е к о м е н д у е м о г о к использо¬ ванию оператора у д о б н е е всего п р и м е н я т ь оператор В л и с т и н г е 7.4 мы п р и в е л и второй в а р и а н т п р о г р а м м ы , о п и с а н н о й в п р е д ы д у щ е м р а з д е л е и п р е д н а з н а ч е н н о й для в ы в о д а на к о н с о л ь с о д е р ж и м о г о м а с с и в а , п р о и н и ц и а л и з и р о в а н н о г о статически. Листинг

7.4.

Файл

using System; namespace ArrayForeach class

ArrayForeachApp

static

void

Глава 7. Массивы и индексаторы

args)

233


string[]

Lines

foreach

(string

in

Lines)

Console

К а к в и д и т е , ц и к л о б р а б о т к и м а с с и в а в ы г л я д и т очень п р о с т о : foreach

(string

CurrentString

in

Lines)

В круглых скобках после оператора foreach мы объя��или переменную C u r r e n t S t r i n g типа к о т о р о й в п р о ц е с с е о б р а б о т к и м а с с и в а L i n e s будет п о с л е д о в а т е л ь н о п р и с в а и в а т ь с я с о д е р ж и м о е к а ж д о й его я ч е й к и . Далее вы сможете использовать переменную C u r r e n t S t r i n g в теле цикла для про¬ смотра элементов массива. К с о ж а л е н и ю , оператор f o r e a c h не позволяет изменять со­ д е р ж и м о е элементов массива, но для просмотра массива (и, как вы узнаете позже, для про¬ смотра с о д е р ж и м о г о контейнеров объектов других типов) он очень удобен.

Многомерный массив объектов класса String Для р а б о т ы с м н о г о м е р н ы м и м а с с и в а м и у д о б н о и с п о л ь з о в а т ь в л о ж е н н ы е о п е р а т о р ы цикла, т а к и е , н а п р и м е р , как В л и с т и н г е 7.5 мы п р и в е л и и с х о д н ы й текст п р о г р а м м ы , к о т о р а я с о з д а е т д в у м е р н ы й м а с с и в т е к с т о в ы х с т р о к , з а п о л н я е т е г о , а затем в ы в о д и т с о д е р ж и м о е м а с с и в а на к о н с о л ь . Листинг

7.5.

Файл

using System; namespace class

ArrayMultiDimApp

static

void

args) A

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

Самоучитель


Colors int

i,

new

j ; 0;

i

for{j

i

2 0;

j

i

3;

2; j

j++)

i++) 3;

В н а ч а л е м ы о б ъ я в л я е м д в у м е р н ы й массив о б ъ е к т о в класса s t r i n g и сразу резер¬ в и р у е м для него п а м я т ь : Colors

new

Таким о б р а з о м , здесь создается м а с с и в из д в у х строк и ч е т ы р е х с т о л б ц о в . Д л я р а б о т ы со с т р о к а м и т а б л и ц ы мы будем и с п о л ь з о в а т ь п е р е м е н н ы е i (для п е р е ­ бора строк т а б л и ц ы ) и j

(для п е р е б о р а с т о л б ц о в т а б л и ц ы ) :

int Массив C o l o r s заполняется в двойном вложенном цикле: for(i

i 0;

2; j

i+ + ) 3;

j++)

В каждую ячейку массива записывается строка вида C o l o r

х и

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

Глава 7. Массивы и индексаторы

235


0

i

2

0;

i+

j

3;

j

Несимметричный массив объектов класса String В нашей с л е д у ю щ е й п р о г р а м м е ( л и с т и н г 7.6) мы д е м о н с т р и р у е м ц и к л и ч е с к у ю обра¬ ботку н е с и м м е т р и ч н о г о м а с с и в а т е к с т о в ы х строк. Листинг

7.6.

Файл

using namespace

static

void

args) Asymm;

int

i,

j new s t r i n g new new i 0;

i++) j

j++)

i for(j

0;

i++) j

j++) i

236

А В

[j

Г.

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


в

Определяя несимметричный с т о л б ц о в и строк:

массив

мы

сначала не у к а з ы в а е м , с к о л ь к о

Asymm; Д а л е е при р е з е р в и р о в а н и и памяти для массива мы задаем т о л ь к о к о л и ч е с т в о строк, равное двум: Asymm

new s t r n g 2

Затем в к а ж д о й строке задается разное к о л и ч е с т в о с т о л б ц о в : в первой строке — 3 с т о л б ц а , а во второй 4: new new И н и ц и а л и з и р у я несимметричный м а с с и в в д в о й н о м в л о ж е н н о м ц и к л е , мы не мо¬ жем п о л а г а т ь с я на т о , что каждая строка с о д е р ж и т о д и н а к о в о е к о л и ч е с т в о с т о л б ц о в . П о э т о м у п р е д е л ь н о е з н а ч е н и е п е р е м е н н о й в н у т р е н н е г о цикла о п р е д е л я е т с я как 0;

i 0;

i++) j

j++)

Для первой строки оно равно т р е м , а для второй — четырем. С о д е р ж и м о е н е с и м м е т р и ч н о г о массива р а с п е ч а т ы в а е т с я н а консоли образом: 0;

i 0;

следующим

i++) j

Length;

j++)

Здесь г р а н и ц ы п е р е м е н н ы х в н е ш н е г о и в н у т р е н н е г о ц и к л о в у к а з а н ы соответствен¬ но как L e n g t h и Asymm i

Индексаторы В п р е д ы д у щ е й главе мы р а с с к а з ы в а л и о с в о й с т в а х о б ъ е к т о в (properties), с п о м о щ ь ю к о т о р ы х м о ж н о создавать «умные» поля. Доступ к т а к и м полям о с у щ е с т в л я е т с я т о л ь ­ ко с п о м о щ ь ю с п е ц и а л ь н ы х п р о ц е д у р g e t и s e t . Н а п о м н и м , что вы м о ж е т е п о л у ч а т ь и и з м е н я т ь з н а ч е н и е свойства с п о м о щ ь ю о б ы ч н о г о о п е р а т о р а п р и с в а и в а н и я , о д н а к о вместо п р о с т о г о к о п и р о в а н и я значения в ы п о л н я е т с я с о о т в е т с т в у ю щ а я п р о ц е д у р а дос¬ тупа. Эта п р о ц е д у р а м о ж е т , н а п р и м е р , с о х р а н я т ь з н а ч е н и е свойства не в поле класса, а в базе д а н н ы х или где-то е щ е , в ы п о л н я я д о п о л н и т е л ь н у ю о б р а б о т к у д а н н ы х . Глава

Массивы и индексаторы

237


В я з ы к е С# м о ж н о н а д е л и т ь лается с

не т о л ь к о поля, но и м а с с и в ы . Это де­

п о м о щ ь ю к о н с т р у к ц и и , н а з ы в а е м о й индексатором (indexer).

Чтобы

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

более п о н я т н ы м , р а с с м о т р и м п р а к т и ч е с к и й

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

вернуть строку «Ка¬

нал н е д о с т у п е н » . Для у п р о щ е н и я п р о г р а м м ы с названиями

каналов

в

нам х о т е л о с ь

каком-либо

бы

классе,

инкапсулировать например

в

алгоритм работы

классе

с

названием

С н а б д и в этот класс и н д е к с а т о р о м , м о ж н о о р г а н и з о в а т ь д о с т у п к на¬ з в а н и я м к а н а л о в , как будто бы они х р а н я т с я в о б ы ч н о м м а с с и в е : new ch[0] ch[l] ch[2] ch[3] ch[4]

"Мир "Наше

i string

0;

i

i++)

s

Как видите,

здесь м ы создали о б ъ е к т

к л а с с а C h a n n e l N a m e s , передав конст¬

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

способом

о б ъ е к т ch

ведет себя п о д о б н о

массиву,

однако

он

п р е д с т а в л я е т собой н е ч т о б о л ь ш е е , чем о б ы ч н ы й м а с с и в . Рассмотрим

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

в

которой

определен

класс

Channel-

N a m e s ( л и с т и н г 7.7).

238

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

Самоучитель


7.7.

Листинг

Файл

using System; namespace class

ChannelNames

private private

uint

public

Channels; ArraySize;

ChannelNames ( u i n t

Channels ArraySize public

Count)

new Count;

uint

Size

get return

public

ArraySize;

string

this[uint

index]

get 0 return else return

"Канал

index

недоступен";

set 0

index

class static

void

args)

ChannelNames

ch[l] ch[2]

new

"Мир "Боевик";

Глава 7. Массивы и индексаторы

239


i

0;

i

i++)

s

В C h a n n e l N a m e s мы о б ъ я в и л и с с ы л к у на м а с с и в т е к с т о в ы х строк C h a n n e l s . С о о т в е т с т в у ю щ е е поле и м е е т м о д и ф и к а т о р д о с т у п а p r i v a t e , поэтому д о с т у п к н е м у в о з м о ж е н т о л ь к о для м е т о д о в класса C h a n n e l N a m e s : private

Channels;

К р о м е т о г о , в классе о б ъ я в л е н о поле S i z e , х р а н я щ е е р а з м е р массива названий ка¬ налов: private

uint

К э т о м у п о л ю т о ж е и м е ю т д о с т у п т о л ь к о ч л е н ы класса C h a n n e l N a m e s . К о н с т р у к т о р класса C h a n n e l N a m e s создает массив з а д а н н о г о р а з м е р а и сохраня¬ е т этот р а з м е р в п о л е A r r a y S i z e : public

ChannelNames ( u i n t

Channels ArraySize

Count)

new Count;

Чтобы программа, внешняя о т н о ш е н и ю к классу C h a n n e l N a m e s , могла опреде­ лять количество элементов в массиве объявили в классе свойство S i z e : public

uint

Size

get return

не

ArraySize;

В этом с в о й с т в е п р е д у с м о т р е н т о л ь к о один м е т о д д о с т у п а g e t , п о э т о м у п р о г р а м м а с м о ж е т и з м е н и т ь с о д е р ж и м о е поля A r r a y S i z e после с о з д а н и я о б ъ е к т а класса

Объявление индексатора Теперь public

мы

п е р е х о д и м к с а м о м у и н т е р е с н о м у — к объявлению

string

index]

get

240

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

Язык

Самоучитель


0 return else return

"Канал

index

недоступен";

set 0

index value;

Как в и д и т е , о б ъ я в л е н и е и н д е к с а т о р а очень п о х о ж е на о б ъ я в л е н и е свойства. В нем тоже м о г у т быть п р о ц е д у р ы д о с т у п а g e t и s e t , п р и ч е м д о п у с к а е т с я о б ъ я в л я т ь л и б о обе эти п р о ц е д у р ы , л и б о только к а к у ю - т о одну из них. П р о ц е д у р ы д о с т у п а п о л н о с т ь ю у п р а в л я ю т п р о ц е с с о м записи д а н н ы х в « у м н ы й » м а с с и в , а т а к ж е и з в л е ч е н и е м д а н н ы х из этого массива. При о б ъ я в л е н и и и н д е к с а т о р а , как и при о б ъ я в л е н и и свойства, мы д о л ж н ы у к а з а т ь м о д и ф и к а т о р д о с т у п а и тип. В н а ш е м случае мы создали о б щ е д о с т у п н ы й и н д е к с а т о р , указав м о д и ф и к а т о р д о с т у п а p u b l i c . Так как названия к а н а л о в п р е д с т а в л я ю т собой т е к с т о в ы е с т р о к и , тип и н д е к с а т о р а задан как s t r i n g . К л а с с , с о д е р ж а щ и й и н д е к с а т о р , в ы с т у п а е т в роли массива. С с ы л к а на такой м а с с и в в ы п о л н я е т с я л и б о с и с п о л ь з о в а н и е м и м е н и объекта класса, л и б о ч е р е з имя класса (ес¬ ли и н д е к с а т о р о б ъ я в л е н как с т а т и ч е с к и й ) . П о э т о м у для и н д е к с а т о р а не т р е б у е т с я ука¬ зывать к а к о е - т о о с о б е н н о е и м я . В качестве имени и н д е к с а т о р а в ы с т у п а е т к л ю ч е в о е слово t h i s , о б о з н а ч а ю щ е е ссылку н а о б ъ е к т д а н н о г о класса. П о с л е к л ю ч е в о г о слова t h i s в о б ъ я в л е н и и и н д е к с а т о р а следует п а р а м е т р , заклю¬ ч е н н ы й в к в а д р а т н ы е скобки. Э т о т п а р а м е т р играет роль и н д е к с а и в н а ш е м случае имеет ц е л о ч и с л е н н ы й тип При в ы м о ж е т е и с п о л ь з о в а т ь д л я ин­ дексации п а р а м е т р ы л ю б о г о т и п а , н а п р и м е р текстовые с т р о к и . В п р о ц е д у р а х д о с т у п а g e t и s e t п а р а м е т р и н д е к с а т о р а п р и м е н я е т с я д л я получе¬ ния д о с т у п а к э л е м е н т у массива. В н а ш е м случае н а з в а н и я к а н а л о в х р а н я т с я в п р о с т о м о д н о м е р н о м м а с с и в е т е к с т о в ы х строк C h a n n e l s . О д н а к о при н е о б х о д и м о с т и п р о ц е ­ дура д о с т у п а м о г л а бы извлекать эти н а з в а н и я из базы д а н н ы х или п о л у ч а т ь через И н ­ тернет, п о л ь з у я с ь для и д е н т и ф и к а ц и и канала з н а ч е н и е м п а р а м е т р а i n d e x . П р о ц е д у р а д о с т у п а s e t и н д е к с а т о р а , так ж е как и а н а л о г и ч н а я п р о ц е д у р а д о с т у п а с в о й с т в а , п о л ь з у е т с я к л ю ч е в ы м с л о в о м v a l u e для у с т а н о в к и нового з н а ч е н и я масси¬ ва. При этом н у ж н ы й э л е м е н т массива и д е н т и ф и ц и р у е т с я при п о м о щ и п а р а м е т р а ин¬ дексатора. Приведенная выше реализация процедуры доступа g e t выполняет необходимый нам а л г о р и т м о п р е д е л е н и я н а з в а н и я канала по его номеру. Если указан н е п р а в и л ь н ы й н о м е р , она в о з в р а щ а е т строку «Канал н е д о с т у п е н » . Что же касается п р о ц е д у р ы д о с т у п а то она тоже в ы п о л н я е т н е к о т о р ы е про¬ верки. В ч а с т н о с т и , при у к а з а н и и н е д о п у с т и м о г о номера канала она и г н о р и р у е т по¬ пытку записи в м а с с и в нового з н а ч е н и я . и индексаторы

241


Индексаторы многомерных массивов В п р е д ы д у щ е м разделе мы п о к а з а л и , как п о л ь з о в а т ь с я и н д е к с а т о р а м и для «интеллектуального»

доступа

к

одномерному

массиву

строк.

При

необходимости

вы т а к ж е м о ж е т е создавать и н д е к с а т о р ы и для м н о г о м е р н ы х м а с с и в о в . Теперь н е м н о г о у с о в е р ш е н с т в у е м п р о г р а м м у , и с х о д н ы й текст которой был приве­ ден в л и с т и н г е 7.7. Мы п о т р е б у е м , чтобы п р о г р а м м а х р а н и л а для к а ж д о г о н о м е р кана¬ ла не о д н о , а два н а з в а н и я . П е р в о е название пусть будет с о о т в е т с т в о в а т ь , н а п р и м е р , отечественному каналу, а второе — зарубежному. Для р е а л и з а ц и и этой логики нам п о т р е б у е т с я д в у м е р н ы й массив т е к с т о в ы х строк. В новом в а р и а н т е п р о г р а м м ы ( л и с т и н г 7.8) мы о б ъ я в и л и такой м а с с и в , а т а к ж е индек¬ сатор для о р г а н и з а ц и и к нему « и н т е л л е к т у а л ь н о г о » д о с т у п а . Листинг

7.8.

Файл

using System; namespace class

ChannelNames

private private public

Channels; ArraySize;

uint

ChannelNames ( u i n t

Channels ArraySize

public

uint

Count)

new s t r i n g [ C o u n t , Count;

Size

get return

public

ArraySize;

string

this[uint

index,

uint

language]

get 0 return else return

242

index

"Канал

А В.

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

Самоучитель


set if ( i n d e x

0

index language]

value;

class static

void

args)

ChannelNames ch[0, ch[l,

ch[2,

ch[0, ch[l, ch ch[3,

0] 0] 0] 0]

new

"Спорт";

"Наше кино"

1] 1] 1] 1]

for(uint

ch

"Discovery" "Fashion"; i

0;

i

i++)

string s

ch[i,

С с ы л к а на д в у м е р н ы й м а с с и в о б ъ я в л е н а с л е д у ю щ и м о б р а з о м : private

0],

ch[i,

1]

*

Channels;

Она и н и ц и а л и з и р у е т с я в к о н с т р у к т о р е , з а д а ю щ е м к о л и ч е с т в о строк и с т о л б ц о в д в у м е р н о г о массива: public

ChannelNames ( u i n t

Channels ArraySize

Count)

new Count;

К о л и ч е с т в о строк о п р е д е л я е т с я п а р а м е т р о м к о н с т р у к т о р а , а к о л и ч е с т в о с т о л б ц о в фиксировано и равно двум. Для индексатора нам теперь потребуется два параметра, первый из которых будет за¬ давать номер канала, а второй номер языка (0 для русского языка, и 1 для английского):

Глава 7. Массивы и индексаторы

243


public

string

index,

uint

language]

get 0 return else return

index

"Канал

недоступен";

set 0

Здесь

index language]

процедура доступа

get

value;

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

заданным

и на з а д а н н о м я з ы к е или строку «Канал н е д о с т у п е н » в случае о ш и б к и . П р о ц е д у р а дос¬ тупа s e t и з м е н я е т н а з в а н и е з а д а н н о г о канала с у ч е т о м н о м е р а я з ы к а . В теле метода M a i n , получающего управление сразу после запуска п р о г р а м м ы , про­ грамма создает объект ch класса C h a n n e l N a m e s , передавая конструктору значение 5: ChannelNames

ch

new C h a n n e l N a m e s

В р е з у л ь т а т е этот о б ъ е к т с м о ж е т х р а н и т ь и н ф о р м а ц и ю о пяти к а н а л а х на одном из д в у х языков. Далее п р о г р а м м а инициализирует список каналов, обращаясь неявным образом к индексатору класса C h a n n e l N a m e s : ch[0, ch[2, ch[4,

ch[l, ch[2, ch[3,

0] 0] 0] 0] 0]

"Наше к и н о " t

1) 1] 1] 1] 1)

"Discovery" "TV

-

Как в и д и т е , в н а ч а л е и н и ц и а л и з и р у ю т с я р у с с к и е к а н а л ы , затем — з а р у б е ж н ы е . После инициализации

программа отображает на экране полный

список каналов,

также неявно обращаясь к индексатору: for(uint string

244

i

0; s

i String

i+ + ) ch[i,

0],

ch[i,

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

Самоучитель


В о т что п о я в и т с я на консоли после запуска нашей п р о г р а м м ы : Спорт Мир кино ( D i s c o v e r y ) Боевик (TV 5) Наше кино ( F a s h i o n )

Дополнительные операции с м а с с и в а м и в С# Р а с с к а з ы в а я о таких типах д а н н ы х , как i n t , c h a r и т. п., мы о б р а щ а л и в а ш е в н и м а ­ на т о , что эти типы д а н н ы х созданы на базе с о о т в е т с т в у ю щ и х к л а с с о в . Что же ка­ сается м а с с и в о в , т о они тоже с о з д а н ы н а базе класса

Функциональ­

ность, и н к а п с у л и р о в а н н а я в этом классе, н а д е л я е т массивы С# д о п о л н и т е л ь н ы м и воз¬ можностями,

недоступными

в

массивах,

реализованных

средствами других языков

п р о г р а м м и р о в а н и я . М а с с и в ы С# в о т л и ч и е от м а с с и в о в С + + , м о ж н о , н а п р и м е р , копи¬ ровать и с о р т и р о в а т ь . В

этом

разделе

мы

рассмотрим

наиболее полезные методы

и

с в о й с т в а класса

к о т о р ы е в ы м о ж е т е п р и м е н я т ь при р а б о т е с м а с с и в а м и .

Определение размера массива Ранее в н а ш и х п р о г р а м м а х мы у ж е о п р е д е л я л и к о л и ч е с т в о э л е м е н т о в , и м е ю щ и х с я в м а с с и в е , о б р а щ а я с ь д л я этого к с в о й с т в у L e n g t h . С у щ е с т в у ю т и д р у г и е средства, п о з в о л я ю щ и е п о л у ч и т ь

информацию о размере

массива. Н а п р и м е р , с п о м о щ ь ю с в о й с т в а R a n k м о ж н о у з н а т ь р а з м е р н о с т ь ( р а н г ) масси¬ ва.

Для

о д н о м е р н ы х массивов значение ранга равно е д и н и ц е , для д в у м е р н ы х —

д в у м и т. д. Методы

и

G e t U p p e r B o u n d п о з в о л я ю т узнать

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

м и н и м а л ь н о е и м а к с и м а л ь н о е з н а ч е н и е индекса э л е м е н т о в , х р а н я щ и х с я в м а с с и в е . В

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

ш е н н о е на е д и н и ц у . Для и з м е н е н и я з н а ч е н и я , х р а н я щ е г о с я в я ч е й к е массива, м о ж н о и с п о л ь з о в а т ь не только к в а д р а т н ы е с к о б к и , но и метод S e t V a l u e . При р а б о т е с о д н о м е р н ы м и масси¬ вами в качестве первого п а р а м е т р а этому м е т о д у н у ж н о п е р е д а т ь и з м е н я е м о е значе¬ н и е , а в качестве второго — и н д е к с с о о т в е т с т в у ю щ е й я ч е й к и массива. На наш взгляд, однако,

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

с к о б о к п р е д с т а в л я е т собой

более

наглядный

способ

работы

с массивами. В л и с т и н г е 7.9 мы привели п р и м е р п р о г р а м м ы , д е м о н с т р и р у ю щ е й и с п о л ь з о в а н и е п е р е ч и с л е н н ы х выше свойств и м е т о д о в . Глава 7. Массивы и индексаторы

245


Листинг

7.9.

Файл

using System; namespace class static

void

args) 3, 5, 7, массива

Console

i

9,

in

изменения

i

элемента

in

массива: граница граница

массива: массива:

Console ReadLine

П о л у ч и в у п р а в л е н и е , п р о г р а м м а с о з д а е т м а с с и в ц е л ы х чисел и н и ц и а л и з и р у я его с т а т и ч е с к и : int[]

arrayOfNumbers

3,

5,

7,

9,

Далее п р о г р а м м а о т о б р а ж а е т н а к о н с о л и к о л и ч е с т в о э л е м е н т о в ( ч и с е л ) , х р а н я щ и х ­ ся в м а с с и в е , а т а к ж е в ы в о д и т все эти э л е м е н т ы на К О Н С О Л Ь : массива i

in i

246

А В

Г.

Язык

Самоучитель


К о л и ч е с т в о э л е м е н т о в , х р а н я щ и х с я в м а с с и в е , извлекается и з с в о й с т в а L e n g t h ,

о п р е д е л е н н о г о в классе Далее мы з а п и с ы в а е м в ячейку массива с и н д е к с о м 5 з н а ч е н и е , р а в н о е 14, в ы з ы в а я для этого м е т о д

А н а л о г и ч н ы й р е з у л ь т а т м о ж н о п о л у ч и т ь и с п о м о щ ь ю к в а д р а т н ы х скобок:

И з м е н и в з н а ч е н и е э л е м е н т а м а с с и в а , п р о г р а м м а вновь в ы в о д и т его с о д е р ж и м о е на к о н с о л ь . На с л е д у ю щ е м этапе своей р а б о т ы п р о г р а м м а в ы в о д и т на консоль р а з м е р н о с т ь м а с с и в а , о б р а щ а я с ь для ее о п р е д е л е н и я к свойству R a n k : массива: И н а к о н е ц , перед тем как з а в е р ш и т ь свое в ы п о л н е н и е , п р о г р а м м а в ы в о д и т на кон¬ соль м и н и м а л ь н о е и м а к с и м а л ь н о е з н а ч е н и е и н д е к с а для э л е м е н т о в , х р а н я щ и х с я в массиве: граница

массива:

граница

массива:

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

и

GetUpperBound

нулевое

з н а ч е н и е , т. е. значение р а н г а , у м е н ь ш е н н о е на е д и н и ц у .

Сортировка и реверсирование массивов С помощью статических методов Sort и R e v e r s e м о ж н о соответст¬ венно о т с о р т и р о в а т ь э л е м е н т ы массива и п е р е с т а в и т ь их в о б р а т н о м п о р я д к е . Исполь¬ з о в а н и е этих м е т о д о в д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т к о т о р о й приве¬ ден в л и с т и н г е 7.10. Листинг

7.10,

Файл

using System; namespace SortReverse class

SortReverseApp

static

void

args) 3,

Console

"Исходный i

Главв

51,

7,

29,

массив:

in

Массивы и индексаторы

i);

247


массив: i

in

i

in i);

В ней о б ъ я в л я е т с я массив ц е л ы х ч и с е л с п р и м е н е н и е м с т а т и ч е с к о й и н и ц и а л и з а ц и и : int[]

arrayOfNumbers

3,

51,

7,

29,

Далее наша программа сортирует массив, вызывая метод A r r a y . S o r t :

С о д е р ж и м о е и с х о д н о г о и о т с о р т и р о в а н н о г о массива в ы в о д и т с я на к о н с о л ь при по¬ мощи п р о с т е й ш е г о цикла: i

in i);

Э т о т же цикл п р и м е н я е т с я и для в ы в о д а с о д е р ж и м о г о м а с с и в а , э л е м е н т ы к о т о р о г о были п е р е с т а в л е н ы в о б р а т н о м при п о м о щ и метода A r r a y

В о т что н а ш а п р о г р а м м а в ы в о д и т н а к о н с о л ь : Исходный м а с с и в : 21 3 51 7 29 11 С о р т и р о в а н н ы й м а с с и в : 3 7 11 21 29 51 Р е в е р с и р о в а н н ы й м а с с и в : 51 29 21 11 7 3 З а м е т и м , что оба о п и с а н н ы х в ы ш е м е т о д а п о з в о л я ю т р а б о т а т ь с м а с с и в а м и л ю б ы х о б ъ е к т о в , а не т о л ь к о чисел. П р и н е о б х о д и м о с т и вы м о ж е т е о т с о р т и р о в а т ь м е т о д о м S o r t , н а п р и м е р , массив т е к с т о в ы х строк.

Поиск в массиве С помощью метода B i n a r y S e a r c h м о ж н о о р г а н и з о в а т ь поиск э л е м е н т о в в о д н о м е р н о м м а с с и в е . В к а ч е с т в е первого п а р а м е т р а этому методу н у ж н о п е р е д а т ь с с ы л к у на м а с с и в , а в качестве в т о р о г о — и с к о м ы й элемент. П р и у с п е х е ме¬ тод возвратит индекс найденного элемента, а в том с л у ч а е , если элемент не найден от¬ рицательное з н а ч е н и е . 248

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

Самоучитель


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

применение метода

n a r y S e a r c h для поиска текстовой строки в м а с с и в е , п р и в е д е н в л и с т и н г е Листинг

7.11.

Файл

using System; namespace class

BinarySearchApp

static

void

args) arrayOfNumbers

string

searchString

int if

(index

0 \"{0}\"

не

else строки

\"{0}\"

равен

index

В п р о г р а м м е объявлен и п р о и н и ц и а л и з и р о в а н с т а т и ч е с к и массив т е к с т о в ы х строк arrayOfNumbers М е т о д M a i n ищет в м а с с и в е слово Array

вызывая для этого статический метод

Д а н н ы й м е т о д п о л у ч а е т в качестве п е р в о г о п а р а м е т р а ссыл¬

ку на м а с с и в , а в качестве в т о р о г о — ссылку на и с к о м у ю строку. Если искомая строка н е найдена, м е т о д A r r a y

в о з в р а щ а е т отри¬

ц а т е л ь н о е з н а ч е н и е . В случае успеха в о з в р а щ а е т с я и н д е к с н а й д е н н о й строки. Н а ш а п р о г р а м м а о т о б р а ж а е т на консоли и с к о м у ю строку и ее индекс. Глава

Массивы и индексаторы

249


Глава 8. Интерфейсы Как мы у ж е г о в о р и л и в гл. 3, класс С# м о ж е т н а с л е д о в а т ь с в о й с т в а т о л ь к о одного ба­ з о в о г о класса. Т а к и м о б р а з о м , в о т л и ч и е от д р у г и х я з ы к о в О О П ( н а п р и м е р , С++) в я з ы к е С# к а ж д ы й п р о и з в о д н ы й класс м о ж е т иметь только один базовый класс. На первый взгляд такое ограничение может показаться довольно существенным, одна­ ко во многих случаях оно у с п е ш н о обходится при помощи механизма интерфейсов. В то время как классы п р е д с т а в л я ю т собой м е х а н и з м для п р е д с т а в л е н и я с у щ н о с т е й ( т а к и х , н а п р и м е р , как г е о м е т р и ч е с к и е ф и г у р ы , т е л е в и з о р ы и т. п.), интер¬ фейсы п р и м е н я ю т с я для о п и с а н и я н е к и х д е й с т в и й над э т и м и с у щ н о с т я м и . П о н я т и е и н т е р ф е й с а о ч е в и д н о н а о б ы ч н о м ж и т е й с к о м у р о в н е . П р е д с т а в ь т е себе, н а п р и м е р , плеер для п р о и г р ы в а н и я к о м п а к т - д и с к о в . С у щ е с т в у е т б е с ч и с л е н н о е множе¬ ство р а з л и ч н ы х м о д е л е й т а к и х п л е е р о в , о т л и ч а ю щ и х с я друг о т д р у г а ф о р м о й к о р п у с а , ц в е т о м , р а з м е р о м и д р у г и м и а т р и б у т а м и . Тем не менее все они и м е ю т о д и н а к о в ы й н а б о р к н о п о к , с п о м о щ ь ю к о т о р ы х м о ж н о з а п у с к а т ь или о с т а н а в л и в а т ь п р о и г р ы в а н и е к о м п а к т - д и с к а , п е р е х о д и т ь с одной д о р о ж к и на д р у г у ю , а также извле¬ кать к о м п а к т - д и с к из к о р п у с а плеера. Такая у н и ф и к а ц и я « п о л ь з о в а т е л ь с к о г о и н т е р ф е й с а » п л е е р а п о з в о л я е т л ю б о м у из вас быстро о с в о и т ь н о в ы й п л е е р , не р а з б и р а я с ь в д е т а л я х его в н у т р е н н е г о устрой¬ ства. А н а л о г и ч н о вы л е г к о сумеете в о с п о л ь з о в а т ь с я б а н к о м а т о м л ю б о й модели для п о л у ч е н и я д е н е г с к р е д и т н о й к а р т о ч к и , так как все б а н к о м а т ы и м е ю т один и тот же «интерфейс». В о з в р а щ а я с ь к я з ы к у С # , з а м е т и м , что и с п о л ь з о в а н и е р а с с м о т р е н н ы х ранее с в о й с т в , и н д е к с а т о р о в , с о б ы т и й (о к о т о р ы х мы р а с с к а ж е м п о з ж е ) , а т а к ж е интерфей¬ сов п о з в о л я е т с о з д а в а т ь о б ъ е к т ы , к о т о р ы е м о ж н о и с п о л ь з о в а т ь , не вникая в детали их р е а л и з а ц и и . П р и этом о б ъ е к т (класс) м о ж е т реализовать набор и н т е р ф е й с о в , к а ж д ы й из к о т о р ы х о т в е ч а е т за в ы п о л н е н и е над о б ъ е к т о м к а к и х - л и б о д е й с т в и й . И н т е р ф е й с ы б о л е е всего п о х о ж и н а в и р т у а л ь н ы е м е т о д ы а б с т р а к т н о г о класса, ко­ т о р ы е д о л ж н ы быть о п р е д е л е н ы в б а з о в о м классе. Х о р о ш е й н о в о с т ь ю я в л я е т с я т о , что к а ж д ы й класс С # м о ж е т р е а л и з о в а т ь п р о и з в о л ь н о е к о л и ч е с т в о и н т е р ф е й с о в . И м е н н о это о б с т о я т е л ь с т в о д е л а е т н е с у щ е с т в е н н ы м о г р а н и ч е н и е С#, к а с а ю щ е е с я не¬ возможности множественного наследования классов.

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

250


Объявление интерфейса П у с т ь нам н у ж н о создать класс P o i n t , о т р а ж а ю щ и й п о в е д е н и е точки н а п л о с к о с т и . Этот о б ъ е к т д о л ж е н х р а н и т ь т е к у щ и е к о о р д и н а т ы т о ч к и , делая и х д о с т у п н ы м и при п о м о щ и свойств X и Y . К р о м е т о г о , класс P o i n t д о л ж е н два и н т е р ф е й ­ с а I P r i n t и I M a i l , первый и з к о т о р ы х п о з в о л я е т р а с п е ч а т ы в а т ь точку н а п р и н т е р е и в ы п о л н я т ь п р е д в а р и т е л ь н ы й п р о с м о т р р е з у л ь т а т о в п е ч а т и , а второй — о т п р а в л я т ь точку по э л е к т р о н н о й почте по з а д а н н о м у адресу. Ниже м ы привели объявление интерфейсов I P r i n t и interface void void

IPrint Print

interface

IMail

void

(string

Как в и д и т е , в о б ъ я в л е н и и и н т е р ф е й с о в мы у к а з ы в а е м т о л ь к о п р о т о т и п ы м е т о д о в , не в к л ю ч а я тело этих м е т о д о в . За к о н к р е т н у ю р е а л и з а ц и ю м е т о д о в и н т е р ф е й с а отве¬ чает к л а с с , к о т о р ы й р е а л и з у е т этот и н т е р ф е й с . К р о м е т о г о , в п р о т о т и п а х м е т о д о в не т р е б у е т с я (и не д о п у с к а е т с я ) у к а з ы в а т ь мо¬ д и ф и к а т о р ы д о с т у п а , т а к и е , как p u b l i c или Все м е т о д ы и н т е р ф е й с а явля¬ ются о б щ е д о с т у п н ы м и п о у м о л ч а н и ю . Обратите также внимание на названия интерфейсов. В принято, что название ин­ терфейса начинается с прописной буквы I. И хотя это не строгое правило, мы рекоменду¬ ем его придерживаться, так как при этом исходный текст программы будет понятнее.

Реализация интерфейса При объявлении интерфейсов мы устанавливаем соглашение (контракт), которому д о л ж н ы у д о в л е т в о р я т ь м е т о д ы , с в о й с т в а , и н д е к с а т о р ы и с о б ы т и я , о б ъ я в л е н н ы е в рам¬ ках и н т е р ф е й с а . Ч т о же касается к о н к р е т н о й р е а л и з а ц и и и н т е р ф е й с а , то она, как мы уже г о в о р и л и , в о з л а г а е т с я н а класс, р е а л и з у ю щ и й и н т е р ф е й с . Н и ж е м ы п р и в е л и в с о к р а щ е н н о м виде и с х о д н ы й текст класса P o i n t , р е а л и з у ю ­ щего и н т е р ф е й с ы I P r i n t и I M a i l : class

Point

protected protected public

IPrint, int int

IMail

xPos; yPos;

P o i n t ( i n t x,

int

y)

xPos yPos

Глава 8. Интерфейсы

251


void точки

this.X,

void

"Просмотр

перед

печатью

void

точки

this.X,

this.Y);

mailAddress)

ПО адресу

"Отправка т о ч к и this.X, this.Y,

Тот факт, что класс р е а л и з у е т те или и н ы е и н т е р ф е й с ы , о т р а ж а е т с я в к л ю ч е в о м слове c l a s s . Н а з в а н и я р е а л и з у е м ы х и н т е р ф е й с о в п е р е ч и с л я ю т с я после этого слова через з а п я т у ю : class

Point

IPrint,

IMail

Если бы наш класс P o i n t был у н а с л е д о в а н от б а з о в о г о класса с и м е н е м , напри¬ м е р , S h a p e и д о п о л н и т е л ь н о р е а л и з о в ы в а л и н т е р ф е й с ы I P r i n t и I M a i l , это м о ж н о было бы з а п и с а т ь с л е д у ю щ и м о б р а з о м : Point

Shape,

IPrint,

IMail

О д н а к о п р о с т о г о п е р е ч и с л е н и я р е а л и з у е м ы х и н т е р ф е й с о в н е д о с т а т о ч н о . В классе необходимо

расположить

тело

методов

этих

интерфейсов.

В

нашем

случае

класс

P o i n t д о л ж е н с о д е р ж а т ь тело всех м е т о д о в и н т е р ф е й �� о в I P r i n t и I M a i l . О б р а т и т е в н и м а н и е , как м ы о п р е д е л и л и м е т о д P r i n t и н т е р ф е й с а I P r i n t : void точки

И м я м е т о д а у к а з а н о как I P r i n t

this.X,

this.Y);

Здесь мы с н а б д и л и имя м е т о д а п р е ф и к ­

сом в виде и м е н и и н т е р ф е й с а . Х о т я такой п р е ф и к с н е о б я з а т е л е н ,

же л у ч ш е его

у к а з ы в а т ь . Это п о з в о л и т и з б е ж а т ь н е о д н о з н а ч н о с т и , если класс р е а л и з у е т и н т е р ф е й с ы с

одинаковыми

и

названиями

методов.

Например,

если

бы

в

интерфейсах

IPrint

был о п р е д е л е н м е т о д , при р е а л и з а ц и и м е т о д о в мы могли бы их р а з л и ч а т ь

п о полным именам

252

и

C h e c k соответственно.

А В

Г В Фролов

Самоучитель


Вызов методов интерфейса П о с л е того как мы о б ъ я в и л и и н т е р ф е й с и р е а л и з о в а л и к употреблению.

в к л а с с е , и н т е р ф е й с готов

Как же им п о л ь з о в а т ь с я ? Д о с т а т о ч н о просто. В н а ч а л е н у ж н о создать о б ъ е к т класса, р е а л и з у ю щ е г о наш ин¬ терфейс: Point pt; pt new

20);

Далее нам н у ж н о создать ссылку на и н т е р ф е й с . Это делается с л е д у ю щ и м о б р а з о м : IPrint

ptPrinter

(IPrint)pt;

Здесь м ы о б ъ я в и л и п е р е м е н н у ю p t P r i n t e r т и п а I P r i n t и з а п и с а л и в нее ссыл¬ ку, п р и в е д я ее тип я в н ы м о б р а з о м к типу I P r i n t . С п о м о щ ь ю такой ссылки м о ж н о а д р е с о в а т ь с я к м е т о д а м и н т е р ф е й с а I P r i n t , но нельзя п о л у ч и т ь д о с т у п к п о л я м и ме­ т о д а м класса

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

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

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

объявление, реализацию и использование описанных выше интерфейсов. Листинг using

8.1.

Файл

System;

namespace interface void void interface

RectPrintable IPrint Print

IMail

void

Интерфейсы

253


class

Point

protected protected public

IPrint, int int

xPos; yPos;

Point ( i n t x,

xPos yPos

public

IMail

int

y)

x; y;

int

X

set xPos

value;

return

xPos;

get

public

int

Y

set yPos

value;

return

yPos;

get

void точки

this.X,

void

"Просмотр this.X,

void

254

перед

печатью

(string

точки

mailAddress)

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

Самоучитель


"Отправка точки this.X, this.Y,

адресу

2

class static

void

args)

Point pt; pt new IPrint

IMail

ptPrinter

(IPrint)pt;

ptMailer

В классе P o i n t определен конструктор, а также свойства X и Y, предназначенные для работы с координатами точки. Этот класс р е а л и з у е т интерфейсы I P r i n t и для чего в нем р а с п о л о ж е н о объявление м е т о д о в PrintPreview и IMail Эти м е т о д ы стимулируют выполне­ ние операций, отображая текстовые на консоли.

Проверка реализации интерфейса В предыдущем разделе мы описали один из возможных способов использования интер­ основанный на явном приведении типа ссылки на объект класса к типу Point p t ; pt new P o i n t IPrint ptPrinter

2 0);

Здесь м ы создали объект p t класса P o i n t ,

а также п е р е м е н н у ю p t P r i n t e r ,

для хранения ссылки на интерфейс переменной p t к типу

З а т е м мы привели тип

с помощью скобок.

На следующем этапе аналогичная операция была проведена и для интерфейса IMail

ptMailer

Глава 8 Интерфейсы

(IMail)pt;

255


Так как класс P o i n t оба и н т е р ф е й с а I P r i n t и I M a i l , в процессе пре¬ о б р а з о в а н и я т и п о в у нас не в о з н и к а е т никаких п р о б л е м . О д н а к о если бы мы попыта¬ лись привести с с ы л к у на класса к типу ссылки на и н т е р ф е й с , р е а л и з а ц и я кото¬ рого о т с у т с т в у е т в этом к л а с с е , такая о п е р а ц и я вызвала бы и с к л ю ч е н и е на этапе вы¬ полнения программы. И з у ч и м эту с и т у а ц и ю на п р и м е р е п р о г р а м м ы , и с х о д н ы й текст к о т о р о й приведен в л и с т и н г е 8.2. Листинг using

8.2.

ch08\ITelevision\ITelevisionApp.cs

Файл

System;

namespace

ITelevision

interface

IChannel

void uint

interface void uint

IVolume

current

interface

uint

ITunning

current

class

IChannel,

private private public

uint uint

IVolume

volume;

TvSet()

channel volume

1;

void channel

256

В.

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

Самоучитель


uint return

void volume

volumeLevel;

uint return

class

volume;

RadioSet

private private

uint uint

ITunning,

IVolume

channel;

public channel volume

1;

void

void 0)

return

void

setLevel (uint

volumeLevel)

volume uint return

volume;

Глава 8. Интерфейсы

257


class static

void

args)

TvSet tv; tv new T v S e t IChannel IVolume

channelTv

RadioSet radio; radio new ITunning tuneRadio IVolume v o l R a d i o

канал

канал

if(tv

is

громкость

громкость

ITunning)

ITunning

tuneTv

else ITunning ITunning

tuneTvl tv null)

as

ITunning; ITunning

мы объявили interface

не

не

реализован")

IChannel, IVolume и ITunning:

IChannel

void uint

258

В.

Г. В

Язык

Самоучитель


interface

IVolume

void uint

interface

ITunning

И н т е р ф е й с I C h a n n e l п р е д н а з н а ч е н для и с п о л ь з о в а н и я в в и р т у а л ь н о м т е л е в и з о р е или л ю б о м д р у г о м п о д о б н о м у с т р о й с т в е . Он п о з в о л я е т п е р е к л ю ч и т ь у с т р о й с т в о на за¬ д а н н ы й н о м е р канала, а т а к ж е п о л у ч и т ь н о м е р т е к у щ е г о у с т а н о в л е н н о г о канала. С п о м о щ ь ю и н т е р ф е й с а I V o l u m e п р о г р а м м а м о ж е т у с т а н о в и т ь или о п р е д е л и т ь текущий уровень громкости устройства. И н т е р ф е й с I T u n n i n g т о ж е п о з в о л я е т п е р е к л ю ч а т ь каналы и о п р е д е л я т ь н о м е р т е к у щ е г о канала. В о т л и ч и е от и н т е р ф е й с а I C h a n n e l , о д н а к о , с п о м о щ ь ю д а н н о г о и н т е р ф е й с а н е в о з м о ж н о п е р е к л ю ч и т ь с я н а канал с з а д а н н ы м н о м е р о м . М е т о д ы n e x t и p r e v интерфейса I T u n n i n g дают возможность последовательно переключаться на с л е д у ю щ и й и п р е д ы д у щ и й канал. Как видите, данные интерфейсы можно с успехом использовать для управления л ю б ы м и у с т р о й с т в а м и , в к о т о р ы х есть в о з м о ж н о с т ь у с т а н о в к и к а н а л о в и г р о м к о с т и . В н а ш е й п р о г р а м м е мы создали классы для м о д е л и р о в а н и я д в у х т а к и х у с т р о й с т в телевизора и радиоприемника: class

IChannel,

private private

uint uint

IVolume

channel; volume;

public channel volume

class

1; 10;

RadioSet

private private

uint uint

ITunning,

IVolume

channel; volume;

public channel volume

1; 10;

Глава 8. Интерфейсы

259


Класс T v S e t

моделирует телевизор (фактически только возможность переключе­

ния каналов и р е г у л и р о в к и г р о м к о с т и ) , а класс R a d i o S e t

радиоприемник.

Оба эти класса р е а л и з у ю т и н т е р ф е й с I V o l u m e , п р е д н а з н а ч е н н ы й для р е г у л и р о в к и г р о м к о с т и . Что же касается п е р е к л ю ч е н и я к а н а л о в , то т е л е в и з о р м о ж н о п е р е к л ю ч а т ь на л ю б о й канал с з а д а н н ы м н о м е р о м п о с р е д с т в о м и н т е р ф е й с а I C h a n n e l , а прием¬ ник — только п е р е с т р а и в а т ь на с о с е д н и е каналы с п о м о щ ь ю и н т е р ф е й с а I T u n n i n g . Н и ж е м ы п р и в е л и и с х о д н ы й текст р е а л и з а ц и и и н т е р ф е й с а I C h a n n e l , предусмот¬ ренной в классе T v S e t : void channel

uint С return

channel;

Метод з а п и с ы в а е т н о в ы й н о м е р канала в поле класса c h a n n e l , а метод c u r r e n t в о з в р а щ а е т з н а ч е н и е , х р а н я щ е е с я в э т о м поле. Р е а л и з а ц и я и н т е р ф е й с а I T u n n i n g , и м е ю щ а я с я в классе R a d i o S e t , н е н а м н о г о сложнее: id channel+ void 0)

uint return М е т о д n e x t у в е л и ч и в а е т н о м е р к а н а л а на е д и н и ц у при к а ж д о м о б р а щ е н и и , а ме¬ тод p r e v у м е н ь ш а е т этот н о м е р , при у с л о в и и , что о н б о л ь ш е нуля. С п о м о щ ь ю метода c u r r e n t п р о г р а м м а м о ж е т о п р е д е л и т ь т е к у щ и й н о м е р канала. Что же касается и н т е р ф е й с а I V o l u m e , то он р е а л и з о в а н о д и н а к о в о и в классе T v S e t и в классе R a d i o S e t : void volume

260

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

Самоучитель


uint return

volume;

Метод

setLevel

позволяет

установить

c u r r e n t — определить

нужный

уровень

громкости,

а

метод

громкости.

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

метод

Main

нашей

программы

а т а к ж е два и н т е р ф е й с а переключать каналы, а второй

создает

объект-телевизор

tv

класса

первый из которых позволяет регулировать громкость:

TvSet tv; tv new T v S e t IChannel channelTv IVolume v o l T v Для и с п о л ь з о в а н и я и н т е р ф е й с о в м ы в ы з ы в а е м с о о т в е т с т в у ю щ и е м е т о д ы :

Здесь мы п е р е к л ю ч а е м т е л е в и з о р на

10-й канал и у с т а н а в л и в а е м у р о в е н ь г р о м к о ­

сти, р а в н ы й 5 0 А н а л о г и ч н ы м о б р а з о м создается о б ъ е к т - р а д и о п р и е м н и к и ссылки на и н т е р ф е й с ы для у п р а в л е н и я р а д и о п р и е м н и к о м : RadioSet radio; radio new ITunning tuneRadio IVolume v o l R a d i o Сразу

после

с о з д а н и я р а д и о п р и е м н и к будет п р и н и м а т ь

1-й канал

(это з а д а е т с я

с о о т в е т с т в у ю щ и м к о н с т р у к т о р о м ) . Н а ш а п р о г р а м м а п е р е к л ю ч а е т его н а с л е д у ю щ и й , 2-й к а н а л , а затем у с т а н а в л и в а е т у р о в е н ь г р о м к о с т и , р а в н ы й 30

П о с л е в ы п о л н е н и я всех этих д е й с т в и й н а ш а п р о г р а м м а в ы в о д и т на к о н с о л ь теку¬ щие номера к а н а л о в , а т а к ж е у р о в н и г р о м к о с т и т е л е в и з о р а и п р и е м н и к а : канал

канал

Глава 8. Интерфейсы

громкость

громкость

261


Для п о л у ч е н и я этих значений в ы з ы в а ю т с я м е т о д ы c u r r e n t с о о т в е т с т в у ю щ и х ин¬ терфейсов. А что будет, если мы п о п ы т а е м с я и с п о л ь з о в а т ь и н т е р ф е й с I T u n n i n g для управ¬ ления т е л е в и з о р о м ? Такая п о п ы т к а м о ж е т в ы г л я д е т ь с л е д у ю щ и м о б р а з о м : ITunning

tuneTv

Здесь мы п о п ы т а л и с ь п р е о б р а з о в а т ь с с ы л к у tv на о б ъ е к т класса T v S e t в тип ин¬ терфейса

ITunning,

чтобы

в дальнейшем

использовать

методы

этого

интерфейса

n e x t и p r e v для последовательного переключения каналов. Компилятор

не

найдет

в

этой

строке

никаких

I T u n n i n g был о п р е д е л е н в н а ш е й п р о г р а м м е . денной

ошибок,

так

как

интерфейс

О д н а к о п о п ы т к а и с п о л н е н и я приве¬

с т р о к и вызовет и с к л ю ч е н и е

Это

и с к л ю ч е н и е п о я в и т с я п о т о м у , что и н т е р ф е й с I T u n n i n g не был р е а л и з о в а н в классе TvSet.

Таким о б р а з о м , наш т е л е в и з о р «не умеет» п е р е к л ю ч а т ь каналы с п о м о щ ь ю

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

или a s .

i s п р и м е н я е т с я в н а ш е й п р о г р а м м е вместе с у с л о в н ы м о п е р а т о р о м

if

следующим образом: is

ITunning)

ITunning

tuneTv

else ITunning

не

Здесь мы в н а ч а л е п р о в е р я е м д о п у с т и м о с т ь п р е о б р а з о в а н и я и т о л ь к о затем выпол¬ няем это п р е о б р а з о в а н и е . Если ж е п р е о б р а з о в а н и е н е д о п у с т и м о , п р о г р а м м а в ы в о д и т предупреждающее сообщение на консоль. Д р у г о й с п о с о б основан н а п р и м е н е н и и о п е р а т о р а a s : ITunning

tuneTvl tv null)

as

ITunning; ITunning

не

В т о м с л у ч а е , когда п р е о б р а з о в а н и е н е д о п у с т и м о , этот о п е р а т о р вернет з н а ч е н и е null.

Программе

остается

только

проверить

результат

выполнения

операции

as,

предприняв соответствующие действия. З а м е т и м , что в т о р о й с п о с о б п р е о б р а з о в а н и я э ф ф е к т и в н е е , так как он и с к л ю ч а е т н е о б х о д и м о с т ь д в о й н о й п р о в е р к и д о п у с т и м о с т и п р е о б р а з о в а н и я , н е и з б е ж н о й в случае и с п о л ь з о в а н и я о п е р а т о р а is.

262

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

Самоучитель


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

8.3.

Файл

using System; n a m e s p a c e Combine interface

IChannel

void

interface

IVolume

void

interface

ITunning

interface

ITvControl

IChannel,

IVolume

on void interface

class

IRadioControl

TvSet

private private private

ITunning,

IVolume

ITvControl uint uint bool

Глава 8. Интерфейсы

volume;

263


public channel volume power

1; 10; false;

void power

true;

void power

false;

void channel int return

channel;

void

setLevel (uint

volume

volumeLevel)

volumeLevel;

int return

class

volume;

RadioSet

private private public

uint uint

IRadioControl channel;

RadioSet

channel volume

1;

void channel

264

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

Самоучитель


void 0)

int return

channel;

void

(uint

volume

volumeLevel)

volumeLevel;

uint

1

return

volume;

class static

void

args)

TvSet tv; tv new T v S e t ITvControl if

tv

as

ITvControl;

null)

else реализован

интерфейс

RadioSet radio; radio new IRadioControl if

Глава 8. Интерфейсы

radioControl null)

radio

as

265


else реализован

интерфейс

канал

канал

Console

громкость

ReadLine

З д е с ь , так же как IChannel, IVolume и г р о м к о с т ь ю звука: interface

громкость

и и

в п р е д ы д у щ е й п р о г р а м м е , мы о п р е д е л и л и и н т е р ф е й с ы I T u n n i n g , предназначенные для управления каналами

IChannel

void uint

interface

IVolume

void uint

interface

ITunning

uint Н е к о т о р ы е из этих и н т е р ф е й с о в ( I C h a n n e l и I V o l u m e ) п р и м е н я ю т с я для у п р а в ­ ления телевизором, а некоторые ( I T u n n i n g и д л я у п р а в л е н и я радио¬ приемником. С целью у п р о щ е н и я программы мы попарно скомбинировали интерфейсы, образо­ вав ITvControl и IRadioControl: interface

ITvControl

IVolume

on оf

266

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

Фролов. Язык

Самоучитель


interface

IRadioControl

ITunning,

IVolume

П е р в ы й и з этих и н т е р ф е й с о в с и м е н е м I T v C o n t r o l о б ъ е д и н я е т и н т е р ф е й с ы I C h a n n e l и I V o l u m e , д о б а в л я я к ним еще д в а метода (on и o f f ) , п о з в о л я ю щ и е в к л ю ч а т ь и в ы к л ю ч а т ь т е л е в и з о р . Таким о б р а з о м , этот к о м б и н и р о в а н н ы й и н т е р ф е й с п р е д н а з н а ч е н для у п р а в л е н и я т е л е в и з о р о м . К о м б и н и р о в а н н ы й и н т е р ф е й с I R a d i o C o n t r o l п р е д с т а в л я е т собой ч и с т у ю ком­ б и н а ц и ю и н т е р ф е й с о в I T u n n i n g и I V o l u m e без к а к и х - л и б о д о б а в л е н и й . О н создан для у п р а в л е н и я р а д и о п р и е м н и к о м . Теперь при о б ъ я в л е н и и класса T v S e t нам д о с т а т о ч н о у к а з а т ь , что о н р е а л и з у е т к о м б и н и р о в а н н ы й и н т е р ф е й с I T v C o n t r o l , н е п е р е ч и с л я я с о с т а в л я ю щ и е его и н т е р фсйсы I C h a n n e l и I V o l u m e : c l a s s TvSet private private private

ITvControl uint uint bool

volume; power;

public channel volume power

1; 10; false;

void power

true;

void power

Так как комбинированный и н т е р ф е й с I T v C o n t r o l включает в себя м е т о д ы on и of f, нам н е о б х о д и м о реализовать их в классе Для хранения текущего с о ­ стояния телевизора (включен или выключен) мы предусмотрели в э т о м классе поле Аналогично при объявлении класса R a d i o S e t мы указали, что он р е а л и з у е т к о м ­ бинированный интерфейс class

RadioSet

private private

uint uint

IRadioControl

volume;

Глава 8. Интерфейсы

267


public channel volume

10;

Так как в р а м к а х этого и н т е р ф е й с а нет н и к а к и х д о п о л н и т е л ь н ы х м е т о д о в , отсутст¬ в у ю щ и х в с о с т а в л я ю щ и х его и н т е р ф е й с а х , при о б ъ я в л е н и и класса R a d i o S e t нам не пришлось объявлять дополнительные методы. Теперь мы р а с с к а ж е м о т о м , как п о л ь з о в а т ь с я к о м б и н и р о в а н н ы м и и н т е р ф е й с а м и . С о з д а в о б ъ е к т класса T v S e t , м ы о б ъ я в л я е м п е р е м е н н у ю t v C o n t r o l , предназна¬ ч е н н у ю для х р а н е н и я ссылки н а и н т е р ф е й с I T v C o n t r o l : TvSet tv; tv new T v S e t ITvControl tvControl

tv

as

ITvControl;

Эта переменная инициализируется безопасным способом с применением оператора a s . Если с с ы л к а н а и н т е р ф е й с I T v C o n t r o l п о л у ч е н а у с п е ш н о , м ы и с п о л ь з у е м для вызова м е т о д о в к о м б и н и р о в а н н о г о и н т е р ф е й с а : null)

else реализован

интерфейс

О б р а т и т е в н и м а н и е , что с п о м о щ ь ю е д и н с т в е н н о й ссылки на к о м б и н и р о в а н н ы й интерфейс I T v C o n t r o l м ы в ы з ы в а е м м е т о д ы в х о д я щ и х в него и н т е р ф е й с о в I C h a n n e l и I V o l u m e . Так как все эти м е т о д ы н а з ы в а ю т с я п о - р а з н о м у , в д а н н о м случае не в о з н и к а ю т к о н ф л и к т ы м е ж д у о д и н а к о в ы м и и м е н а м и м е т о д о в , щих разным интерфейсам. Аналогичным образом создается и используется п р е д н а з н а ч е н н ы й для у п р а в л е н и я р а д и о п р и е м н и к о м :

интерфейс

IRadioControl,

RadioSet radio new IRadioControl if

radioControl null)

radio

as

else реализован

268

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


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

случай, связанный

с вызовом

метода

c u r r e n t . Этот метод, возвращающий текущее значение номера канала или уровня гром¬ кости, определен во всех базовых интерфейсах, и м е ю щ и х с я в нашей программе. П о п ы т к и о б р а щ е н и я к этому методу ч е р е з ссылки current()

и

на и н т е р ф е й с ы вида

current

tvCon-

к п о я в л е н и ю н а этапе

к о м п и л я ц и и с о о б щ е н и я об о ш и б к е . Д е л о в т о м , что в такой записи не я с н о , о какой и м е н н о р е а л и з а ц и и метода c u r r e n t идет р е ч ь . В самом д е л е , к о м б и н и р о в а н н ы й и н т е р ф е й с I T v C o n t r o l состоит и з и н т е р ф е й с о в IChannel и

В к а ж д о м и з них имеется свой метод

комбинированный

интерфейс

состоящий

Аналогично

из интерфейсов

I

n i n g и I V o l u m e , тоже с о д е р ж и т в себе д в е р а з л и ч н ы е р е а л и з а ц и и м е т о д а c u r r e n t . Для того ч т о б ы и з б е ж а т ь н е о д н о з н а ч н о с т и , н е о б х о д и м о п р и в о д и т ь ссылку на ком¬ б и н и р о в а н н ы й и н т е р ф е й с к типу того и н т е р ф е й с а , для к о т о р о г о н у ж н о в ы з в а т ь наш метод: канал

громкость

Здесь м ы в н а ч а л е в ы з ы в а е м м е т о д c u r r e n t и н т е р ф е й с а I C h a n n e l ( п о л у ч а я при этом т е к у щ и й н о м е р к а н а л а ) , а затем этот же м е т о д , но д л я и н т е р ф е й с а

IVolume

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

эта о п е р а ц и я

выполняется

и д л я интерфейса

C o n t r o l , управляющего радиоприемником: канал

громкость

Для того ч т о б ы и з б е ж а т ь в о з н и к н о в е н и я и с к л ю ч е н и й , для п р е о б р а з о в а н и я т и п о в вы можете воспользоваться описанными выше операторами is и a s .

Интерфейсы и наследование классов Если базовый класс р е а л и з у е т к а к и е - л и б о и н т е р ф е й с ы , то они н а с л е д у ю т с я производ¬ ными классами.

При необходимости производный

класс м о ж е т п е р е о п р е д е л и т ь все

или н е к о т о р ы е м е т о д ы и н т е р ф е й с о в б а з о в о г о класса. Р а с с м о т р и м простой п р и м е р . Пусть у нас есть базовый класс T v S e t , о п и с ы в а ю щ и й т е л е в и з о р . В этом классе мы п р е д у с м о т р е л и средства п е р е к л ю ч е н и я к а н а л о в и р е г у л и р о в к и г р о м к о с т и , реали¬ зованные с п о м о щ ь ю интерфейсов. Теперь

на

базе

класса

TvSet

нам

бы

хотелось

создать

новый

класс

в к о т о р о м ч е т н ы е к а н а л ы были бы р у с с к и м и , а лийскими. Глава 8. Интерфейсы

269


Исходный текст программы, объявляющей и использующей эти классы, мы приве­ ли в листинге 8.4. Листинг

8.4.

Файл

using System; namespace interface

IChannel

void uint

interface void uint

class

IVolume

current

TvSet

protected protected

IChannel,

IVolume

uint uint

public channel volume

1; -

void channel

int return

void

channel;

IVolume

setLevel (uint

volumeLevel)

volume

uint return

270

volume;

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


interface string class

TvSet,

private public

IChannel,

IVolume,

ILanguage

string

void channel 0) else "english";

string return

class static

void

args)

InternationalTvSet tv new

tv;

IChannel tv as I C h a n n e l ; IVolume tvVolume tv as ILanguage tvLanguage tv as I L a n g u a g e ; if ( t v C h a n n e l null tvLanguage

"Телевизор:

Интерфейсы

tvVolume

null

null)

канал

громкость

271


"Телевизор:

канал

громкость

else реализован IVolume

интерфейс

IChannel,

или

Для п е р е к л ю ч е н и я к а н а л о в и р е г у л и р о в к и г р о м к о с т и мы о п р е д е л и л и у ж е з н а к о м ы е вам и н т е р ф е й с ы I C h a n n e l и I V o l u m e : interface void uint

IChannel

switchTo (uint current

interface

channelNumber)

IVolume

void uint Эти и н т е р ф е й с ы р е а л и з о в а н ы в б а з о в о м классе T v S e t а н а л о г и ч н о т о м у , как это б ы л о сделано в п р е д ы д у щ е й п р о г р а м м е (см. л и с т и н г 8.3). Что ж е касается п р о и з в о д н о г о класса еще один и н т е р ф е й с

ILanguage,

то он реализует

п о з в о л я ю щ и й о п р е д е л и т ь н а ц и о н а л ь н ы й я з ы к те¬

к у щ е г о канала: Interface string При

ILanguage current

объявлении

производного

класса

вый класс T v S e t , а т а к ж е

InternationalTvSet

м ы у к а з а л и базо­

все и н т е р ф е й с ы , р е а л и з у е м ы е в р а м к а х базо¬

вого и п р о и з в о д н о г о класса: InternationalTvSet private

272

TvSet,

IChannel,

IVolume,

ILanguage

string

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


public

void channel 0) else

string return

Конструктор класса I n t e r n a t i o n a l T v S e t устанавливает ПО у м о л ч а н и ю р у с ­ ский язык,

инициализируя

о б р а з о м поле i n t e r f a c e L a n g u a g e .

В дальнейшем с о д е р ж и м о е э т о г о поля б у д е т зависеть от н о м е р а телевизором. В интерфейсе содержимого

поля

р е а л и з о в а н н о м базовым классом, нет средств и з м е н е н и я interf

aceLanguage,

классе переопределить м е т о д Новая

реализация

п о э т о м у нам придется в

производном

switchTo

этого

метода,

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

в

производном

классе

I n t e r n a t i o n a l T v S e t , скрывает реализацию э т о г о ж е метода базовым классом. Дополнительно класс

I n t e r n a t i o n a l T v S e t р е а л и з у е т м е т о д c u r r e n t интер¬

фейса I L a n g u a g e , предназначенный для определения текущего языка. Теперь мы расскажем о т о м , как м е т о д M a i n нашей программы и с п о л ь з у е т о п и ­ санные выше Прежде

всего

этот

метод

создает

объект

производного

класса

Interna­

t i o n a l T v S e t и получает ссылки н а в с е н е о б х о д и м ы е интерфейсы: InternationalTvSet tv new

tv;

IChannel tvChannel tv as IVolume tvVolume tv as ILanguage tvLanguage tv as

ILanguage;

Если в с е э т и ссылки получены у с п е ш н о , программа продолжает р а б о т у , а в п р о ­ тивном случае выводит на консоль с о о б щ е н и е об if ( t v C h a n n e l

null

8. Интерфейсы

tvVolume

null

tvLanguage

null)

273


else реализован

интерфейс

IChannel,

IVolume

или

В том с л у ч а е , если ссылки на все и н т е р ф е й с ы п о л у ч е н ы у с п е ш н о , п р о г р а м м а уста­ навливает

г р о м к о с т и 50

п е р е к л ю ч а е т т е л е в и з о р на 10-й к а н а л , а затем ото­

бражает на к о н с о л и его с о с т о я н и е :

канал

громкость

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

громкость

О б р а т и т е в н и м а н и е , что здесь мы и с п о л ь з у е м без каких бы то ни было и з м е н е н и й и н т е р ф е й с I V o l u m e , р е а л и з о в а н н ы й в б а з о в о м классе T v S e t . Обращаясь к интерфейсу

I C h a n n e l , н а ш а п р о г р а м м а и м е е т д е л о с методами это­

го и н т е р ф е й с а , р е а л и з о в а н н ы м и как в б а з о в о м , так и в п р о и з в о д н о м к л а с с е . Н а п р и м е р , метод c u r r e n t р е а л и з о в а н в базовом классе и не и з м е н я е т с я в п р о и з в о д н о м к л а с с е , а метод s w i t c h T o , н а п р о т и в , п е р е о п р е д е л е н в д о ч е р н е м классе. И н а к о н е ц , наша п р о г р а м м а и с п о л ь з у е т метод и н т е р ф е й с а I L a n g u a g e , реализо¬ ванный

в п р о и з в о д н о м классе

I n t e r n a t i o n a l T v S e t и никак не у п о м и н а ю щ и й с я

в б а з о в о м классе T v S e t . Таким о б р а з о м , п р о и з в о д н ы е классы м о г у т н а с л е д о в а т ь не т о л ь к о м е т о д ы , но и ин¬ т е р ф е й с ы б а з о в о г о класса, при н е о б х о д и м о с т и п е р е о п р е д е л я я их.

Свойства в интерфейсах В п р е д ы д у щ и х п р и м е р а х п р о г р а м м мы с о з д а в а л и и н т е р ф е й с ы , с о д е р ж а щ и е и с к л ю ч и ­ т е л ь н о м е т о д ы . М е ж д у тем в р а м к а х и н т е р ф е й с о в С# д о п у с к а е т с я о б ъ я в л я т ь свойства, индексаторы и события. В этом р а з д е л е мы р а с с м о т р и м п р и м е р п р о г р а м м ы у п р а в л е н и я т е л е в и з о р о м , функ¬ ц и о н и р о в а н и е к о т о р о й о с н о в а н о на и с п о л ь з о в а н и и с в о й с т в , о б ъ я в л е н н ы х в рамках ин¬ т е р ф е й с а I T v C o n t r o l ( л и с т и н г 8.5). Листинг

8.5.

Файл

using System; namespace Properties interface

274

ITvControl

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

Самоучитель


bool

PowerOn

byte

MaxChannel

get

byte

Channel

byte

Volume

class

TelevisionSet

private private private private

bool byte byte byte

ITvControl

isPowerOn; maxChannel;

public isPowerOn maxChannel

false;

currentVolume

10;

bool get return

isPowerOn;

set isPowerOn

Глава

Интерфейсы

275


byte get

byte get return J set if ( v a l u e

maxChannel value 0) currentChannel value;

byte get return set 0

value currentVolume

100) value;

else currentVolume

class

0;

PropertiesApp

static

void

args)

TelevisionSet TelevisionSet tvSmall tvLarge ITvControl ITvControl

tvSmall; tvLarge;

new T e l e v i s i o n S e t new tvsControl tvlControl

if ( t v s C o n t r o l

null

tvSmall tvLarge

as as

tvlControl реализован

ITvControl; ITvControl; null) интерфейс

ITvControl")

return;

276

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


true; 5 50; true; 27; 30; "Телевизор PowerOn

tvSmall: канал "Включен"

из

громкость "Выключен",

из

громкость "Выключен",

tvLarge: канал "Включен"

Р а н е е в этой главе мы у ж е п р о в о д и л и а н а л о г и ю м е ж д у и н т е р ф е й с а м и С# и абст­ р а к т н ы м и к л а с с а м и . В гл. 6 мы р а с с к а з ы в а л и про а б с т р а к т н ы е и н т е р ф е й с ы , о б ъ я в л е ­ ние к о т о р ы х н е с о д е р ж и т тела ф у н к ц и й д о с т у п а и set. А н а л о г и ч н ы м о б р а з о м о б ъ я в л я ю т с я с в о й с т в а в р а м к а х и н т е р ф е й с а . Н и ж е мы п р и ­ вели и с х о д н ы й текст и н т е р ф е й с а I T v C o n t r o l , с о д е р ж а щ е г о о б ъ я в л е н и я с в о й с т в , с помощью которых можно управлять нашим виртуальным телевизором: interface bool

ITvControl PowerOn

get;

byte

MaxChannel

byte

Channel

Глава 8. Интерфейсы

277


byte

Volume

Свойство P o w e r O n п о з в о л я е т в к л ю ч а т ь или в ы к л ю ч а т ь т е л е в и з о р п о с р е д с т в о м за­ писи в него з н а ч е н и й t r u e или

s

Это же с в о й с т в о м о ж е т быть

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

вклю¬

ч е н н о е или в ы к л ю ч е н н о е . С п о м о щ ь ю с в о й с т в а M a x C h a n n e l м о ж н о о п р е д е л и т ь м а к с и м а л ь н ы й номер кана¬ ла, п р и н и м а е м о г о т е л е в и з о р о м .

Это с в о й с т в о д о с т у п н о т о л ь к о для ч т е н и я , так как

в нем п р е д у с м о т р е н а только одна ф у н к ц и я д о с т у п а g e t . Свойство

Channel

позволяет

т е л е в и з о р н а л ю б о й з а д а н н ы й канал,

а также о п р е д е л я т ь н о м е р т е к у щ е г о канала. И наконец, п о с р е д с т в о м свойства V o l u m e м о ж н о р е г у л и р о в а т ь у р о в е н ь г р о м к о с т и , а т а к ж е о п р е д е л я т ь т е к у щ и й уровень г р о м к о с т и . При о б ъ я в л е н и и класса T e l e v i s i o n S e t м ы у к а з а л и , что о н р е а л и з у е т только что описанный

интерфейс

class

ITvControl: ITvControl

private private private private

bool byte byte byte

isPowerOn; maxChannel;

public

currentVolume

О б р а т и т е в н и м а н и е , что при р е а л и з а ц и и и н т е р ф е й с а мы не у к а з ы в а е м модифика¬ тор д о с т у п а , а имя с в о й с т в а задаем вместе с и м е н е м и н т е р ф е й с а : byte get return

278

А В

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

Самоучитель


if ( v a l u e

maxChannel value value;

0)

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

Индексаторы в интерфейсах Если класс создан для п р е д с т а в л е н и я о б ъ е к т а с м а с с и в о м , для д о с т у п а к нему м о ж н о и с п о л ь з о в а т ь и н д е к с а т о р ы . Об этом мы р а с с к а з ы в а л и вам в п р е д ы д у щ е й г л а в е . К а к мы уже

говорили,

индексатор

м о ж е т быть

включен

в

о б ъ я в л е н и е и н т е р ф е й с а наряду

с методами и другими членами. В л и с т и н г е 8.6 мы п р и в е л и м о д и ф и ц и р о в а н н ы й в а р и а н т п р о г р а м м ы , р а с с м о т р е н ­ ной нами р а н е е в разделе « И н д е к с а т о р ы » п р е д ы д у щ е й г л а в ы . Эта п р о г р а м м а исполь¬ зует к л а с с , х р а н я щ и й н а з в а н и я т е л е в и з и о н н ы х к а н а л о в в м а с с и в е . 8.6.

Файл

using System; namespace interface uint

IChannelNames Size

string

class

this[uint

ChannelNames

private private public

uint

index]

IChannelNames

ArraySize;

ChannelNames ( u i n t

Channels ArraySize

Глава 8. Интерфейсы

Count)

new Count;

279


uint get return

ArraySize;

string

index]

get 0 return

index

else return

"Канал

set 0

index value;

class

IndexerApp

static

void

args)

C h a n n e l N a m e s ch IChannelNames

new

ChannelNames ch as

null) реализован

интерфейс

"Мир "Боевик"; "Наше к и н о " ; i

0;

i

i++)

s

280

А. В. Фролов,

В.


Для терфейс

д о с т у п а к э л е м е н т а м массива названий каналов мы о б ъ я в и л и ин­

interface uint

IChannelNames Size

string

index]

Этот и н т е р ф е й с в к л ю ч а е т в себя с в о й с т в о S i z e , а т а к ж е и н д е к с а т о р . Свойство S i z e позволяет узнать текущий размер массива, созданного конструкто­ р о м , п о э т о м у оно и м е е т т о л ь к о одну п р о ц е д у р у д о с т у п а g e t . Ч т о ж е касается и н д е к ­ сатора, т о м ы о б ъ я в и л и д л я него обе п р о ц е д у р ы и set. Таким о б р а з о м , через и н т е р ф е й с I C h a n n e l N a m e s п р о г р а м м а с м о ж е т о п р е д е л и т ь размер м а с с и в а , а т а к ж е п о л у ч и т ь или и з м е н и т ь с о д е р ж и м о е его о т д е л ь н ы х я ч е е к . П р и о б ъ я в л е н и и класса C h a n n e l N a m e s м ы у к а з а л и , что о н р е а л и з у е т и н т е р ф е й с IChannelNames: class

ChannelNames

private private public

IChannelNames

string[] uint

Channels;

ChannelNames ( u i n t

Channels ArraySize

Count)

new s t r i n g [ C o u n t ] Count;

К о н с т р у к т о р этого класса о б е с п е ч и в а е т с о з д а н и е м а с с и в а н е о б х о д и м ы х р а з м е р о в , а т а к ж е и н и ц и а л и з а ц и ю поля х р а н я щ е г о размер массива. Реализация свойства S i z e не имеет никаких особенностей: uint get return

ArraySize;

Здесь и с п о л ь з у е т с я п о л н о е имя с в о й с т в а , в к л ю ч а ю щ е е имя и н т е р ф е й с а ; модифика¬ торы д о с т у п а не у к а з ы в а ю т с я . 8. Интерфейсы

281


Что же касается и н д е к с а т о р а , то он р е а л и з о в а н с л е д у ю щ и м о б р а з о м : string

index]

get 0 return else return

"Канал

index

недоступен";

set 0

index value;

Мы д о б а в и л и к и м е н и и н д е к с а т о р а н а з в а н и е и н т е р ф е й с а , в р е з у л ь т а т е чего получи¬ ли полное имя I C h a n n e l N a m e s В о с т а л ь н о м этот и н д е к с а т о р п о л н о с т ь ю ана­ л о г и ч е н и н д е к с а т о р у , о п и с а н н о м у в п р е д ы д у щ е й главе (см. л и с т и н г 7.7). Теперь мы р а с с к а ж е м о т о м , как п о л ь з о в а т ь с я с о з д а н н ы м нами и н т е р ф е й с о м . В н а ч а л е н е о б х о д и м о создать о б ъ е к т ( м а с с и в названий к а н а л о в ) , а т а к ж е ссылку на и н т е р ф е й с : C h a n n e l N a m e s ch new I C h a n n e l N a m e s chNames

ch

as

С о з д а н н а я с с ы л к а затем п р о в е р я е т с я н а р а в е н с т в о з н а ч е н и ю n u l l : if (chNames

null) реализован

интерфейс

Если с с ы л к а на и н т е р ф е й с была п о л у ч е н а у с п е ш н о , мы и н и ц и а л и з и р у е м 5 ячеек массива, используя ссылку на интерфейс I C h a n n e l N a m e s : "Мир "Боевик"; "Наше к и н о " ; Как в и д и т е , а д р е с а ц и я я ч е е к м а с с и в а через и н д е к с а т о р , о б ъ я в л е н н ы й в рамках ин¬ терфейса, выполняется очевидным и ожидаемым образом. Чтение ячеек массива происходит аналогично: i string

i

i++)

s

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

282

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


Глава 9. Обработка исключений О б щ е и з в е с т н о м н е н и е о т о м , что ч е л о в е к м о ж е т о ш и б а т ь с я , а к о м п ь ю т е р ы нет. На­ в е р н о е , только п р о ф е с с и о н а л ь н ы е п р о г р а м м и с т ы и администраторы знают, н а с к о л ь к о вторая часть этого не с о о т в е т с т в у е т д е й с т в и т е л ь н о с т и . К а к о в а же п р и ч и н а п о я в л е н и я к о м п ь ю т е р н ы х о ш и б о к и м о ж н о ли п р е д о т в р а т и т ь их п о я в л е н и е ? На наш взгляд, б о л ь ш и н с т в о таких о ш и б о к связано с тем, что к о м п ь ю т е р ы и про¬ г р а м м ы для них создаются л ю д ь м и . С о в р е м е н н ы е к о м п ь ю т е р ы и п р о г р а м м ы чрезвы¬ чайно с л о ж н ы , и ч е л о в е к просто не в с о с т о я н и и у д е р ж а т ь в голове все д е т а л и и осо¬ бенности их р а б о т ы . Проблемы начинаются уже в центральном процессоре, содержащем миллионы к о м п о н е н т о в . Когда п о я в и л а с ь первая о ш и б к а в п р о ц е с с о р е P e n t i u m к о м п а н и и Intel, это в ы з в а л о ш о к о в у ю р е а к ц и ю у многих п о л ь з о в а т е л е й к о м п ь ю т е р о в , после чего на¬ чалась м а с с о в а я з а м е н а д е ф е к т н ы х п р о ц е с с о р о в . Мы тоже в свое время были с ч а с т л и ­ выми о б л а д а т е л я м и д е ф е к т н о г о п р о ц е с с о р а P e n t i u m 90 и, п о л ь з у я с ь с л у ч а е м , д а ж е на¬ писали пару статей на эту тему. С о в р е м е н н ы е п р о ц е с с о р ы д о п у с к а ю т и с п р а в л е н и е не¬ к о т о р ы х о ш и б о к п р о г р а м м н ы м с п о с о б о м , для чего в к о м п а н и и Intel была р а з р а б о т а н а специальная технология. Н а д о сказать, что о ш и б к и в п р о ц е с с о р а х п о я в л я ю т с я вовсе не п о т о м у , что разра¬ ботчик у д е л я е т н е д о с т а т о ч н о в н и м а н и я т е с т и р о в а н и ю н о в ы х м о д е л е й , стремясь вы¬ пустить их на р ы н о к как м о ж н о быстрее (хотя какая-то д о л я п р а в д ы тут есть). На тес¬ т и р о в а н и е и п о и с к о ш и б о к т р а т я т с я о г р о м н ы е л ю д с к и е и ф и н а н с о в ы е р е с у р с ы , однако из-за н е в о о б р а з и м о й с л о ж н о с т и п р о ц е с с о р о в н е р е а л ь н о провести полное т е с т и р о в а н и е всех его к о м п о н е н т о в за р а з у м н о е в р е м я . С о в р е м е н н ы е ОС и с л о ж н ы е п р о г р а м м ы т а к ж е и з о б и л у ю т о ш и б к а м и , н е к о т о р ы е из к о т о р ы х м е ш а ю т п о л ь з о в а т е л я м , а н е к о т о р ы е так и о с т а ю т с я н е з а м е ч е н н ы м и . Не з а т и х а ю т с п о р ы о т о м , какая ОС р а б о т а е т н а д е ж н е е Microsoft W i n d o w s или L i n u x . Мы п о л а г а е м , что все с у щ е с т в у ю щ и е версии этих и д р у г и х ОС н е н а д е ж н ы по п р и ч и н е п р и с у т с т в и я в них о ш и б о к . В з а в и с и м о с т и от с и т у а ц и и ( к о н ф и г у р а ц и и а п п а р а т н о г о о б е с п е ч е н и я , д р а й в е р о в , н а б о р а у с т а н о в л е н н ы х п р о г р а м м и т. п.) эта н е н а д е ж н о с т ь м о ж е т с к а з ы в а т ь с я в б о л ь ш е й или м е н ь ш е й степени. Е щ е одна важная п р и ч и н а в о з н и к н о в е н и я о ш и б о к при р а б о т е п р о г р а м м н о г о о б е с ­ печения отказы а п п а р а т у р ы к о м п ь ю т е р а . Н и ч т о не вечно под Л у н о й , и к о м п ь ю т е р тоже не я в л я е т с я и с к л ю ч е н и е м . И з н о с у п о д в е р ж е н ы в п е р в у ю о ч е р е д ь такие компо¬ н е н т ы , как н а к о п и т е л и д а н н ы х на д и с к а х , х о т я о т к а з ы в а ю т и д р у г и е к о м п о н е н т ы . П о с л е д с т в и я к о м п ь ю т е р н ы х о ш и б о к м о г у т быть очень т я ж е л ы м и . В р е з у л ь т а т е их п о я в л е н и я могут пропасть очень в а ж н ы е с т о и м о с т ь к о т о р ы х п о р о й много¬ кратно п р е в ы ш а е т с т о и м о с т ь всей к о м п ь ю т е р н о й с и с т е м ы . И с к а ж е н и е и н ф о р м а ц и и о пациентах в м е д и ц и н с к о й базе д а н н ы х м о ж е т д а ж е п р и в е с т и к т р а г и ч е с к о м у исходу. Как же з а с т р а х о в а т ь себя от п о я в л е н и я о ш и б о к или по крайней сократить у щ е р б от их в о з н и к н о в е н и я ?

283


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

Классические способы обработки ошибок О т в л е к а я с ь о т ф и л о с о ф с к и х п р и ч и н в о з н и к н о в е н и я к о м п ь ю т е р н ы х о ш и б о к , рассмот¬ р и м с и т у а ц и и , в к о т о р ы х такие о ш и б к и о б ы ч н о в о з н и к а ю т при с о з д а н и и п р о г р а м м . П р о с т е й ш и е п р и м е р ы — д е л е н и е на нуль и в ы х о д за г р а н и ц ы массива. Рассмотрим программу, исходный текст которой приведен в листинге Листинг

9.1.

Файл

namespace class

DivideByZeroApp

static

void

int

i

args) 0;

intj

Ошибка

деления

на

Мы о б ъ я в и л и в ней две п е р е м е н н ы е т и п а i n t с и м е н а м и i и j. П е р е м е н н о й i при¬ с в а и в а е т с я н у л е в о е з н а ч е н и е , после чего п р е д п р и н и м а е т с я п о п ы т к а р а з д е л и т ь число 5 н а с о д е р ж и м о е i , записав р е з у л ь т а т в п е р е м е н н у ю j . К о м п и л я т о р не с л е д и т за с о д е р ж и м ы м п е р е м е н н ы х , к о т о р о е они п о л у ч а т в процес¬ се в ы п о л н е н и я п р о г р а м м ы , п о э т о м у он не с м о ж е т п р е д у г а д а т ь , что будет п р е д п р и н я т а п о п ы т к а д е л е н и я числа 5 на 0. В р е з у л ь т а т е к о м п и л я ц и я з а к о н ч и т с я у с п е ш н о . О д н а к о при п о п ы т к е з а п у с т и т ь п р о г р а м м у на экране п о я в и т с я с л е д у ю щ е е сообще¬ ние об о ш и б к е : An unhandled occurred in Additional 284

exception

information:

of

type

Attempted

to

divide

by

zero.

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


В этом произошло программе никло при

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

программы DivideByZero.exe для к о т о р о г о в н а ш е й т а к ж е , что и с к л ю ч е н и е воз¬

Н а п о м н и м , что п р о г р а м м ы С# в ы п о л н я ю т с я под у п р а в л е н и е м с и с т е м ы Microsoft F r a m e w o r k , к о н т р о л и р у ю щ е й п о я в л е н и е о ш и б о к п о д о б н о г о рода. Если в прог¬ р а м м е н е п р е д у с м о т р е т ь о б р а б о т к у о ш и б о ч н о й с и т у а ц и и , т о при п о я в л е н и и о ш и б к и система Microsoft F r a m e w o r k просто з а в е р ш и т ее работу с выдачей на экран со¬ ответствующего сообщения. А н а л о г и ч н ы м о б р а з о м ведут себя п р о г р а м м ы , с о с т а в л е н н ы е на д р у г и х я з ы к а х про¬ г р а м м и р о в а н и я . П р и этом о т в е т с т в е н н о с т ь з а а в а р и й н о е п р е к р а щ е н и е р а б о т ы с б о й н о й п р о г р а м м ы л е ж и т на О С . Если она не с п р а в и т с я с этой задачей д о л ж н ы м о б р а з о м , мо¬ гут в о з н и к н у т ь с е р ь е з н ы е п р о б л е м ы , вплоть до «зависания» к о м п ь ю т е р а и п о т е р и данных.

Предварительная проверка параметров Ваша программа не должна выдавать пользователю системное сообщение об ошибке, аналогичное приведенному выше. Обнаружив ошибку, программа должна сообщить пользователю причины возникновения ошибки, а также рекомендовать какие-либо д е й с т в и я . В с е это н е в о з м о ж н о без создания в п р о г р а м м е с о б с т в е н н о й с и с т е м ы обра¬ ботки о ш и б о к . Н и ж е м ы п р е д с т а в и л и п р о с т е й ш и й способ п р е д о т в р а щ е н и я о ш и б к и д е л е н и я на нуль ( з а м е т ь т е : именно п р е д о т в р а щ е н и я , а не о б р а б о т к и ) : if(i

0)

int j

5

i;

else деления

на

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

Проверка кодов возврата функций и методов Р а с с м о т р и м теперь д р у г о й случай ( л и с т и н г 9.2). Здесь мы в ы з ы в а е м в ц и к л е м е т о д D i v i d e , в ы п о л н я ю щ и й д е л е н и е п е р в о г о своего п а р а м е т р а н а в т о р о й . В к а ч е с т в е де­ лителя и с п о л ь з у е т с я п е р е м е н н а я цикла i , к о т о р а я в о время р а б о т ы ц и к л а п р и н и м а е т нулевое

значение.

Поэтому

при

работе

программы

происходит

ошибка

деления

на нуль. 9. Обработка

285


Листинг

9.2.

Файл

using System; namespace class

DivideByZerolApp

static

int

Divide(int x,

int

y)

int result; result x y; return result;

static

void

int j for(int j

i

args)

-3;

i

3;

Divide

i+ + ) Ошибка д е л е н и я

на

К а к и с к л ю ч и т ь п о я в л е н и е этой о ш и б к и ? М ы могли б ы н е м н о г о и з м е н и т ь м е т о д

D i v i d e , ч т о б ы о н п р о в е р я л з н а ч е н и е де¬

л и т е л я перед в ы п о л н е н и е м о п е р а ц и и д е л е н и я : int

int

Divide(int х,

int

у)

result;

if(y result return

0) x result;

else return Т е п е р ь о ш и б к а д е л е н и я на нуль не в о з н и к н е т , так как, если д е л и т е л ь равен н у л ю , д е л е н и е н е в ы п о л н я е т с я . О д н а к о м е т о д D i v i d e д о л ж е н к а к и м - т о о б р а з о м просигна¬ лизировать вызывающему методу о возникновении ошибки. Н а ш а р е а л и з а ц и я этого м е т о д а при о ш и б к е в о з в р а щ а е т н у л е в о е з н а ч е н и е , о д н а к о о ч е в и д н о , это не л у ч ш и й с п о с о б . В с а м о м д е л е , н у л е в о е з н а ч е н и е в о з в р а щ а е т с я и в том с л у ч а е , если д е л и т е л ь равен н у л ю , а эта с и т у а ц и я не я в л я е т с я о ш и б о ч н о й . З а м е т и м , что о с о б о г о з н а ч е н и я м е т о д о м или ф у н к ц и е й и с п о л ь з у е т с я очень часто в д р у г и х я з ы к а х п р о г р а м м и р о в а н и я в качестве п р и з н а к а о ш и б к и . Естест286

Г.

Фролов.

Самоучитель


венный п р о г р а м м н ы й и н т е р ф е й с (Application P r o g r a m Interface, A P I ) О С на базе набора ф у н к ц и й , в о з в р а щ а ю щ и х в с л у ч а е в о з н и к н о в е н и я о ш и б к и к а к о е - л и б о особое значение. П р е д п о л а г а е т с я , что после о б р а щ е н и я к ф у н к ц и и о п е р а ц и о н н о й с и с т е м ы ( т а к о й , н а п р и м е р , как о т к р ы т и е файла, ч т е н и е блока д а н н ы х из этого файла и т. п.) вызываю¬ щая п р о г р а м м а п р о в е р я е т код возврата, п р е д п р и н и м а я при в о з н и к н о в е н и и о ш и б к и ка¬ кие-либо действия. В о т как м ы м о ж е м и с п о л ь з о в а т ь м о д и ф и ц и р о в а н н ы й в а р и а н т м е т о д а D i v i d e , воз¬ в р а щ а ю щ и й н у л е в о е значение при в о з н и к н о в е н и и о ш и б к и д е л е н и я н а нуль: j

Divide (10,

i)

0) деления

на

В реальных программах встречается довольно большая глубина вложенности ф у н к ц и й и м е т о д о в , когда один метод в ы з ы в а е т д р у г о й , тот, в свою о ч е р е д ь , обраща¬ ется к т р е т ь е м у и ч е т в е р т о м у и т. д. С т р о к и л и с т и н г а п р о г р а м м ы , п р е д н а з н а ч е н н ы е для о б р а б о т к и к о д о в в о з в р а т а д л я всех этих ф у н к ц и й или м е т о д о в , з а г р о м о ж д а ю т и с х о д н ы й т е к с т п р о г р а м м ы , д е л а я его «нечитаемым». Кроме того, программист может по забывчивости опустить проверку кода в о з в р а т а к а к о й - л и б о о д н о й ф у н к ц и и или метода, что п р и в е д е т к а в а р и й н о м у за¬ в е р ш е н и ю п р и л о ж е н и я во в р е м я его р а б о т ы . Есть и еще одна п р о б л е м а . О б ы ч н о ф у н к ц и я , о б н а р у ж и в ш а я о ш и б к у , не знает, что с ней н у ж н о д е л а т ь . Дейст¬ вия по о б р а б о т к е о ш и б к и д о л ж н ы в ы б и р а т ь с я и с х о д я из к о н т е к с т а в ы з о в а этой функ¬ ц и и , так как одно и то же с о б ы т и е в р а з н ы х случаях м о ж е т и н т е р п р е т и р о в а т ь с я поразному. Рассмотрим следующую ситуацию. П у с т ь мы с к а н и р у е м д и с к , читая по о ч е р е д и все его с е к т о р ы до д о с т и ж е н и я к о н ц а д и с к а . Мы в ы п о л н я е м эту о п е р а ц и ю с ц е л ь ю о п р е д е л е н и я о б щ е г о к о л и ч е с т в а секто¬ р о в , р а с п о л о ж е н н ы х на д и с к е . Д а л е е , пусть у нас есть ф у н к ц и я R e a d S e c t o r , единст¬ венная задача к о т о р о й — чтение сектора д и с к а с з а д а н н ы м н о м е р о м . К о г д а в п р о ц е с с е п о с л е д о в а т е л ь н о г о чтения п р о г р а м м а д о б е р е т с я до конца д и с к а , в о з н и к н е т о ш и б к а , так как ф у н к ц и я R e a d S e c t o r п о п ы т а е т с я п р о ч и т а т ь сектор с не¬ с у щ е с т в у ю щ и м н о м е р о м . О ш и б к а м о ж е т в о з н и к н у т ь и в д р у г о й с и т у а ц и и — если су¬ щ е с т в у ю щ и й сектор д и с к а о к а ж е т с я и с п о р ч е н н ы м . С а м а по себе н а ш а ф у н к ц и я R e a d S e c t o r не знает, что делать с о ш и б к о й , так как она м о ж е т в ы з ы в а т ь с я и з р а з н ы х п р о г р а м м . П р и о б н а р у ж е н и и о ш и б к и в ы з ы в а ю щ а я п р о г р а м м а м о ж е т , н а п р и м е р , п о в т о р и т ь в ы п о л н е н и е о п е р а ц и и в н а д е ж д е на т о , что сектор все же будет прочитан п р а в и л ь н о , а м о ж е т а в а р и й н о з а в е р ш и т ь работу про¬ г р а м м ы . В о п и с а н н о м в ы ш е случае, когда п р о г р а м м а о п р е д е л я е т о б ъ е м д и с к а , возник¬ н о в е н и е о ш и б к и чтения сектора — н о р м а л ь н о е о ж и д а е м о е с о б ы т и е , к о т о р о е не долж¬ но р а с с м а т р и в а т ь с я как о ш и б о ч н о е . Глава 9. Обработка исключений

287


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

Применение механизма исключений Как в и д и т е , к л а с с и ч е с к и е с х е м ы о б р а б о т к и о ш и б о к , о с н о в а н н ы е н а п р о в е р к е допус¬ т и м о с т и п а р а м е т р о в и анализе к о д о в возврата ф у н к ц и й и м е т о д о в , о б л а д а ю т сущест¬ венными недостатками. П р а к т и ч е с к и все с о в р е м е н н ы е языки п р о г р а м м и р о в а н и я с н а б ж е н ы м о щ н ы м сред¬ ством о б р а б о т к и о ш и б о к , о с н о в а н н ы м на и с п о л ь з о в а н и и так н а з ы в а е м ы х исключений (exceptions). я в л я е т с я и с к л ю ч е н и е м ( п р о с т и т е за т а в т о л о г и ю ) и я з ы к п р о г р а м м и р о ­ вания С#. С р е д с т в а о б р а б о т к и и с к л ю ч е н и й в я з ы к е С# д е л а ю т и с х о д н ы й текст п р о г р а м м по¬ нятнее и п р о щ е . С их п о м о щ ь ю п р о г р а м м и с т м о ж е т о р г а н и з о в а т ь с т р у к т у р н у ю обра¬ ботку о ш и б о к , как п р о г н о з и р у е м ы х , так и в о з н и к а ю щ и х н е о ж и д а н н о . С и с т е м а о б р а б о т к и о ш и б о к , и с п о л ь з о в а н н а я в б и б л и о т е к е классов Microsoft Framework, работает исключительно с применением механизма исключений. Поэтому, к а к у ю бы п р о г р а м м у вы ни р а з р а б а т ы в а л и на я з ы к е С # , вы о б я з а т е л ь н о с т о л к н е т е с ь с н е о б х о д и м о с т ь ю о б р а б а т ы в а т ь или в ы з ы в а т ь и с к л ю ч е н и я (в д р у г и х я з ы к а х програм¬ м и р о в а н и я это не так; п р о г р а м м ы С + + , н а п р и м е р , м о ж н о с о з д а в а т ь и без о б р а б о т к и исключений).

Блоки try-catch Для того ч т о б ы о р г а н и з о в а т ь в своей п р о г р а м м е С# о б р а б о т к у о ш и б о к с использова¬ н и е м и с к л ю ч е н и й , н у ж н о п р и м е н и т ь б л о к и , с о з д а н н ы е при п о м о щ и к л ю ч е в ы х слов try,

catch и

С о з д а н и е и с к л ю ч е н и я (или, как еще г о в о р я т , передачу или

в о з б у ж д е н и е и с к л ю ч е н и я ) в ы п о л н я ю т с п о м о щ ь ю к л ю ч е в о г о слова t h r o w . В

блок

try

заключается

т. е. « н е н а д е ж н ы й » код.

код,

который

может

вызвать

возникновение

ошибок,

С л е д о м за б л о к о м t r y м о ж е т р а с п о л а г а т ь с я один или не­

с к о л ь к о б л о к о в c a t c h , в к о т о р ы х н а х о д и т с я код для о б р а б о т к и о ш и б о к . Р а с с м о т р и м п р и м е р о б р а б о т к и о ш и б к и д е л е н и я на нуль с п о м о щ ь ю и с к л ю ч е н и й (листинг Листинг

9.3.

Файл

using namespace

class

288

DivideByZeroExApp

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

С#. Самоучитель


static int

void i

args) 0;

i n t j

5

i ex)

Ошибка в п р о г р а м м е

Здесь м ы з а к л ю ч и л и в блок t r y строку п р о г р а м м ы , при в ы п о л н е н и и к о т о р о й м о ­ жет п �� о и з о й т и о ш и б к а д е л е н и я на нуль: try j

i;

В блоке t r y м о ж е т р а с п о л а г а т ь с я п р о и з в о л ь н о е к о л и ч е с т в о п р о г р а м м н ы х строк, содержащих вызовы методов, обращения к интерфейсам, свойствам и индексаторам, о п е р а т о р ы цикла и т. п. При о т с у т с т в и и о ш и б о к все эти строки и с п о л н я ю т с я о б ы ч н ы м образом. Если же в п р о ц е с с е в ы п о л н е н и я к а к о й - л и б о п р о г р а м м н о й строки в о з н и к а е т ошиб¬ ка, н о р м а л ь н а я п о с л е д о в а т е л ь н о с т ь р а б о т ы п р о г р а м м ы п р е р ы в а е т с я и у п р а в л е н и е пе¬ р е д а е т с я б л и ж а й ш е м у блоку c a t c h , р а с п о л о ж е н н о м у вслед з а б л о к о м t r y . В о т как в нашей п р о г р а м м е в ы г л я д и т блок c a t c h , п р е д н а з н а ч е н н ы й для о б р а б о т к и о ш и б к и д е л е н и я на нуль: ex) Ошибка

в

программе

(0)

В к р у г л ы х скобках после к л ю ч е в о г о слова c a t c h у к а з ы в а е т с я тип ( к л а с с ) обраба¬ т ы в а е м о г о и с к л ю ч е н и я . В д а н н о м случае м ы у к а з а л и базовый класс S y s t e m . Ex­ c e p t i o n , от которого все о с т а л ь н ы е классы о б р а б о т к и и с к л ю ч е н и й . С к о н с т р у и р о в а н н ы й т а к и м с п о с о б о м о б р а б о т ч и к и с к л ю ч е н и й способен п е р е х в а т ы в а т ь и с к л ю ч е н и я всех т и п о в , в о з н и к а ю щ и х при в ы п о л н е н и и кода, о г р а н и ч е н н о г о предше¬ с т в у ю щ и м блоком t r y .

Глава 9. Обработка исключений 10

Язык С#

289


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

К о г д а блок

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

н а объект класса

С в о й с т в а этого класса с о д е р ж а т и н ф о р м а ц и ю ,

которая м о ж е т о к а з а т ь с я п о л е з н о й при о б р а б о т к е и с к л ю ч е н и я : свойство S y s t e m .

S o u r c e с о д е р ж и т название п р о г р а м м ы , в которой

произошло исключение; в свойстве

х р а н и т с я текст с о о б щ е н и я о б о ш и б к е ;

свойство

асе

содержит

точную

информацию

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

в

программе

DivideByZeroEx

[Attempted

at c#

to

divide

by

args)

in

bo 11

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

Использование нескольких блоков catch Теперь вы з н а е т е , ч т о , з а к л ю ч и в « н е н а д е ж н ы й » с точки зрения в о з н и к н о в е н и я о ш и б о к код в блок t r y , вы м о ж е т е п е р е х в а т и т ь и о б р а б о т а т ь и с к л ю ч е н и я в блоке c a t c h . З а м е т и м , что и с к л ю ч е н и я в о з н и к а ю т в т а к и х р а с п р о с т р а н е н н ы х с и т у а ц и я х , как вы¬ х о д за п р е д е л ы м а с с и в а или с т р о к и в п р о ц е с с е и н д е к с а ц и и , д е л е н и е на н у л ь , п о п ы т к а и с п о л ь з о в а н и я с с ы л к и с о з н а ч е н и е м n u l l , н е д о п у с т и м о е п р е о б р а з о в а н и е типа ссылки и т. д. С п о м о щ ь ю м е х а н и з м а и с к л ю ч е н и й все п о д о б н ы е о ш и б к и н е т р у д н о о б н а р у ж и т ь и о б р а б о т а т ь во в р е м я в ы п о л н е н и я п р и л о ж е н и я . Программа может предусмотреть общий

блок о б р а б о т к и

исключений различных

т и п о в , о п р е д е л и в один блок c a t c h , или создать н е с к о л ь к о таких блоков — по о д н о м у для и с к л ю ч е н и й к а ж д о г о типа. П р и м е р п р о г р а м м ы , в к о т о р о й р е а л и з о в а н такой с п о с о б о б р а б о т к и и с к л ю ч е н и й не¬ с к о л ь к и х т и п о в , п р и в е д е н в л и с т и н г е 9.3. Листинг

9.3.

Файл

using System; namespace MultiCatch class

MultiCatchApp

static

void

string

290

args)

errType; А

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

Самоучитель


do тип Завершить р а б о т у Деление на Выход з а границы Неправильное приведение Отрицательный р а з м е р И с п о л ь з о в а н и е ссылки errType try

int

i j

0;

- 5

case int

array

new

int[5]

case o b j e c t ch ch byte b

new

case i int

array

new 0;

case array [0]

Глава 9. Обработка исключений

291


ex) Попытка

деления

Выход

на

границы ex)

Неправильное

приведение

типов")

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

while

(errType

Эта п р о г р а м м а п о з в о л я е т в ы б р а т ь с к о н с о л и тип о ш и б к и , а затем в ы п о л н я е т про¬ граммный код, содержащий данную ошибку. При вводе с к о н с о л и числа 1 будет в ы п о л н е н о п и с а н н ы й ранее к о д , предприни¬ мающий попытку выполнить деление на нуль: case int i int j break;

0; 5

i

П о п ы т к а в ы п о л н е н и я этого кода во время р а б о т ы п р о г р а м м ы п р и в е д е т к возникно¬ вению

исключения

System. DivideByZeroException.

Он

обрабатывается

при

п о м о щ и с л е д у ю щ е г о блока c a t c h : ex) Попытка

деления

на

К а к в и д и т е , в н а ш е м случае вся о б р а б о т к а с в о д и т с я к в ы в о д у на к о н с о л ь соответ¬ ствующего сообщения об ошибке. Если ввести на к о н с о л и число 2, а затем н а ж а т ь к л а в и ш у Enter, будет в ы п о л н е н с л е д у ю щ и й код: 2 int 292

array

new В

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

Самоучитель


0

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

этого кода в о з н и к а е т и с к л ю ч е н и е

Для о б р а б о т к и этого и с к л ю ч е н и я м ы п р е д у с м о т р е л и такой блок c a t c h : ex) Выход

Исключение типа

за

границы

v

п (неправильное приведение

т и п о в ) создается с л е д у ю щ и м ф р а г м е н т о м п р о г р а м м ы :

object

ch

new

byte b

Здесь м ы создаем п е р е м е н н у ю c h типа o b j e c t ( н а п о м н и м , что все классы в С # н а с л е д у ю т с я от класса o b j e c t ) . н а о б ъ е к т класса

Char

Далее мы з а п и с ы в а е м

в

эту

(предназначенный для представления

переменную ссылку

символов U N I C O D E ) ,

чего и н и ц и а л и з и р у е м п е р е м е н н у ю c h с и м в о л о м з в е з д о ч к и . Финальное преобразовать не в о з р а ж а е т

действие, ссылку, против

вызывающее хранящуюся

такого

возникновение

в

переменной

преобразования,

исключения,

ch,

так как

в

тип

это

byte.

переменная

попытка

Компилятор

ch

имеет

тип

а п р е о б р а з о в а н и я ссылки базового класса в ссылку на д о ч е р н и й к л а с с разре¬ ш е н ы . Т е п е р ь в роли д о ч е р н е г о класса у нас в ы с т у п а е т класс b y t e . О д н а к о во время р а б о т ы п р о г р а м м ы в ы я с н я е т с я , что в ы п о л н и т ь н е о б х о д и м о е пре¬ образование

типов

невозможно,

в

результате

чего

возникает

исключение

Sys­

t e m . I n v a l i d C a s t E x c e p t i o n . В о т его о б р а б о т ч и к : ex) Неправильное В не для

нашей

программе

всех

исключений,

ByZeroException,

мы

намеренно

а только

выход

за

для

приведение

предусмотрели

индивидуальную

трех

на нуль

границы

(деление

массива

обработку

System. IndexOutOfRange-

E x c e p t i o n и неправильное приведение типов S y s t e m . I n v a l i d C a s t E x c e p t i o n ) . Все о с т а л ь н ы е и с к л ю ч е н и я о б р а б а т ы в а ю т с я о д н и м у н и в е р с а л ь н ы м с п о с о б о м : Глава 9. Обработка исключений

293


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

ex

ToString

Вот что мы у в и д и м на э к р а н е при п о п ы т к е и с п о л ь з о в а н и я с с ы л к и , с о д е р ж а щ е й значение n u l l : Выберите тип ошибки: Завершить р а б о т у программы Д е л е н и е н а нуль Выход з а г р а н и ц ы м а с с и в а 3. Неправильное приведение типов Отрицательный размер массива 5 . И с п о л ь з о в а н и е ссылки n u l l 5 Исключение set to an i n s t a n c e of an o b j e c t . at Для

Object

reference

args) in 59

not c#

т е к с т а с о о б щ е н и я с и н ф о р м а ц и е й об и с к л ю ч е н и и мы о б р а т и л и с ь

к м е т о д у T o S t r i n g . Н а п о м н и м , что этот м е т о д о п р е д е л е н в я з ы к е С# для всех объек¬ тов и п о з в о л я е т п о л у ч и т ь т е к с т о в о е п р е д с т а в л е н и е объекта. Если же вам н у ж н а д е т а л и з и р о в а н н а я и н ф о р м а ц и я об и с к л ю ч е н и и , н е о б х о д и м о об¬ ращаться tion.

к

свойствам

класса

таким,

как

S o u r c e (название программы),

(текст сооб­

щения об ошибке) и

S t a c k T r a c e ( и н ф о р м а ц и я о месте воз¬

никновения ошибки).

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

9.4.

Файл

using System; namespace Overflow class static int 294

void x у

args) 200000; 100000; А

Фролов, Г.

Язык

Самоучитель


z

x *

у; 100000

int

z);

b 100000

int int

b)

z2;

unchecked zl

x

y; zl)

checked

z2

x

y; z2)

В н а ч а л ь н о м ф р а г м е н т е этой п р о г р а м м ы мы у м н о ж а е м ч и с л о 200 000 на 100 0 0 0 , в р е з у л ь т а т е чего по идее д о л ж н о п о л у ч и т ь с я з н а ч е н и е 20 000 000 000: int х int у int z

200000; 100000; х у; 100000

z)

О д н а к о , з а п у с т и в эту п р о г р а м м у н а в ы п о л н е н и е , н е т р у д н о у б е д и т ь с я , что р е з у л ь т а т будет равен о т р и ц а т е л ь н о м у числу -1 4 7 4 836 4 8 0 . В этом нет ничего у д и в и т е л ь н о г о : р а з р я д н о й сетки д а н н ы х типа i n t н е д о с т а т о ч н о для п р а в и л ь н о г о п р е д с т а в л е н и я ч и с л а 20 000 000 000. Глава 9. Обработка исключений

295


К о в а р с т в о этой о ш и б к и в т о м , что она не о б н а р у ж и в а е т себя ни на стадии компи¬ ляции п р о г р а м м ы , ��и на стадии в ы п о л н е н и я . П р о г р а м м а п о л у ч а е т н е п р а в и л ь н ы й рсзультат, а п р о г р а м м и с т м о ж е т об этом д а ж е не д о г а д ы в а т ь с я . О д н а к о , и с п о л ь з у я о п е р а т о р c h e c k e d , м ы м о ж е м о б н а р у ж и т ь о ш и б к у н а стадии выполнения программы: try int

b 100000 ex)

В данном на

случае о п е р а т о р

переполнение.

При

c h e c k e d проверяет результат выполняемой операции

возникновении

переполнения

оператор

создает исключение

S y s t e m . O v e r f l o w E x c e p t i o n , к о т о р о е м о ж е т быть о б р а б о т а н о п р о г р а м м о й . Оператор u n c h e c k e d , напротив, позволяет отключить проверку на переполнение. Ключевые

слова

checked

и

unchecked

можно

использовать

для

включения

и выключения проверки переполнения и по-другому: int int

z2;

unchecked x

у;

x

y;

checked z2

ex)

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

исключение

Sys

Ex

p t i o n при

обнаружении

переполне-

такой ф р а г м е н т кода с л е д у е т з а к л ю ч и т ь в блок c h e c k e d . А н а л о г и ч н о блок u n c h e c k e d предназначен для отключения проверки переполнения. 296

А В

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

Самоучитель


Стандартные классы исключений Класс E x c e p t i o n о б ы ч н о п р и м е н я е т с я в к а ч е с т в е у н и в е р с а л ь н о г о средства, позво¬ л я ю щ е г о о б р а б а т ы в а т ь о ш и б к и л ю б о г о типа. Для более т о н к о й к л а с с и ф и к а ц и и оши¬ бок л у ч ш е и с п о л ь з о в а т ь с т а н д а р т н ы е к л а с с ы , п о р о ж д е н н ы е о т класса E x c e p t i o n или р а з р а б о т а н н ы е вами с а м о с т о я т е л ь н о . С т а н д а р т н ы е классы о б р а б о т к и о ш и б о к п р е д у с м о т р е н ы п р а к т и ч е с к и для к а ж д о й б и б л и о т е к и классов Microsoft В о п и с а н и и м е т о д о в у к а з а н о , к а к и е ис¬ к л ю ч е н и я м о г у т создаваться при их в ы з о в е в случае в о з н и к н о в е н и я тех или и н ы х ошибочных ситуаций. Теперь в ы знаете, что д л я о д н о г о блока t r y м о ж н о о п р е д е л и т ь н е с к о л ь к о б л о к о в c a t c h , к о т о р ы е будут о б р а б а т ы в а т ь с я п о с л е д о в а т е л ь н о . Если в о з н и к н е т и с к л ю ч е н и е , то будет в ы п о л н е н тот блок c a t c h , в п а р а м е т р е к о т о р о г о это и с к л ю ч е н и е о б ъ я в л е н о . В том с л у ч а е , когда ни один блок не п о д х о д и т , в ы п о л н я е т с я блок с о б ъ я в л е н и е м клас¬ са E x c e p t i o n . А если такой блок не п р е д у с м о т р е н , и с к л ю ч е н и е б у д е т о б р а б о т а н о на у р о в н е с и с т е м ы и с п о л н е н и я В т а б л . 9.1 мы п е р е ч и с л и л и с т а н д а р т н ы е к л а с с ы и с к л ю ч е н и й С#. Таблица

Стандартные

классы

исключений

С# Описание

Класс

Б а з о в ы й класс д л я о б р а б о т к и и с к л ю ч е н и й , возни¬ Exception

к а ю щ и х при в ы п о л н е н и и а р и ф м е т и ч е с к и х опера¬ ций. Это такие и с к л ю ч е н и я , как и П р о и с х о д и т при п о п ы т к е з а п и с а т ь в

мас¬

сива д а н н ы е н е п р а в и л ь н о г о т и п а , о т л и ч н о г о от ти¬ па м а с с и в а Sys

Попытка деления целочисленного значения на нуль В о з н и к а е т при п о п ы т к е и с п о л ь з о в а т ь отрицатель¬

RangeException

ный и н д е к с в м а с с и в е или и н д е к с , в ы х о д я щ и й за г р а н и ц ы м а с с и в а Ошибка в процессе явного преобразования ссылки

Exception

No

Exception Глава 9. Обработка исключений

на о б ъ е к т б а з о в о г о класса в ссылку на о б ъ е к т про¬ и з в о д н о г о класса О ш и б к а при п о п ы т к е к о м б и н и р о в а н и я д в у х деле¬ гатов, не равных из-за т о г о , что тип в о з в р а щ а е м о г о з н а ч е н и я д е л е г а т о в отличен от v o i d П о п ы т к а и с п о л ь з о в а н и я с с ы л к и , с о д е р ж и м о е кото¬ рой р а в н о n u l l

297


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

Exception

П е р е п о л н е н и е при в ы п о л н е н и и а р и ф м е т и ч е с к о й Exception

операции

Exception

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

Создание исключений М е х а н и з м о б р а б о т к и о ш и б о к С# был бы н е п о л н ы м , если бы п р о г р а м м а м предоставля¬ лась в о з м о ж н о с т ь ю о б р а б а т ы в а т ь и с к л ю ч е н и я , но не с о з д а в а т ь их. К а к и с л е д о в а л о о ж и д а т ь , п р о г р а м м ы С # м о г у т с о з д а в а т ь ( и н ы м и с л о в а м и , в о з б у ж д а т ь или п е р е д а в а т ь ) и с к л ю ч е н и я при п о м о щ и к л ю ч е в о г о слова t h r o w . Рассмотрим различные способы создания исключений.

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

в

которой

нам

бы

пригодилось создавать

П у с т ь мы о п р е д е л и л и в своей п р о г р а м м е класс P o i n t , п р е д н а з н а ч е н н ы й для хра¬ нения т о ч е к с п о л о ж и т е л ь н ы м и к о о р д и н а т а м и : class

Point

public public

uint uint

xPos; yPos;

public

Point

(int

x,

int

y)

xPos yPos

П р и с о з д а н и и н о в о г о о б ъ е к т а класса P o i n t м ы п е р е д а е м к о о р д и н а т ы т о ч к и конст¬ руктору: pt2

298

new P o i n t ( 0 , new

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

Самоучитель


П р о б л е м а з а к л ю ч а е т с я в т о м , что в этой р е а л и з а ц и и класса P o i n t к о н с т р у к т о р знак п е р е д а в а е м ы х ему к о о р д и н а т , п о э т о м у п р о г р а м м а по о ш и б к е м о ж е т создать точку с о т р и ц а т е л ь н ы м и к о о р д и н а т а м и . Конечно, конструктор может проверить знак своих параметров, но как сообщить вызы­ вающей программе об ошибке? Ведь конструктор не может возвращать никаких значений! Выход в создании и с к л ю ч е н и я при о б н а р у ж е н и и о т р и ц а т е л ь н ы х к о о р д и н а т ( л и с т и н г 9.5). Листинг

9.5.

Файл

using System; namespace PositivePoint class

Point

public public

uint uint

xPos;

public

Point

(int

0

у

i f (x

x,

int

y)

0)

xPos yPos else t h r o w new E x c e p t i o n "Обнаружена о т р и ц а т е л ь н а я

class

координата

PositivePointApp

static

void

Point Point

ptl pt2

args)

pt2;

new P o i n t ( 0 , new P o i n t ( - l ,

2) ex)

Console

9. Обработка исключений

Исключение

299


Вот новый вариант конструктора: public

Point

i f (х

(int

0

х,

у

xPos yPos

int

у)

0)

(uint)y;

else t h r o w new "Обнаружена

отрицательная

координата

при о б н а р у ж е н и и о ш и б к и к о н с т р у к т о р объекта класса P o i n t в о з б у ж д а е т с о с т о я н и е и с к л ю ч е н и я , п р е р ы в а я н о р м а л ь н ы й ход в ы п о л н е н и я п р о г р а м м ы . При этом в качестве класса

п а р а м е т р а к л ю ч е в о м у слову

throw

п е р е д а е т с я с с ы л к а на новый о б ъ е к т

Exception.

Обратите в н и м а н и е , что при создании и с к л ю ч е н и я мы передаем конструктору объекта класса E x c e p t i o n параметр

строку описания о ш и б о ч н о й ситуации.

Вот как в ы з ы в а ю щ и й м е т о д о б р а б а т ы в а е т н а ш е и с к л ю ч е н и е : Point Point

р pt2

ptl; pt2;

new new ex) Исключение

К а к в и д и т е , т е х н и к а о б р а б о т к и с о з д а н н о г о нами и с к л ю ч е н и я класса E x c e p t i o n н и ч е м н е о т л и ч а е т с я о т т е х н и к и о б р а б о т к и о п и с а н н ы х в ы ш е и с к л ю ч е н и й , создавае¬ мых с и с т е м о й и с п о л н е н и я Framework.

Новый класс на базе класса Exception О п и с а н н а я в ы ш е м е т о д и к а с о з д а н и я и с к л ю ч е н и я класса E x c e p t i o n н е позволяет пе­ р е д а т ь в в ы з ы в а ю щ у ю п р о г р а м м у н и к а к о й д о п о л н и т е л ь н о й и н ф о р м а ц и и , кроме тек¬ стового сообщения об ошибке. Ч т о б ы п р о г р а м м а могла в ы п о л н и т ь более п о л н у ю д и а г н о с т и к у п р и ч и н возникно¬ вения и с к л ю ч е н и я ,

создайте

на

базе

класса

E x c e p t i o n собственный

производный

класс. П о л я т а к о г о класса м о г у т и с п о л ь з о в а т ь с я для х р а н е н и я р а с ш и р е н н о й информа¬ ции об о ш и б к е . При этом д о п о л н и т е л ь н а я и н ф о р м а ц и я м о ж е т быть п е р е д а н а , напри¬ м е р , ч е р е з п а р а м е т р ы к о н с т р у к т о р а класса, п р о и з в о д н о г о о т класса E x c e p t i o n . Создание

собственного

класса для

обработки

исключений

позволяет

выполнить

более т о н к у ю о б р а б о т к у о ш и б о к п о с р а в н е н и ю той, что п о з в о л я е т класс E x c e p t i o n

300

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


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

9.6.

Файл

using System; namespace PositivePointEx class

Exception

public public

int int

xErr; yErr;

public

x,

xErr yErr

class

int

y,

string

X;

Point

public public

uint uint

public

Point

i f (x

xPos;

(int

0

int

у

y)

0)

xPos yPos else t h r o w new "Обнаружена

class

отрицательная

y, координата

PositivePointExApp

static

void

Point Point

args)

ptl; pt2;

try ptl pt2

new new

Point(-l,

Глава 9. Обработка исключений

301


ex)

Исключение:

Здесь н а базе класса E x c e p t i o n м ы создали п р о и з в о д н ы й класс P o i n t E x c e p ­ tion class

PointException

public int public int public

Exception

xErr; Err

PointException

xErr yErr

x,

int

y,

string

errMessage)

x; y;

О б р а т и т е в н и м а н и е , что

к о н с т р у к т о р класса P o i n t E x c e p t i o n в ы з ы в а е т конст­

р у к т о р б а з о в о г о класса E x c e p t i o n .

Это н е о б х о д и м о для п р а в и л ь н о г о ф у н к ц и о н и р о ­

вания с и с т е м ы о б р а б о т к и и с к л ю ч е н и й . Расширенная

информация

об

ошибке,

а именно

ошибочные координаты точки,

х р а н я т с я в полях x E r r и y E r r класса P o i n t E x c e p t i o n .

Они и н и ц и а л и з и р у ю т с я

конструктором. П р и о б н а р у ж е н и и о ш и б к и в н о в о м в а р и а н т е к о н с т р у к т о р а класса P o i n t м ы соз­ даем

исключение

public

Point

i f (x xPos yPos

0

класса

(int

x,

int

и с п о л ь з у я для

этого

ключевое

слово

у)

0)

у

(uint)y;

else t h r o w new "Обнаружена

302

PointException,

отрицательная

y, координата А

Фролов, Г.

Фролов. Язык

Самоучитель


Помимо

текста

сообщения

об

ошибке

мы

передаем

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

класса

P o i n t E x c e p t i o n дополнительную и н ф о р м а ц и ю — ошибочные координаты точки. О б р а б о т ч и к нашего

исключения об

ошибке

извлекает и з и

ошибочные

полей

объекта

координаты,

класса

Point-

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

это

на к о н с о л и :

ptl pt2

new P o i n t ( 0 , new P o i n t

0) 2 ex)

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

Конструкторы класса Exception Как мы уже говорили, и с к л ю ч е н и й С#.

В

классе

класс E x c e p t i o n является

б а з о в ы м д л я всех

E x c e p t i o n предусмотрено

несколько

классов

перегруженных

конструкторов: public public public public

Exception

П е р в ы й и з этих к о н с т р у к т о р о в самый п р о с т о й . О н п р е д н а з н а ч е н д л я т о г о , ч т о б ы в о з б у ж д а т ь с о с т о я н и е и с к л ю ч е н и я без т е к с т о в о г о с о о б щ е н и я о б о ш и б к е . В т о р о й к о н с т р у к т о р вам у ж е з н а к о м — его е д и н с т в е н н ы й п а р а м е т р п р е д н а з н а ч е н для передачи текстового сообщения в обработчик исключения. С п о м о щ ь ю т р е т ь е г о к о н с т р у к т о р а м о ж н о о б р а б а т ы в а т ь так н а з ы в а е м ы е внутрен­ ние (inner) и с к л ю ч е н и я . Э т о т к о н с т р у к т о р п о з в о л я е т и з м е н и т ь и с к л ю ч е н и е , а затем пе¬ р е д а т ь его для д а л ь н е й ш е й о б р а б о т к и . И н а к о н е ц , ч е т в е р т ы й к о н с т р у к т о р п о з в о л я е т и н и ц и а л и з и р о в а т ь о б ъ е к т класса E x c e p t i o n п о с л е д о в а т е л ь н ы м и д а н н ы м и (такие д а н н ы е будут р а с с м о т р е н ы п о з ж е ) . В п р е д ы д у щ е м п р и м е р е п р о г р а м м ы м ы , создавая п р о и з в о д н ы й класс P o i n t E x c e p t i o n , п е р е о п р е д е л и л и в нем т о л ь к о второй в а р и а н т к о н с т р у к т о р а . О д н а к о кор¬ р е к т н е е было бы п р е д у с м о т р е т ь п е р е г р у з к у всех ч е т ы р е х в а р и а н т о в , н а п р и м е р так: class

PointException

public public

int

Exception

xErr;

9. Обработка исключений

303


public

x,

int

y)

x,

int

y,

string

errMessage)

public PointException x, int Exception innerException) Exception

y,

string

errMessage,

xErr yErr

public

xErr yErr

xErr yErr

x;

PointException

x;

y;

public si,

xErr yErr

x, int y, StreamingContext s i , sc)

sc)

x; y;

П р и с о з д а н и и с о б с т в е н н ы х классов и с к л ю ч е н и й п р и н я т о д о б а в л я т ь к имени класса с у ф ф и к с « E x c e p t i o n » : так будет сразу п о н я т н о н а з н а ч е н и е класса. И м е н н о поэтому мы и назвали свой класс P o i n t E x c e p t i o n , а не к а к - н и б у д ь еще. Х о т я , р а з у м е е т с я , это п р а в и л о н е с т р о г о е

вы м о ж е т е в ы б р а т ь для н а з в а н и я т а к о г о класса л ю б о е имя.

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

304

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

Самоучитель


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

Файл

using System; namespace class

Point

public public public

uint uint int

xPos; yPos;

public

Point

(int

x,

int

y)

try ratio

x

catch

class

PointExInternalApp

static

void

Point Point Point

args)

ptl; pt2;

try ptl pt2 pt3

new new new

Point(-l,

ex)

Исключение:

Глава

Обработка

305


В

центре

нашей

программы

находится

класс

Point,

представляющий

точку

н а п л о с к о с т и к о о р д и н а т . Д о п о л н и т е л ь н о к полям x P o s и y P o s , х р а н я щ и м к о о р д и н а т ы т о ч к и , мы о б ъ я в и л и в этом классе поле r a t i o , п р е д н а з н а ч е н н о е для х р а н е н и я отно¬ шений к о о р д и н а т : class

Point

public public public

uint xPos; uint yPos; int r a t i o ;

К о н с т р у к т о р класса P o i n t при п о м о щ и о п е р а ц и и д е л е н и я , ключения: public

Point

(int

х,

int

вычисляет

о т н о ш е н и е к о о р д и н а т п о осям Х и Y к о т о р о й м о ж е т п р и в е с т и к п о я в л е н и ю ис¬

у)

try ratio

x

catch

О б р а т и т е в н и м а н и е на о б р а б о т ч и к и с к л ю ч е н и й , п р е д у с м о т р е н н ы й в к о н с т р у к т о р е класса P o i n t . Этот обработчик вначале выводит на консоль сообщение об о ш и б к е (это имитация об¬ работки о ш и б к и внутри того метода, где возникло исключение), а затем передает исклю­ чение в в ы з ы в а ю щ и й метод при п о м о щ и ключевого слова t h r o w без параметров. В н у т р и в ы з ы в а ю щ е г о метода м ы т о ж е п р е д у с м о т р е л и о б р а б о т ч и к и с к л ю ч е н и й :

pt2 pt3

new new new

0) (-1,

ex) Исключение: Если з а п у с т и т ь нашу п р о г р а м м у на в ы п о л н е н и е , то на к о н с о л и появятся следую¬ щие с о о б щ е н и я :

306

А

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


Произошло и с к л ю ч е н и е Исключение: A t t e m p t e d

to

divide

by

zero.

П е р в о е с о о б щ е н и е в ы в е д е т о б р а б о т ч и к и с к л ю ч е н и й , п р е д у с м о т р е н н ы й в конструк¬ торе класса P o i n t , а второе — м е т о д M a i n , с о з д а ю щ и й о б ъ е к т этого класса (и н е я в н о вызывающий упомянутый конструктор).

Применение блока finally В н е к о т о р ы х с л у ч а я х при о б р а б о т к е и с к л ю ч е н и й с п р и м е н е н и е м блоков t r y и c a t c h имеет с м ы с л д о п о л н и т е л ь н о п р е д у с м о т р е т ь блок Э т о т блок в ы п о л н я е т с я всегда, вне з а в и с и м о с т и от т о г о , п р о и з о ш л о ли и с к л ю ч е н и е в процессе р а б о т ы блока t r y или нет. Н а и л у ч ш е е п р и м е н е н и е д л я блока освобождение ресурсов, заказан­ ных п р о г р а м м о й перед в о з н и к н о в е н и е м и с к л ю ч е н и й . Х о т я система с б о р к и м у с о р а ав¬ т о м а т и ч е с к и о с в о б о ж д а е т н е н у ж н у ю более о п е р а т и в н у ю п а м я т ь , д р у г и е р е с у р с ы , та¬ кие, как, н а п р и м е р , о т к р ы т ы е п о т о к и , с в я з а н н ы е с ф а й л а м и , следует з а к р ы в а т ь я в н ы м образом, вызывая соответствующие методы. В л и с т и н г е 9.8 мы п р и в е л и п р и м е р п р о г р а м м ы , д е м о н с т р и р у ю щ е й и с п о л ь з о в а н и е блока finally. Листинг using

9.8.

Файл

ch09\Finally\FinallyApp.cs

System; Finally

class

FinallyApp

static С

void

static

void

static

void

path)

data) в

int x int у static

5

файл

строки

x;

void

Глава 9. Обработка исключений

args)

307


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

Так как мы е щ е не р а с с к а з ы в а л и вам о р а б о т е с ф а й л а м и , в м е с т о в ы з о в а р е а л ь н ы х м е т о д о в , п р е д н а з н а ч е н н ы х для записи в ф а й л , мы будем п о л ь з о в а т ь с я и м и т а т о р а м и . П е р е д тем как п р и с т у п и т ь к р а б о т е с ф а й л о м , его н е о б х о д и м о о т к р ы т ь . Эта опера¬ ция и м и т и р у е т с я в н а ш е й п р о г р а м м е openFile: static

void

path)

В качестве п а р а м е т р а методу п е р е д а е т с я путь к о т к р ы в а е м о м у файлу, од¬ н а к о , в н а ш е м и м и т а т о р е никак не и с п о л ь з у е т с я . П о с л е того как файл о т к р ы т , в него м о ж н о з а п и с ы в а т ь д а н н ы е . Эта о п е р а ц и я ими¬ т и р у е т с я в нашей п р о г р а м м е м е т о д о м w r i t e F i l e : static

void

data) в

int x int у

0; 5

файл

строки

x;

В м е с т о з а п и с и с т р о к и , передаваемой методу в качестве е д и н с т в е н н о г о п а р а м е т р а , этот м е т о д о т о б р а ж а е т строку на к о н с о л и . А затем о н . . . в ы п о л н я е т д е л е н и е числа 5 на 0, ч т о б ы вызвать исключение. Т а к и м с п о с о б о м мы и м и т и р у е м в о з н и к н о в е н и е о ш и б к и в п р о ц е с с е записи д а н н ы х в файл. И н а к о н е ц , п о с л е р а б о т ы необходимо з а к р ы т ь файл. Если этого не с д е л а т ь , с о д е р ­ ж и м о е файла на д и с к е будет и с к а ж е н о . Для и м и т а ц и и з а к р ы т и я файла в нашей про­ грамме применяется метод c l o s e F i l e : static

308

void

closeFile()

А В

Г, В. Фролов

Самоучитель


Теперь з а й м е м с я самой п р о г р а м м о й . М е т о д M a i n нашей

программы

о т к р ы в а е т файл

посредством метода

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

ex)

О д н а к о вызов м е т о д а w r i t e F i l e п р и в е д е т к в о з н и к н о в е н и ю о ш и б к и д е л е н и я н а н у л ь , в р е з у л ь т а т е чего у п р а в л е н и е будет п е р е д а н о блоку c a t c h , о б р а б а т ы в а ю щ е ¬ му о ш и б к у . П о с л е з а в е р ш е н и я о б р а б о т к и в дело в к л ю ч и т с я блок i n a l l y . O H будет в ы п о л н е н в л ю б о м с л у ч а е , д а ж е при в о з н и к н о в е н и и и с к л ю ч е н и я в п р о ц е с с е записи д а н н ы х в файл. В этом н е т р у д н о у б е д и т ь с я , з а п у с т и в п р о г р а м м у и п о с м о т р е в на с о о б щ е н и я , к о т о р ы е она в ы в е д е т на к о н с о л ь : Открытие Запись в Закрытие

файла файл с т р о к и t e s t Attempted файла

to

divide

by

zero.

К а к в и д и т е , в н а ч а л е п р о и з о ш л о о т к р ы т и е файла, затем — п о п ы т к а записи д а н н ы х в файл. П о с л е этого п р о и з о ш л о и с к л ю ч е н и е , и наша п р о г р а м м а его о б р а б о т а л а . В к о н к о н ц о в файл

был з а к р ы т при п о м о щ и метода c l o s e F i l e ,

Глава 9. Обработка исключений

в ы з в а н н о г о в блоке

309


Глава 10. Многопоточность Как п р а в и л о , н о р м а л ь н ы й ч е л о в е к с п о с о б е н в ы п о л н я т ь н е с к о л ь к о задач Н е с м о т р я на то что врачи с ч и т а ю т это в р е д н ы м , м н о г и е л ю б я т читать во время еды или с м о т р е т ь т е л е в и з о р , у х и т р я я с ь при этом р е а г и р о в а т ь на р е п л и к и о к р у ж а ю щ и х . к о м п ь ю т е р ы также могут д е л а т ь о д н о в р е м е н н о н е с к о л ь к о дел. На¬ п р и м е р , пока п е ч а т а е т с я д о к у м е н т , м о ж н о п р и н я т ь почту или о т р е д а к т и р о в а т ь д р у г и е д о к у м е н т ы . Р а з у м е е т с я , ч т о б ы такое стало в о з м о ж н ы м , О С д о л ж н а д о п у с к а т ь парал¬ лельную работу нескольких программ. Если ОС на это с п о с о б н а , она н а з ы в а е т с я многопоточной ( m u l t i - t h r e a d e d ) . При этом работа

каждой

программы

осуществляется

в

рамках

одного

потока

исполнения,

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

(single-threaded).

В мир п е р с о н а л ь н ы х к о м п ь ю т е р о в м н о г о п о т о ч н о с т ь

вторглась о т н о с и т е л ь н о

не¬

д а в н о , и д а л е к о не к а ж д ы й п р о г р а м м и с т у м е е т и с п о л ь з о в а т ь все ее п р е и м у щ е с т в а . Во м н о г и х с л у ч а я х м н о г о п о т о ч н о с т ь в ц е л о м б л а г о п р и я т н о с к а з ы в а е т с я на производи¬ т е л ь н о с т и с и с т е м ы , так как во в р е м я о ж и д а н и я о д н и х задач с в о ю работу могут выпол¬ нять д р у г и е з а д а ч и , г о т о в ы е для э т о г о . Н а п р и м е р , если вы р а б о т а е т е в И н т е р н е т е , то м о ж е т е о д н о в р е м е н н о п о д к л ю ч и т ь с я к н е с к о л ь к и м с е р в е р а м F T P и W e b , « п е р е к а ч и в а я » сразу н е с к о л ь к о ф а й л о в и з а г р у ж а я н е с к о л ь к о д о к у м е н т о в H T M L . П р и этом еще м о ж н о о т п р а в л я т ь или п о л у ч а т ь элек¬ т р о н н у ю почту. О б щ а я с к о р о с т ь п е р е д а ч и д а н н ы х в этом случае будет в ы ш е , чем при п о о ч е р е д н о й р а б о т е с с е р в е р а м и , — пока один из них н а х о д и т с я в с о с т о я н и и ожида¬ ния, вы будете п о л у ч а т ь д а н н ы е от д р у г о г о . Такое у в е л и ч е н и е средней скорости пере¬ дачи д а н н ы х в о з м о ж н о из-за т о г о , что ч е р е з о б щ и й канал И н т е р н е т а м о г у т одновре¬ м е н н о п р о х о д и т ь п а к е т ы д а н н ы х , п р е д н а з н а ч е н н ы е для р а з л и ч н ы х а д р е с а т о в . О д н а к о все это касается п о л ь з о в а т е л е й . А какие п р е и м у щ е с т в а п о л у ч а т от поточности

программисты?

П р о г р а м м и с т ы с м о г у т с о з д а в а т ь п р и л о ж е н и я , р а б о т а ю щ и е в м н о г о п о т о ч н о м ре¬ ж и м е . При этом о т д е л ь н ы е к о м п о н е н т ы т а к и х п р и л о ж е н и й с м о г у т р а б о т а т ь одновре¬ м е н н о , н е м е ш а я д р у г другу. О ч е н ь часто м н о г о п о т о ч н о с т ь и с п о л ь з у е т с я при в ы п о л н е н и и к а к и х - л и б о длитель¬ ных о п е р а ц и й . При этом к о д , о б е с п е ч и в а ю щ и й ф у н к ц и о н и р о в а н и е п о л ь з о в а т е л ь с к о г о и н т е р ф е й с а , р а б о т а е т в р а м к а х о д н о г о п о т о к а , а к о д , в ы п о л н я ю щ и й д л и т е л ь н у ю опе¬ рацию, — в рамках другого. В к а ч е с т в е п р и м е р а мы м о ж е м д а н н ы х C r a s h U n d o L a b o r a t o r y (рис.

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

для с л у ж б ы в о с с т а н о в л е н и я д а н н ы х

310


drive Drive S:\ opened

partition

Пример

многопоточного

приложения

В окне P a r t i t i o n S c a n n e r о т о б р а ж а е т с я х о д п р о ц е с с а с к а н и р о в а н и я д и с к а , выпол¬ н я е м о г о с ц е л ь ю поиска у ц е л е в ш и х р а з д е л о в . П р и этом для в ы п о л н е н и я д л и т е л ь н о г о п р о ц е с с а с к а н и р о в а н и я запускается д о п о л н и т е л ь н ы й п о т о к , р а б о т а ю щ и й в р а м к а х того же п р о ц е с с а , в р а м к а х к о т о р о г о р а б о т а е т и г л а в н ы й поток и с п о л н е н и я п р о г р а м м ы . Зачем с к а н и р о в а т ь диск в о т д е л ь н о м п о т о к е ? Т о л ь к о так м ы смогли д о б и т ь с я , ч т о б ы в о в р е м я д л и т е л ь н о г о с к а н и р о в а н и я м о ж н о было п р о д о л ж а т ь работу с п р о г р а м м о й C r a s h U n d o L a b o r a t o r y нирования,

просматривать

предварительные

результаты

следить за х о д о м ска¬

сканирования,

прерывать

п р о ц е с с и т. п. Если к о м п л е к с C r a s h U n d o Laboratory и с п о л ь з у е т с я для у д а л е н н о г о в о с с т а н о в л е н и я д а н н ы х , то он создает о т д е л ь н ы е потоки для п е р е д а ч и у п р а в л я ю щ и х к о м а н д и резуль¬ татов их и с п о л н е н и я через И н т е р н е т . Работа в И н т е р н е т е ( о с о б е н н о ч е р е з м о д е м )

связана с з а д е р ж к а м и и о б р ы в а м и свя¬

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

Глава 10.


Мы р е к о м е н д у е м п р и м е н я т ь м н о г о п о т о ч н о с т ь в с л е д у ю щ и х с л у ч а я х : •

для в ы п о л н е н и я д л и т е л ь н ы х п р о ц е д у р , х о д о м к о т о р ы х н у ж н о у п р а в л я т ь ;

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

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

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

З а в с ю и с т о р и ю с у щ е с т в о в а н и я О С для п е р с о н а л ь н ы х к о м п ь ю т е р о в было р а з р а б о т а н о н е с к о л ь к о м о д е л е й многопоточности. Э т о п е р е к л ю ч а т е л ь н а я , с о в м е с т н а я и в ы т е с ­

няющая Р а с с к а ж е м об э т и х м о д е л я х м н о г о п о т о ч н о с т и в п о р я д к е их в н е д р е н и я в ОС персо¬ нальных компьютеров.

Переключательная многопоточность В о в р е м е н а п е р в ы х п е р с о н а л ь н ы х к о м п ь ю т е р о в , когда п о в с е м е с т н о н а и б о л ь ш е й попу¬ л я р н о с т ь ю п о л ь з о в а л а с ь о д н о п о т о ч н а я О С M S - D O S , п о л ь з о в а т е л ю была д о с т у п н а так называемая переключательная основанная главным образом на п р и м е н е н и и так н а з ы в а е м ы х резидентных программ. Р е з и д е н т н ы е п р о г р а м м ы з а г р у ж а л и с ь в о п е р а т и в н у ю п а м я т ь к о м п ь ю т е р а , остава¬ ясь там до п е р е з а г р у з к и О С . Д о в о л ь н о п о п у л я р н ы е в свое в р е м я р е з и д е н т н ы е кальку¬ л я т о р ы п о з в о л я л и , н а п р и м е р , н е п р е р ы в а я р а б о т ы п р о г р а м м ы р е д а к т о р а текста или д р у г о й п р о г р а м м ы , в ы п о л н я т ь а р и ф м е т и ч е с к и е в ы ч и с л е н и я . Для п е р е к л ю ч е н и я от ре¬ д а к т и р о в а н и я т е к с т а к в ы ч и с л е н и я м на р е з и д е н т н о м к а л ь к у л я т о р е и о б р а т н о н у ж н о было н а ж а т ь на к л а в и а т у р е ту или и н у ю к о м б и н а ц и ю к л а в и ш . К м о м е н т у п о я в л е н и я н а с т о я щ и х м н о г о п о т о ч н ы х ОС I B M O S / 2 и Microsoft Win¬ d o w s б ы л о с о з д а н о в е л и к о е м н о ж е с т в о с а м ы х р а з н о о б р а з н ы х и часто н е с о в м е с т и м ы х м е ж д у собой р е з и д е н т н ы х п р о г р а м м для M S - D O S . С р е д и них были как п р о с т ы е , так и д о с т а т о ч н о с л о ж н ы е п р о г р а м м ы , н а п р и м е р п р о г р а м м а Borland SideKick, выполняю¬ щая ф у н к ц и и п е р с о н а л ь н о г о о р г а н а й з е р а .

Совместная П о я в л е н и е О С Microsoft W i n d o w s версии 3 . 0 , р а б о т а в ш е й как о б о л о ч к а для M S - D O S , с т и м у л и р о в а л о п о я в л е н и е п р и л о ж е н и й д л я Microsoft W i n d o w s , р а б о т а в ш и х в р е ж и м е т а к н а з ы в а е м о й совместной многопоточности ( c o o p e r a t i v e m u l t i - t h r e a d i n g ) .

312


Для

с о в м е с т н о й м н о г о п о т о ч н о с т и п р и л о ж е н и я Microsoft W i n d o w s соз¬

д а в а л и с ь о п р е д е л е н н ы м о б р а з о м и время от в р е м е н и передавали д р у г д р у г у управле¬ ние. В р е з у л ь т а т е создавалась и л л ю з и я о д н о в р е м е н н о й работы н е с к о л ь к и х приложе¬ ний. А н а л о г и ч н ы й п р и н ц и п п р и м е н я л с я в сетевой ОС Novell N e t W a r e , а т а к ж е в ОС компьютеров Macintosh компании Apple. Совместная

многопоточность

решила

проблемы

совместимости,

которые

были

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

п о з в о л я е т з а п у с т и т ь , напри¬

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

будет о т ф о р м а т и р о в а н а ,

все о с т а л ь н ы е з а п у щ е н н ы е п р и л о ж е н и я будут р а б о т а т ь

очень м е д л е н н о . Е щ е один с у щ е с т в е н н ы й н е д о с т а т о к с о в м е с т н о й м н о г о п о т о ч н о с т и п р о я в л я е т с я при запуске н е д о с т а т о ч н о х о р о ш о о т л а ж е н н ы х п р и л о ж е н и й . Если п о к а к о й - л и б о п р и ч и н е приложение

не

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

сможет

периодически

работа всей

системы

передавать

управление

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

другим

запущенным

пользователю

т о л ь к о н а ж а т ь к о м б и н а ц и ю из трех и з в е с т н ы х к л а в и ш

останется кнопку

а п п а р а т н о г о сброса, р а с п о л о ж е н н у ю н а к о р п у с е к о м п ь ю т е р а .

Вытесняющая многопоточность В

отличие

(preemptive

от

совместной

mult-threading)

многопоточности

предполагает выделение

вытесняющая всем

запущенным

многопоточность приложениям

квантов времени е использованием системного таймера. Не с л е д у е т д у м а т ь , что у с п е ц и а л и с т о в к о м п а н и и Microsoft не х в а т и л о у м а п р и м е ­ нить в ы т е с н я ю щ у ю м н о г о п о т о ч н о с т ь в п е р в ы х в е р с и я х о б о л о ч к и Microsoft W i n d o w s . Она была и с п о л ь з о в а н а в ОС OS/2 версий 1.0

1.3, к о т о р а я в те в р е м е н а р а з р а б а т ы ­

валась с о в м е с т н о к о м п а н и я м и Microsoft и I B M . К с о ж а л е н и ю , слабая а р х и т е к т у р а п р о ц е с с о р а Intel 8 0 2 8 6 , н е д о с т а т о ч н а я произво¬ д и т е л ь н о с т ь в ы п у с к а в ш и х с я тогда к о м п ь ю т е р о в и м а л ы й о б ъ е м о п е р а т и в н о й п а м я т и , у с т а н о в л е н н о й в к о м п ь ю т е р а х п о д а в л я ю щ е г о числа п о л ь з о в а т е л е й (1 — 2 М б а й т ) , по¬ м е ш а л и ш и р о к о м у р а с п р о с т р а н е н и ю O S / 2 . Эта ОС с и с т и н н о й в ы т е с н я ю щ е й м у л ь т и з а д а ч н о с т ь ю р а б о т а л а очень м е д л е н н о и п р о и г р а л а с р а ж е н и е более л е г к о в е с н о й обо¬ л о ч к е Microsoft W i n d o w s версии 3.1. В е д ь в к о н е ч н о м счете пользователям было все равно,

какой

тип

многопоточности

применяется

в

ОС,

их и н т е р е с о в а л а

скорость

и удобство работы. С е г о д н я ситуация и з м е н и л а с ь . С о в р е м е н н ы е О С для п е р с о н а л ь н ы х к о м п ь ю т е р о в , т а к и е , как Microsoft W i n d o w s 9 5 / 9 8 / М Е , Microsoft W i n d o w s N T / 2 0 0 0 / X P , I B M W a r p ( в п р о ч е м , эту ОС у ж е не н а з о в е ш ь

а т а к ж е н а б и р а ю щ а я попу­

л я р н о с т ь ОС L i n u x р а б о т а ю т в р е ж и м е и с т и н н о й в ы т е с н я ю щ е й м н о г о п о т о ч н о с т и . Глава 10 Многопоточность


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

Процессы, потоки и приоритеты П р е ж д е чем п р и с т у п и т ь к о п и с а н и ю п р а к т и ч е с к и х п р и е м о в п р и м е н е н и я м н о г о п о т о ч ности в п р о г р а м м а х С # , с л е д у е т у т о ч н и т ь н е к о т о р ы е т е р м и н ы . О б ы ч н о в л ю б о й мно¬ г о п о т о ч н о й ОС в ы д е л я ю т т а к и е о б ъ е к т ы , как п р о ц е с с ы и п о т о к и . М е ж д у ними суще¬ ствует б о л ь ш а я р а з н и ц а , к о т о р у ю с л е д у е т четко себе п р е д с т а в л я т ь . В с и с т е м е испол¬ нения F r a m e w o r k д о б а в и л о с ь е щ е одно новое приложения, представленный классом

Процесс (process) ОС — это о б ъ е к т , к о т о р ы й создается О С , к о г д а п о л ь з о в а т е л ь запус¬ кает п р и л о ж е н и е . П р о ц е с с у в ы д е л я е т с я о т д е л ь н о е а д р е с н о е п р о с т р а н с т в о , п р и ч е м это п р о с т р а н с т в о ф и з и ч е с к и н е д о с т у п н о для д р у г и х п р о ц е с с о в . П р о ц е с с м о ж е т р а б о т а т ь с ф а й л а м и или с д р у г и м и п р о ц е с с а м и через к а н а л ы , соз¬ д а н н ы е ОС в о п е р а т и в н о й п а м я т и , в л о к а л ь н о й сети или ч е р е з И н т е р н е т . К о г д а в среде Microsoft W i n d o w s вы з а п у с к а е т е т е к с т о в ы й п р о ц е с с о р Microsoft W o r d for W i n d o w s или п р о г р а м м у к а л ь к у л я т о р а , вы тем с а м ы м создаете н о в ы й п р о ц е с с .

Поток Для к а ж д о г о п р о ц е с с а ОС с о з д а е т один г л а в н ы й поток (thread), к о т о р ы й является на¬ б о р о м в ы п о л н я ю щ и х с я п о о ч е р е д и к о м а н д ц е н т р а л ь н о г о п р о ц е с с о р а . П р и необходи¬ м о с т и г л а в н ы й п о т о к м о ж е т с о з д а в а т ь д р у г и е п о т о к и , п о л ь з у я с ь для этого программ¬ ным и н т е р ф е й с о м О С (Application P r o g r a m Interface, A P I ) . В с е п о т о к и , с о з д а н н ы е п р о ц е с с о м , в ы п о л н я ю т с я в а д р е с н о м п р о с т р а н с т в е этого п р о ц е с с а и и м е ю т д о с т у п к р е с у р с а м процесса. О д н а к о п о т о к о д н о г о п р о ц е с с а не име¬ ет н и к а к о г о д о с т у п а к р е с у р с а м задачи д р у г о г о процесса, так как они р а б о т а ю т в раз¬ н ы х а д р е с н ы х п р о с т р а н с т в а х . При н е о б х о д и м о с т и в з а и м о д е й с т в и я м е ж д у п р о ц е с с а м и или п о т о к а м и , п р и н а д л е ж а щ и м и р а з н ы м п р о ц е с с а м , с л е д у е т п о л ь з о в а т ь с я с п е ц и а л ь н о п р е д н а з н а ч е н н ы м и для этого с и с т е м н ы м и с р е д с т в а м и . А В

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


Домен приложения AppDomain В ы ш е мы п е р е ч и с л и л и п о н я т и я и п р и м е н я ю щ и е с я при с о з д а н и и многопо¬ т о ч н ы х п р и л о ж е н и й для ОС Microsoft W i n d o w s . Здесь и м е ю т с я в виду о б ы ч н ы е ис¬ п о л н я е м ы е п р и л о ж е н и я . Что же касается п р о г р а м м С # , то, как мы у ж е г о в о р и л и в на­ чале этой к н и г и , они в ы п о л н я ю т с я не сами по себе, а под у п р а в л е н и е м с и с т е м ы F r a m e w o r k . Эта же система отвечает и за р е а л и з а ц и ю м н о г о п о т о ч н о с т и в п р и л о ж е н и ­ ях, н а п и с а н н ы х с и с п о л ь з о в а н и е м я з ы к а С#. При этом F r a m e w o r k м о ж е т с о з д а в а т ь на базе о д н о г о с и с т е м н о г о п р о ц е с с а не­ с к о л ь к о в л о ж е н н ы х л о г и ч е с к и х п р о ц е с с о в , н а з ы в а е м ы х доменами приложений cation d o m a i n s ) . У п р а в л я е м ы е потоки С# (т. е. н а х о д я щ и е с я под у п р а в л е н и е м Microsoft work) могут р а б о т а т ь в р а м к а х о д н о г о или н е с к о л ь к и х д о м е н о в п р и л о ж е н и й и принад¬ л е ж а т ь при этом о д н о м у с и с т е м н о м у процессу. При з а п у с к е л ю б о г о п р и л о ж е н и я С# вначале с о з д а е т с я один п о т о к и с п о л н е н и я , ра¬ б о т а ю щ и й в р а м к а х д о м е н а п р и л о ж е н и я . П р и н е о б х о д и м о с т и п р о г р а м м н ы й к о д , ис¬ п о л н я е м ы й в р а м к а х о д н о г о д о м е н а п р и л о ж е н и я , м о ж е т создавать д о п о л н и т е л ь н ы е потоки и д о п о л н и т е л ь н ы е д о м е н ы п р и л о ж е н и й . П о э т о м у у п р а в л я е м ы е п о т о к и С# мо¬ гут пересекать г р а н и ц ы д о м е н о в п р и л о ж е н и я , если, к о н е ч н о , они о с т а ю т с я в п р е д е л а х о д н о г о с и с т е м н о г о процесса. Это п о з в о л я е т и с п о л ь з о в а т ь один и тот же п о т о к в рам¬ ках н е с к о л ь к и х д о м е н о в п р и л о ж е н и й .

Примеры многопоточных программ Чтобы использовать многопоточность, обычные программы, составленные на таких я з ы к а х п р о г р а м м и р о в а н и я , как С + + или P a s c a l , о б р а щ а ю т с я к п р о г р а м м н о м у и н т е р ­ фейсу О С . Ч т о же касается п р о г р а м м С # , то в них м н о г о п о т о ч н о с т ь р е а л и з у е т с я с п о ­ м о щ ь ю н а б о р а к л а с с о в , в х о д я щ и х в б и б л и о т е к у классов Microsoft Framework.

Создание и запуск потока класса Thread В нашей первой м н о г о п о т о ч н о й п р о г р а м м е мы будем р а б о т а т ь с к л а с с о м t e m . T h r e a d , о б ъ я в л е н н ы м в п р о с т р а н с т в е имен S y s t e m . T h r e a d i n g . И с х о д н ы й текст п р о г р а м м ы очень прост и п р е д с т а в л е н в л и с т и н г е 10.1. Листинг using using

10.1.

Файл

System;

namespace ThreadDemoApp public

static

void поток

Sys­


static

void

Thread

args)

thr

myThreadDelegate new

new

потока

Прежде

всего

обратите

внимание

на

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

пространства

имен

Sys­

метод

Main,

tem

М ы п о д к л ю ч и л и его с п о м о щ ь ю к л ю ч е в о г о слова u s i n g . В

главном

классе

приложения

где

находится

мы о б ъ я в и л и с т а т и ч е с к и й м е т о д M y T h r e a d , к о т о р ы й будет р а б о т а т ь в рамках отдель¬ ного потока: static

void поток

Е д и н с т в е н н а я задача м е т о д а M y T h r e a d — в ы в о д на к о н с о л ь т е к с т о в о й с т р о к и . Что же касается м е т о д а M a i n , то он з а п у с к а е т м е т о д M y T h r e a d в о т д е л ь н о м пото¬ ке, о т о б р а ж а я п е р е д этим с о о б щ е н и е н а к о н с о л и , д о ж и д а е т с я ввода п р о и з в о л ь н о й с т р о к и с к л а в и а т у р ы , после чего з а в е р ш а е т с в о ю работу: ThreadStart Thread thr

new

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

new потока

П о с л е з а п у с к а этой п р о г р а м м ы н а к о н с о л и о т о б р а ж а ю т с я с л е д у ю щ и е строки: З а п у с к п о т о к а MyThread MyThread: поток запущен К а к все это р а б о т а е т ? П о с м о т р е в в н и м а т е л ь н о н а и с х о д н ы й текст метода M a i n , м о ж н о что в нем нет п р я м о г о в ы з о в а м е т о д а M y T h r e a d . В м е с т о этого для м е т о д а M y T h r e a d соз¬ д а е т с я так н а з ы в а е м ы й делегат m y T h r e a d D e l e g a t e типа T h r e a d S t a r t (о т о м , что это т а к о е , мы р а с с к а ж е м чуть п о з ж е ) : ThreadStart

new А В

Г. В.

Самоучитель


Конструктору T h r e a d s t a r t

имя метода, и с п о л ь з у е м о г о для с о з д а н и я

делегата. Д а л е е с п о м о щ ь ю этого делегата создается п о т о к класса T h r e a d : Thread thr

new

И н а к о н е ц , с о з д а н н ы й поток запускается при п о м о щ и метода S t a r t , о п р е д е л е н н о ­ го в классе T h r e a d :

Сразу п о с л е в ы з о в а метода S t a r t н а ч н е т с я р а б о т а метода M y T h r e a d , о чем мож¬ но судить по с о о б щ е н и я м , п о я в л я ю щ и м с я на к о н с о л и . З а м е т и м , что о с н о в н а я про¬ г р а м м а и с о з д а н н ы й ей д о п о л н и т е л ь н ы й п о т о к р а б о т а ю т о д н о в р е м е н н о и с о в е р ш е н н о н е з а в и с и м о д р у г от друга. П р е ж д е чем м ы п р о д о л ж и м д а л ь н е й ш е е и з у ч е н и е м н о г о п о т о ч н о с т и , н е о б х о д и м о сделать н е к о т о р ы е р а з ъ я с н е н и я п о поводу н а з н а ч е н и я д е л е г а т о в , я в л я ю щ и х с я о д н о й из о с о б е н н о с т е й я з ы к а С#.

Использование делегатов К о г д а мы р а с с к а з ы в а л и о р а з м е щ е н и и п е р е м е н н ы х в о п е р а т и в н о й памяти к о м п ь ю т е р а , то г о в о р и л и , что каждая такая п е р е м е н н а я и м е е т свой адрес и р а з м е р . И с п о л ь з у я меха¬ низм у к а з а т е л е й , п р о г р а м м ы С++ могут н а п р я м у ю а д р е с о в а т ь с я к о б л а с т я м оператив¬ ной п а м я т и , в ы д е л е н н ы м для п е р е м е н н ы х . Аналогичная возможность прямой адресации имеется в языке С++ для функций. Ф у н к ц и и и м е т о д ы т о ж е р а з м е щ а ю т с я в о п е р а т и в н о й памяти по о п р е д е л е н н ы м адре¬ сам. З а п и с а в эти адреса в п е р е м е н н ы е , н а з ы в а е м ы е у к а з а т е л я м и на ф у н к ц и и или мето¬ д ы , п р о г р а м м ы С++ могут п е р е д а в а т ь и м у п р а в л е н и е . Д а л е е , п р о г р а м м ы С++ могут п е р е д а в а т ь у к а з а т е л и н а ф у н к ц и и д р у г и м ф у н к ц и я м и м е т о д а м . П р и этом ф у н к ц и я , и с п о л ь з у я п о л у ч е н н ы й у к а з а т е л ь на д р у г у ю ф у н к ц и ю , м о ж е т е е в ы з в а т ь . Ф у н к ц и и , в ы з ы в а е м ы е п о д о б н ы м о б р а з о м ч е р е з у к а з а т е л и , называ¬ ются функциями обратного вызова. Вы т а к ж е з н а е т е , что н е п р а в и л ь н о е п р и м е н е н и е у к а з а т е л е й м о ж е т привести к появ¬ лению трудно обнаруживаемых ошибок. В самом если п е р е д и с п о л ь з о в а н и е м со¬ д е р ж и м о е у к а з а т е л я на п е р е м е н н у ю или ф у н к ц и ю будет задано н е п р а в и л ь н о , послед¬ ствия о к а ж у т с я н е п р е д с к а з у е м ы м и . В н е м а л о й степени из-за этого о б с т о я т е л ь с т в а раз¬ р а б о т ч и к и С# отказались от и с п о л ь з о в а н и я у к а з а т е л е й . Тем не менее п о т р е б н о с т ь в ф у н к ц и я х о б р а т н о г о в ы з о в а при и с п о л ь з о в а н и и м н о г о п о т о ч н о с т и н у ж н о к а к и м - т о о б р а з о м F r a m e w o r k м е т о д , к о т о р ы й будет работать в р а м к а х о т д е л ь н о г о р а т н о г о в ы з о в а н е о б х о д и м ы и для о б р а б о т к и событий ( e v e n t s ) , рассказывать позже.

осталась. Например, указать с и с т е м е потока. Ф у н к ц и и об¬ о к о т о р ы х мы будем

Для р е а л и з а ц и и б е з о п а с н ы х указателей на м е т о д ы в я з ы к е С# был р а з р а б о т а н ме¬ х а н и з м д е л е г а т о в . В роли д е л е г а т а м о ж е т в ы с т у п а т ь с т а т и ч е с к и й м е т о д класса или статическое свойство. О б ы ч н о м е т о д - д е л е г а т о б ъ я в л я е т с я с п о м о щ ь ю к л ю ч е в о г о слова одна¬ ко в н а ш е м случае для р е а л и з а ц и и м н о г о п о т о ч н о с т и оно не п о т р е б о в а л о с ь : Глава 10. Многопоточность

317


public

static

void поток

Мы с о з д а е м д е л е г а т из о б ы ч н о г о с т а т и ч е с к о г о м е т о д а , п о л ь з у я с ь для этого классом ThreadStart: ThreadStart

new

П о с л е в ы п о л н е н и я этой строки в п е р е м е н н у ю m y T h r e a d D e l e g a t e будет записан с с ы л к а на м е т о д M y T h r e a d . Эта ссылка р е а л и з о в а н а без п р и м е н е н и я ука¬ зателей и п о т о м у б е з о п а с н а в и с п о л ь з о в а н и и . Она м о ж е т с с ы л а т ь с я (или, если х о т и т е , « у к а з ы в а т ь » ) т о л ь к о н а реально с у щ е с т в у ю щ и й о б ъ е к т класса T h r e a d S t a r t . По

своему назначению

метод

M y T h r e a d является

функцией

обратного

вызова.

Н а ш а п р о г р а м м а (т. е. м е т о д M a i n ) н и к о г д а не в ы з ы в а е т этот м е т о д н а п р я м у ю . В м е с т о этого она с о з д а е т для метода M y T h r e a d д е л е г а т m y T h r e a d D e l e g a t e , а затем пере¬ дает этот д е л е г а т к о н с т р у к т о р у класса T h r e a d : Thread

thr

new

К о н с т р у к т о р класса T h r e a d создает н о в ы й п о т о к , а наша п р о г р а м м а з а п и с ы в а е т с с ы л к у на этот п о т о к в п е р е м е н н у ю t h r . В д а л ь н е й ш е м с п о м о щ ь ю этой ссылки про¬ г р а м м а м о ж е т у п р а в л я т ь с о з д а н н ы м п о т о к о м . Для запуска п о т о к а на в ы п о л н е н и е , на¬ п р и м е р , и с п о л ь з у е т с я м е т о д S t a r t , о п р е д е л е н н ы й в классе T h r e a d :

Модели многопоточности В н и м а т е л ь н ы й ч и т а т е л ь з а м е т и т , ч т о , п о м и м о д е л е г а т о в , мы и с п о л ь з о в а л и в преды¬ д у щ е й п р о г р а м м е еще одну н о в у ю к о н с т р у к ц и ю С # , а и м е н н о атрибут S T A T h r e a d , з а д а ю щ и й так н а з ы в а е м у ю м о д е л ь м н о г о п о т о ч н о с т и Single Thread Apart¬ ments ( S T A ) : static

void

args)

Если г о в о р и т ь у п р о щ е н н о , т о а т р и б у т ы я з ы к а С # п о з в о л я ю т о п р е д е л и т ь характери¬ стики о б ъ е к т о в , перед к о т о р ы м и они р а с п о л о ж е н ы . В д а н н о м случае а т р и б у т S T A T h r e a d относится к методу M a i n , определяя модель многопоточности, в которой он б у д е т р а б о т а т ь при запуске п р о г р а м м ы . П р о г р а м м и с т ы п о з н а к о м и л и с ь с м о д е л я м и м н о г о п о т о ч н о с т и , когда к о м п а н и я M i п р и с т у п и л а к в н е д р е н и ю в п р а к т и к у своей м о д е л и к о м п о н е н т н ы х о б ъ е к т о в ( C o m p o n e n t Object а также технологии ActiveX. Детальное описание этих т е х н о л о г и й в ы х о д и т з а р а м к и нашей к н и г и . Ч и т а т е л е й , и н т е р е с у ю щ и х с я д а н н ы м в о п р о с о м , мы о т с ы л а е м к и П р и м е р ы с о з д а н и я к о м п о н е н т о в A c t i v e X для W e b мы о п и с а л и в 318

А В

Г.

Фролов. Язык

Самоучитель


Говоря к р а т к о , р е а л и з а ц и я м н о г о п о т о ч н о с т и в к о м п о н е н т а х

и ActiveX, реали­

зующих визуальный пользовательский интерфейс,

учета ряда о с о б е н н о с т е й .

Это,

с применением которого

например,

н а л и ч и е м е х а н и з м а обмена с о о б щ е н и я м и ,

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

работы

был создан

м е х а н и з м разделов,

или

апартаментов (apart¬

m e n t s ) . В рамках этого м е х а н и з м а были о п р е д е л е н ы р а з л и ч н ы е м о д е л и м н о г о п о т о ч н о сти, о т л и ч а ю щ и е с я д р у г от д р у г а с п о с о б о м в з а и м о д е й с т в и я к о м п о н е н т о в , а т а к ж е спо¬ собом с и н х р о н и з а ц и и их р а б о т ы : •

модель разделенных

модель

свободных

потоков потоков

(apartment (free

threading);

threading).

Если и с п о л ь з у е т с я м о д е л ь р а з д е л е н н ы х п о т о к о в , то заботу о с и н х р о н и з а ц и и в ы з о B O B к о м п о н е н т о в берет на себя с и с т е м а то п р о б л е м ы

синхронизации

и

а если м о д е л ь с в о б о д н ы х п о т о к о в —

обеспечения передачи данных между компонентами

л о ж а т с я на сами эти к о м п о н е н т ы . Модель разделенных потоков, в свою

очередь,

однопоточную модель

apartment,

многопоточную модель

(single-threaded (multi-threaded

ч л е н и т с я на две п о д м о д е л и : STA);

apartments,

МТА).

В о з в р а щ а я с ь к п р о г р а м м а м С # , з а м е т и м , что они делятся на к о н с о л ь н ы е програм¬ мы (к ним о т н о с я т с я все п р о г р а м м ы , п р и в е д е н н ы е в нашей к н и г е ) , а т а к ж е на про¬ г р а м м ы с г р а ф и ч е с к и м п о л ь з о в а т е л ь с к и м и н т е р ф е й с о м . П о с л е д н и е из них с о з д а ю т с я с п р и м е н е н и е м к л а с с о в W i n d o w s F o r m s , в х о д я щ и х в состав Microsoft

Framework.

Так вот, п р о г р а м м ы С# с г р а ф и ч е с к и м п о л ь з о в а т е л ь с к и м и н т е р ф е й с о м , с о з д а н н ы е на

базе

классов

Windows

Forms, должны

работать

в

однопоточной

модели

STA.

Это п р о и с х о д и т п о т о м у , что классы W i n d o w s F o r m s п о л ь з у ю т с я с т а н д а р т н ы м интер¬ ф е й с о м О С W i n d o w s , п р е д п о л а г а ю щ и м п р и м е н е н и е модели р а з д е л е н н ы х п о т о к о в . Что же касается д р у г и х п р о г р а м м С # , то они м о г у т создаваться на базе м о д е л и с в о ­ бодных К о г д а вы с о з д а е т е новый п р о е к т С# при п о м о щ и в и з у а л ь н о г о средства р а з р а б о т к и п р о г р а м м Microsoft Visual определение атрибута

Studio

то мастер проекта а в т о м а т и ч е с к и д о б а в л я е т перед о б ъ я в л е н и е м метода M a i n .

В предыдущих

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

Завершение работы созданного потока В п р е д ы д у щ е м п р и м е р е п р о г р а м м ы мы создавали поток, к о т о р ы й в ы в о д и л с о о б щ е н и е на к о н с о л ь , после чего сразу же з а в е р ш а л с в о ю работу. Р е а л ь н о , как мы у ж е г о в о р и л и , в отдельном потоке обычно выполняется какая-либо длительная процедура. В с л е д у ю щ е й м н о г о п о т о ч н о й п р о г р а м м е ( л и с т и н г 10.2) мы п р и в е д е м п о д о б н ы й

Глава

Многопоточность


Листинг using using

10.2.

Файл

System;

namespace class

LoopThreadApp

static

bool

stopThread;

public

static i

void

MyThread i+ + )

Console. WriteLine

i)

MyThread

static

void

args)

ThreadStart Thread thr

myThreadDelegate new

new

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

потока stopThread thr. string do

false;

str;

(x str поток: stopThread

true;

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

320

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

Самоучитель


static

bool

public

static

stopThread; void

i

i++)

i)

MyThread

Условие выхода из р а в е н с т в о с о д е р ж и м о г о поля s t o p T h r e a d з н а ч е н и ю t r u e . З а м е т и м , о д н а к о , что м е т о д M y T h r e a d сам п о себе н е и з м е н я е т с о д е р ж и м о е этого п о л я , п о э т о м у без п о с т о р о н н е й цикл будет р а б о т а т ь б е с к о н е ч н о д о л г о (ну х о р о ш о до п е р е з а г р у з к и к о м п ь ю т е р а ) . Обратите также внимание на то, что внутри цикла мы вызываем статический метод T h r e a d . S l e e p . Он приостанавливает работу потока, в рамках которого работает метод M y T h r e a d , на 2000 П о т о к приостанавливается таким образом, чтобы не у м е н ь ш а т ь ресурсы п р о ц е с с о р а Р а с с м о т р и м т е п е р ь и с х о д н ы й т е к с т метода M a i n , у п р а в л я ю щ е г о р а б о т о й н а ш е г о потока. П р е ж д е всего этот м е т о д создает н а базе м е т о д а M y T h r e a d д е л е г а т , п е р е д а в а я его к о н с т р у к т о р у класса T h r e a d : ThreadStart Thread thr Далее

метод

new new запускает

поток

на

выполнение,

записывая

перед

этим

значение

f a l s e в поле s t o p T h r e a d : потока stopThread

false;

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

появляющим­

ся на к о н с о л и , где х — п о с т о я н н о в о з р а с т а ю щ е е ч и с л о : Запуск потока Команда (х 0 1

MyThread выход)

Глава 10. Многопоточность Язык

Самоучитель

321


П о с л е з а п у с к а п о т о к а для метода M y T h r e a d метод M a i n н е з а в е р ш а е т с в о ю р а б о ­ ту, а о р г а н и з у е т в ы п о л н е н и е цикла: string do

str;

(х str поток: X В этом ц и к л е п р о г р а м м а в в о д и т т е к с т о в ы е к о м а н д ы с к л а в и а т у р ы , о т о б р а ж а я их на к о н с о л и в с л е д у ю щ е м виде: основной

поток:

<команда>

У с л о в и е з а в е р ш е н и я этого цикла з а п и с ы в а е т в поле

в в о д строки q. В этом случае н а ш а п р о г р а м м а

s t o p T h r e a d значение

true,

что п р и в о д и т к о с т а н о в к е п о т о к а

MyThread: stopThread

true;

Т а к и м о б р а з о м , после з а п у с к а п р о г р а м м ы з а п р а в о в ы в е с т и с о о б щ е н и я н а к о н с о л ь к о н к у р и р у ю т д в а потока: З а п у с к п о т о к а MyThread Команда (х — 0 1 основной

поток:

Команда

q

2 rMyThread:

3

основной

поток:

Команда

ВЫХОД)

4 ret основной поток: ret Команда (х выход) 5 б 7 8 х основной поток: х Поток MyThread о с т а н о в л е н В р е з у л ь т а т е с о о б щ е н и я п е р е м е ш и в а ю т с я м е ж д у собой. П р и в в о д е к о м а н д ы х оба п о т о к а ( г л а в н ы й , с о з д а н н ы й для м е т о д а M a i n , а также п о т о к , з а п у щ е н н ы й м е т о д о м завершают свою работу.

322

Г.

Фролов.

Самоучитель


Потоки и классы В п р е д ы д у щ и х п р и м е р а х п р о г р а м м мы з а п у с к а л и в о т д е л ь н о м п о т о к е статический ме¬ тод о с н о в н о г о класса п р и л о ж е н и я , т. е. того класса, в к о т о р о м о б ъ я в л е н м е т о д M a i n . О д н а к о в ряде случаев было бы у д о б н е е с о з д а в а т ь о б ъ е к т ы на базе классов С # , у кото¬ рых один или н е с к о л ь к о м е т о д о в р а б о т а л и бы в о т д е л ь н о м потоке.

Такие о б ъ е к т ы

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

10.3.

10.3.

Файл

System;

namespace class

TwoTreads StringWriter

private private private private

string str; bool stopThread; ThreadStart Thread thr;

public

s)

str s; stopThread

public

false;

void

if

WriteThread

public

void

go

myThreadDelegate thr new

Глава

Многопоточность

new

323


public

void

stop()

stopThread

true;

[STAThread] static void

args)

sw2 Console для о с т а н о в к и

new new Потоки запущены.

Нажмите

Enter

В этой п р о г р а м м е м ы о п р е д е л и л и класс к о т о р о г о цикли¬ чески в ы в о д я т н а к о н с о л ь т е к с т о в у ю с т р о к у , п е р е д а н н у ю к о н с т р у к т о р у : StringWriter private private private private

string str; bool stopThread; ThreadStart Thread

public str stopThread

s)

false;

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

324

A

Г.

Фролов.

Самоучитель


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

public

void

if (stopThread)

WriteThread Так как в своей п р о г р а м м е мы с о б и р а е м с я создавать н е с к о л ь к о р а з н ы х о б ъ е к т о в класса S t r i n g W r i t e r , то нам п р и ш л о с ь и з б а в и т ь с я от с т а т и ч е с к и х полей и м е т о д о в . Если б ы , н а п р и м е р , поле s t r было с т а т и ч е с к и м , все о б ъ е к т ы класса S t r i n g W r i t e r в ы в о д и л и бы на консоль одну и ту же строку. П р и ч и н а этого в т о м , что для всех т а к и х о б ъ е к т о в был бы создан е д и н с т в е н н ы й

с т а т и ч е с к о г о поля s t r .

Чтобы запустить в отдельном потоке метод W r i t e T h r e a d , объявленный

мы и с п о л ь з у е м м е т о д

в классе S t r i n g W r i t e r с л е д у ю щ и м о б р а з о м :

public void go myThreadDelegate thr

new

new

Этот метод создает делегат

для метода

том н а его базе создает п о т о к класса T h r e a d . метода

а по¬

Далее поток запускается при помощи

Start.

Метод

stop

класса

StringWriter

записывает значение

true

в

поле

stop-

Thread:

public

void

stopThread

true;

Это п р и в о д и т к т о м у , что поток метода W r i t e T h r e a d п р е к р а щ а е т с в о ю р а б о т у , так как п р е р ы в а е т с я б е с к о н е ч н ы й ц и к л , в этом м е т о д е . Р а с с м о т р и м теперь м е т о д Main. В этом м е т о д е мы создаем два о б ъ е к т а класса S t r i n g W r i t e r , п е р в ы й из к о т о р ы х будет в ы в о д и т ь на к о н с о л ь в б е с к о н е ч н о м ц и к л е с и м в о л а второй — с и м в о л

StringWriter StringWriter

sw2

new new

Глава 10. Многопоточность

325


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

объяв­

л е н н о г о в классе S t r i n g W r i t e r :

Здесь мы с н а ч а л а «запускаем» п е р в ы й о б ъ е к т , а з а т е м , с з а д е р ж к о й 250 мс, второй. Д а л е е г л а в н ы й п о т о к п р о г р а м м ы (т. е. м е т о д M a i n ) п е р е х о д и т в с о с т о я н и е о ж и д а н и я ввода строки с к л а в и а т у р ы :

Если в этот м о м е н т нажать к л а в и ш у Enter, м е т о д M a i n о с т а н о в и т в ы в о д с и м в о л о в на

консоль,

вызвав

метод

stop

для

каждого

из

созданных

объектов

класса

StringWriter:

В о т что вы у в и д и т е на к о н с о л и : Потоки Поток Поток

запущены.

Нажмите

WriteThread WriteThread

Enter для

остановки

потоков.

остановлен остановлен

О б р а т и т е в н и м а н и е , что в этом п р и м е р е п р о г р а м м ы мы п о л н о с т ь ю скрыли от ме¬ тода M a i n тот факт, что в классе S t r i n g W r i t e r п р и м е н я ю т с я с р е д с т в а м н о г о п о т о ч н о с т и . Это у п р о щ а е т работу с о б ъ е к т а м и класса S t r i n g W r i t e r , д е л а е т код метода Main проще и понятнее.

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

Аварийное завершение потока В предыдущих примерах программ нами потоки з а в е р ш а л и с ь , « у м и р а я естественной П о т о к о с т а н а в л и в а л с я в м о м е н т в ы х о д а из метода, на базе ко¬ т о р о г о этот п о т о к был с о з д а н . П о м и м о э т о г о , с у щ е с т в у е т и д р у г а я в о з м о ж н о с т ь . З а п у щ е н н ы й п о т о к м о ж н о за¬ в е р ш и т ь а в а р и й н о , вызвав для э т о г о м е т о д A b o r t , о п р е д е л е н н ы й в классе T h r e a d . П р и этом в о з н и к н е т и с к л ю ч е н и е в результате н о р м а л ь н а я р а б о т а п о т о к а будет п р е р в а н а . А в а р и й н а я о с т а н о в к а п о т о к о в д е м о н с т р и р у е т с я в п р о г р а м м е , и с х о д н ы й т е к с т кото­ рой п р и в е д е н в л и с т и н г е 10.4.

326

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

Самоучитель


Листинг

10.4.

using using

Файл

System;

namespace class

AbortThread StringWriter

private private private private

string bool stopThread; ThreadStart Thread t h r ;

public

s)

stopThread

public

false;

void

WriteThread ex)

"Аварийная

public

void

потока

go

myThreadDelegate thr new

Глава

остановка

Многопоточность

new

327


public

void

stop()

stopThread

public

class

true;

void

AbortThreadApp

static

void

StringWriter StringWriter

args) swl sw2

new new запущены.

для

аварийной

Нажмите

Enter

остановки

О б р а т и т е в н и м а н и я на и з м е н е н и я , к о т о р ы е мы внесли в и с х о д н ы й т е к с т метода по с р а в н е н и ю с п р е д ы д у щ е й п р о г р а м м о й : public

void

try

328

А В Фролов, Г.

Фролов. Язык

Самоучитель


WriteThread ex)

остановка

потока

Теперь все свои д е й с т в и я п о т о к в ы п о л н я е т внутри блока t r y . Когда р о д и т е л ь с к и й поток (т. е. п о т о к , с о з д а в ш и й д о ч е р н и й поток на базе м е т о д а з а в е р ш а е т р а б о т у н а ш е г о п о т о к а а в а р и й н о , т о , как мы т о л ь к о что го¬ ворили, возникает исключение Н а ш м е т о д п е р е х в а т ы в а е т это и с к л ю ч е н и е , в ы д а в а я н а консоль с о о т в е т с т в у ю щ и е сообщения: Потоки

запущены.

Нажмите

E n t e r для

аварийной

остановки

потоков.

Thread was aborted. at at

in

Аварийная о с т а н о в к а Исключение: aborted. at System at

потока

Аварийная

потока

c#

28 WriteThread Thread was Sleep (Int32

остановка

being

being

millisecondsTimeout) in c#

28 WriteThread

Зачем н у ж н о п е р е х в а т ы в а т ь и с к л ю ч е н и е при а в а р и й н о м з а в е р ш е н и и п о т о к а ? Дело в т о м , что внутри п о т о к а могут и с п о л ь з о в а т ь с я р а з л и ч н ы е р е с у р с ы , требую¬ щие я в н о г о о с в о б о ж д е н и я . Э т о такие р е с у р с ы , н а п р и м е р , как о т к р ы т ы е ф а й л ы , базы д а н н ы х , с е т е в ы е с о е д и н е н и я и т. п. Х о т я с и с т е м а с б о р к и м у с о р а , в с т р о е н н а я в среду и с п о л н е н и я п р о г р а м м С # , а в т о м а т и ч е с к и о с в о б о ж д а е т н е н у ж н ы е более блоки опера¬ т и в н о й п а м я т и , она не в с о с т о я н и и а в т о м а т и ч е с к и з а к р ы т ь файл или базу д а н н ы х . В каком блоке л у ч ш е о с в о б о ж д а т ь р е с у р с ы : в блоке Блок

или в блоке

c a t c h н у ж н о и с п о л ь з о в а т ь в том с л у ч а е , когда п о т о к п о к а к о й - т о п р и ч и н е

не м о ж е т н е м е д л е н н о з а в е р ш и т ь с в о ю р а б о т у . В этом случае он д о л ж е н о т м е н и т ь ава¬ р и й н о е з а в е р ш е н и е , в ы з в а в метод Если

не

вызывать

метод

исключения Глава 10. Многопоточность

ResetAbort. ResetAbort в

блоке

c a t c h при

обработке

это и с к л ю ч е н и е в о з н и к н е т п о в т о р н о .

329


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

Пауза в работе потока В п р е д ы д у щ и х п р и м е р а х п р о г р а м м мы в ы з ы в а л и м е т о д S l e e p для п р и о с т а н о в к и ра¬ боты п о т о к а на к а к о е - т о в р е м я . В классе T h r e a d имеется два п е р е г р у ж е н н ы х м е т о д а S l e e p . П е р в ы й и з этих ме¬ т о д о в п р и н и м а е т в качестве е д и н с т в е н н о г о а р г у м е н т а з н а ч е н и е и н т е р в а л а з а д е р ж к и в м и л л и с е к у н д а х , а в т о р о й — с с ы л к у на о б ъ е к т класса T i m e S p a n . Класс у д о б е н для ф о р м а т н о г о и н т е р в а л а в р е м е н и . В нем предусмотре¬ но н е с к о л ь к о к о н с т р у к т о р о в : public public public public

TimeSpan ( i n t , TimeSpan ( i n t , TimeSpan

int, int, int,

int, int,

int); int,

П е р в ы й из этих к о н с т р у к т о р о в п о з в о л я е т з а д а в а т ь п е р и о д в р е м е н и в и н т е р в а л а х р а б о т ы с и с т е м н о г о т а й м е р а (100 С п о м о щ ь ю в т о р о г о м о ж н о задавать и н т е р в а л в часах, м и н у т а х и с е к у н д а х . Т р е т и й п о з в о л я е т у к а з ы в а т ь к о л и ч е с т в о д н е й , ч а с о в , м и н у т и секунд, а четвертый — количество дней, часов, минут, секунд и миллисекунд. Ниже мы показали пример использования второго варианта перегруженного мето­ да S l e e p для з а д е р ж к и п о т о к а на 2 ч, 1 мин и 10 с: int hour i n t int sec

2; 10; min,

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

что

Если работа метода остановлена указанным выше способом, то единственное, м о ж н о с д е л а т ь е п о т о к о м , — это з а в е р ш и т ь его р а б о т у а в а р и й н о м е т о д о м Р а с с м о т р и м п р о г р а м м у , и с х о д н ы й текст к о т о р о й п р е д с т а в л е н в л и с т и н г е 10.5.

Листинг

10.5.

Файл

using using

System;

class

StringWriter

private private

string Thread

thr; A

Г.

Фролов.

Самоучитель


public

s)

Str public

void

TimeSpan(0,

ex)

public

void

thr

new

public

ThreadStart (WriteThread)

void

namespace class

Thread(new

Timeout TimeoutApp

static

void

S t r i n g W r i t e r sw

args) new Enter

Глава 10. Многопоточность

для

остановки

331


метод public

в р а м к а х о т д е л ь н о г о потока:

void

try

ex)

П о л у ч и в у п р а в л е н и е , м е т о д W r i t e T h r e a d в ы в о д и т с о о б щ е н и е н а к о н с о л ь , а по­ том о с т а н а в л и в а е т с в о ю работу н а б е с к о н е ч н ы й п е р и о д в р е м е н и . Что же к а с а е т с я м е т о д а M a i n , то он з а п у с к а е т п о т о к на в ы п о л н е н и е , а п о т о м пре¬ р ы в а е т его р а б о т у в ы з о в о м метода a b o r t : StringWriter

new

StringWriter Enter

для

остановки

В р е з у л ь т а т е на к о н с о л и п о я в л я е т с я с о о б щ е н и е о в о з н и к н о в е н и и у ж е и з в е с т н о г о вам и с к л ю ч е н и я Нажмите E n t e r д л я о с т а н о в к и п о т о к а . Поток з а п у щ е н Thread was at at

in

being

c#

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

класса

StringWriter: А

Г.

Фролов. Язык

Самоучитель


public

void

go

thr new thr Start( Так как д е л е г а т , с о з д а н н ы й

нами н а базе м е т о д а W r i t e T h r e a d ,

нужен т о л ь к о

один раз для с о з д а н и я нового о б ъ е к т а класса T h r e a d , м ы н е м н о г о у п р о с т и л и класс S t r i n g W r i t e r и метод W r i t e T h r e a d . У п р о щ е н и е з а к л ю ч а е т с я в т о м , что мы те¬ перь не х р а н и м д е л е г а т в о т д е л ь н о м поле класса, а сразу после создания п е р е д а е м его к о н с т р у к т о р у класса T h r e a d .

Приостановка и возобновление работы З а п у с т и в поток н а в ы п о л н е н и е , п р о г р а м м а м о ж е т в р е м е н н о п р и о с т а н о в и т ь его р а б о т у , а затем п р о д о л ж и т ь работу п о т о к а с п р е р в а н н о г о места. Для того чтобы п р и о с т а н о в и т ь п о т о к , н е о б х о д и м о и с п о л ь з о в а т ь м е т о д T h r e a d . S u s p e n d . П р о д о л ж е н и е работы п о т о к а в ы п о л н я е т с я м е т о д о м В л и с т и н г е 10.6 мы привели п р и м е р п р о г р а м м ы , д е м о н с т р и р у ю щ е й и с п о л ь з о в а н и е упомянутых выше методов. Листинг

10.6.

Файл

using using

System;

class

StringWriter

private private private

string bool Thread

str;

public

s)

s stopThread

false;

Thread. Sleep (500)

WriteThread

Глааа

остановлен")

333


public

void

thr

go

new

public

void

stopThread

public

void

public

void

public

void

true;

Resume

thr

StartStop

namespace class

StartStopApp

static

void

StringWriter StringWriter

args)

sw2

new new

запущены.

Нажмите E n t e r

для п р и о с т а н о в к и

Console для в о з о б н о в л е н и я

334

приостановлены.

Нажмите

Enter

работы

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

Самоучитель


Нажмите

Enter

для о с т а н о в к и

Для п р и о с т а н о в к и и п о с л е д у ю щ е г о в о з о б н о в л е н и я р а б о т ы п о т о к а мы п р е д у с м о т р е ¬ л и в классе S t r i n g W r i t e r два м е т о д а : public

public

Suspend

void

Сразу после запуска г л а в н ы й п о т о к нашей п р о г р а м м ы , р а б о т а ю щ и й в р а м к а х мето¬ да M a i n , создает два о б ъ е к т а класса S t r i n g W r i t e r и з а п у с к а е т в к а ж д о м из них по¬ токи, отображающие символы на консоли: StringWriter StringWriter

swl sw2

new new

Далее н а ш а п р о г р а м м а о ж и д а е т , пока п о л ь з о в а т е л ь не н а ж м е т к л а в и ш у Enter, после чего работа п о т о к о в п р и о с т а н а в л и в а е т с я : "Потоки

Глава

запущены.

Многопоточность

Нажмите

Enter

для

приостановки

335


Чтобы вновь в о з о б н о в и т ь работу п о т о к о в , н у ж н о снова н а ж а т ь к л а в и ш у Enter: возобновления

("Потоки работы

приостановлены.

Нажмите

Enter

для

И н а к о н е ц , н а ж а в на к л а в и ш у Enter в т р е т и й р а з , вы о к о н ч а т е л ь н о з а в е р ш и т е р а б о ­ т у потоков методом s t o p : "Работа

Нажмите

Enter

для

остановки

Вот что п о я в и т с я на к о н с о л и : +Потоки

запущены.

-Потоки

E n t e r для

Нажмите

Работа Поток Поток

Нажмите

Нажмите WriteThread WriteThread

приостановки

Enter

Enter

для

для

потоков.

возобновления

остановки

работы

потоков.

остановлен остановлен

Таким о б р а з о м , т е п е р ь в ы з н а е т е , что п о т о к - р о д и т е л ь м о ж е т п р е р ы в а т ь , в р е м е н н о п р и о с т а н а в л и в а т ь и в о з о б н о в л я т ь р а б о т у с о з д а н н ы х им д о ч е р н и х п о т о к о в . Есть и еще одна в о з м о ж н о с т ь п о т о к а м и — и з м е н е н и е их п р и о р и т е т о в .

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

336

А В

Г. В. Фролов.

Самоучитель


К а к и м и м е н н о образом п р о и с х о д и т к о н к у р е н ц и я между п о т о к а м и з а п р о ц е с с о р н о е время? При п о м о щ и м е х а н и з м а приоритетов (priority). П р и л о ж е н и я С# могут у к а з ы в а т ь с л е д у ю щ и е значения для п р и о р и т е т о в о т д е л ь н ы х п о т о к о в : •

Highest,

• П о у м о л ч а н и ю вновь с о з д а н н ы й поток и м е е т н о р м а л ь н ы й п р и о р и т е т N o r m a l . Если о с т а л ь н ы е потоки в системе и м е ю т тот же самый п р и о р и т е т , то все потоки п о л ь з у ю т с я процессорным временем на равных правах. При необходимости вы можете повысить или понизить приоритет отдельных задач, определив для них другие значения приоритета. Потоки с п о в ы ш е н н ы м приоритетом ( A b o v e N o r m a l и H i g h e s t ) выполняются в первую очередь, а с п о н и ж е н н ы м ( B e l o w Normal и при отсутствии готовых к в ы п о л н е н и ю потоков, и м е ю щ и х более высокий приоритет. Зачем н у ж н о и з м е н я т ь п р и о р и т е т ы п о т о к о в ? Если ваша п р о г р а м м а в ы п о л н я е т к а к у ю - л и б о д л и т е л ь н у ю работу и о д н о в р е м е н н о ведет д и а л о г с п о л ь з о в а т е л е м , и м е е т смысл п о в ы с и т ь п р и о р и т е т потока, о т в е ч а ю щ е г о за такой д и а л о г . В п р о т и в н о м случае п о л ь з о в а т е л я будет р а з д р а ж а т ь з а м е д л е н н а я ре¬ акция п р о г р а м м ы на о п е р а ц и и , в ы п о л н я е м ы е при п о м о щ и м ы ш и и к л а в и а т у р ы . Длительные процессы лучше выполнять с низким приоритетом, чтобы они не мешали в ы п о л н е н и ю более важных и более срочных задач. В любом случае изменяйте приоритеты потоков только в том случае, когда это действительно необходимо. Теперь мы п о с м о т р и м на п р а к т и к е , как и з м е н е н и е п р и о р и т е т о в о т д е л ь н ы х п о т о к о в м о ж е т влиять на работу м н о г о п о т о ч н о й п р о г р а м м ы . В л и с т и н г е 10.7 мы п р и в е л и ис¬ х о д н ы й текст такой п р о г р а м м ы , з а п у с к а ю щ е й потоки с р а з н ы м и п р и о р и т е т а м и . Листинг using using

10.7.

Файл

System;

namespace class

Priority PriorityApp

public

static

void

i = 0;

Глава

i<100000;

i++)

337


public

static

void

i+ +

static

void

Main ( s t r i n g

T h r e a d hi Thread

В

главном

и

new new

классе

нашей

args)

Thread(new

программы мы

объявили

методы

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

т о м , а второй — с низким: public

static

void

i = 0;

public

static

void

for(long

i = 0;

i+ + )

i+ +

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

338

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

Самоучитель


чили в цикл о т о б р а ж е н и я с и м в о л о в п р о г р а м м н у ю з а д е р ж к у , р е а л и з о в а н н у ю с помо¬ щ ь ю ничего н е д е л а ю щ е г о цикла f o r ( l o n g i = 0;

i + + );

Почему м ы н е стали и с п о л ь з о в а т ь здесь метод T h r e a d . S l e e p ? Дело в т о м , что м е т о д S l e e p в ы п о л н я е т о ж и д а н и е таким с п о с о б о м , к о ­ торый не нагружает центральный процессор компьютера. Иными словами, поток, вы­ полнение которого задерживается методом не уменьшает ресурсы центрального процессора. Но чтобы наша п р о г р а м м а в ы в о д и л а с и м в о л ы на к о н с о л ь не с л и ш к о м б ы с т р о , нам как раз н у ж н о загрузить ч е м - н и б у д ь ц е н т р а л ь н ы й п р о ц е с с о р . И м е н н о эту задачу и ре¬ шает б е с п о л е з н ы й н а вид цикл З а м е т и м , что для д о с т и ж е н и я н е о б х о д и м о г о эф¬ фекта н а вашем к о м п ь ю т е р е , в о з м о ж н о , п р и д е т с я и з м е н и т ь к о л и ч е с т в о п р о х о д о в э т о г о цикла. П е р е д тем как з а п у с т и т ь п о т о к и , мы задаем их п р и о р и т е т ы , и з м е н я я с в о й с т в о Priority: Thread Thread

hi

new new

Thread(new

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

I

+ +

+ + + + + + + + + +

Если же п р и о р и т е т п о т о к а , в ы в о д я щ е г о на к о н с о л ь п л ю с ы , будет б о л ь ш е , чем при¬ оритет потока, о т о б р а ж а ю щ е г о м и н у с ы , к а р т и н а п р и м е р с л е д у ю щ и й вид: + + + + + + + + + + + ++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

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

Многопоточность

339


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

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

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

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

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

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

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

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

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

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

текущего

счета

и

также

убеждается,

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

340

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


fgdh jhhj