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

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

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

Добавлен: 11.12.2025

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

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

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

/ / П р о в е р к а

обобщенного

м е т о д а и з

необобщенног о

к л а с с а

C o n s o l e . W r i t e L i n e ( " О б о б щ е н н ы й м е т о д из " +

 

 

 

 

 

 

 

 

" н е о б о б щ е н н о г о к л а с с а : \ п " ) ;

 

 

C o n s o l e . W r i t e L i n e ( " \ t n p o B e p K a

д л я

а р г у м е н т о в

тип а

i n t " ) ;

i n t

nOne

=

1;

 

 

 

 

 

 

 

 

 

 

 

 

 

i n t

nTwo

=

2;

 

 

 

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t n e p e f l :

nOne

=

{0},

nTwo

=

{1} " ,

 

 

 

 

 

nOne ,

n T w o ) ;

 

 

 

 

 

 

/ / И н с т а н ц и р о в а н и е д л я i n t

 

 

 

 

 

 

 

 

 

Swap<int>(ref

nOne, ref

nTwo);

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ ь \ Ъ П о с л е :

nOne

=

{0},

nTwo

=

{ l } " ,

 

 

 

 

 

. n O n e , n T w o ) ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t П p o в e p K a д л я а р г у м е н т о в " +

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

" т и п а

s t r i n g " ) ;

 

 

 

 

 

 

s t r i n g

sOne

=

" o n e " ;

 

 

 

 

 

 

 

 

 

 

s t r i n g

sTwo

=

" t w o " ;

 

 

 

 

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t f l o :

sOne =

{ o } , sTwo

=

{ l } " ,

 

 

 

 

 

sOne ,

s T w o ) ;

 

 

 

 

 

 

/ / И н с т а н ц и р о в а н и е д л я s t r i n g

 

 

 

 

 

 

 

Swap<string>(ref sOne,

ref

sTwo);

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t r i o a n e :

sOne

=

{ o } ,

sTwo

=

{ l } " ,

 

 

 

 

 

s O n e , s T w o ) ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ п О б о б щ е н н ы й м е т о д в " +

 

 

 

 

 

 

 

 

 

"обобщенном

к л а с с е " ) ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( " ^ П р о в е р к а дл я а р г у м е н т о в т и п а i n t " ) ; C o n s o l e . W r i t e L i n e ( " \ t G e n e r i c C l a s s . S w a p с " +

 

 

" а р г у м е н т а м и т и п а i n t " ) ;

nOne

=

1;

nTwo

=

2;

GenericClass<int> intClass = new GenericClass<int>();

C o n s o l e . W r i t e L i n e ( " \ t \ t f l o :

nOne

=

{ o } , nTwo =

{ 1 } " ,

 

 

nOne ,

n T w o ) ;

 

 

 

 

 

 

intClass.Swap(ref nOne,

ref

nTwo);

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t \ t n o ^ e :

 

nOne

=

{ o } ,

nTwo

= { l } " ,

 

 

nOne ,

n T w o ) ;

 

 

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t n p o B e p K a д л я а р г у м е н т о в " +

 

 

 

 

 

 

 

 

 

 

 

 

 

" т и п а

s t r i n g " ) ;

 

 

 

 

C o n s o l e . W r i t e L i n e ( " \ t G e n e r i c C l a s s . S w a p с " +

 

 

 

 

" а р г у м е н т а м и т и п а s t r i n g " ) ;

 

sOne

=

" o n e " ;

 

 

 

 

 

 

 

 

 

 

 

sTwo

=

" t w o " ;

 

 

 

 

 

 

 

 

 

 

 

GenericClass<string> strClass

=

 

 

 

 

 

 

 

new

GenericClass<string>();

 

C o n s o l e . W r i t e L i n e ( " Д о :

sOne

=

{ o } ,

sTwo =

{ l } " ,

 

 

s O n e , s T w o ) ;

 

 

 

 

 

 

strClass.Swap(ref sOne,

ref

sTwo);

 

 

 

 

C o n s o l e . W r i t e L i n e ( " П о с л е :

sOne

=

{ o } ,

sTwo

=

{ l } " ,

 

 

s O n e , s T w o ) ;

 

 

 

 

 

 

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

п о л ь з о в а т е л я

 

 

 

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

 

 

 

 

 

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

 

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

 

 

 

 

 

 

 

 

 

 

 

(лава 15. Обобщенное программирование

361


} // e n d

Main

/ / s t a t i c

Swap

- обобщенный м е т о д в необобщенном к л а с с е

p u b l i c s t a t i c

v o i d S w a p < T > ( r e f T l e f t s i d e ,

 

 

r e f T r i g h t S i d e )

{

T t e m p ;

t e m p = l e f t s i d e ;

 

l e f t s i d e =

r i g h t S i d e ;

 

r i g h t S i d e = t e m p ;

}

}// e n d P r o g r a m

/

/ G e n e r i c C l a s s

- обобщенный к л а с с с с о б с т в е н н ы м методом

//Swap

c l a s s G e n e r i c C l a s s < T >

 

 

 

 

{

 

 

 

 

 

 

 

 

//Swap '-

м е т о д

обобщенный, п о с к о л ь к у

п р и н и м а е т параметры

//

т и п а

Т;

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

ч т о

мы

не

можем

/ /

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

Swap<T>, и н а ч е

получим

п р е д у п р е ж д е н и е

// к о м п и л я т о р а о д у б л и р о в а н и и п а р а м е т р а к л а с с а

p u b l i c v o i d S w a p ( r e f T l e f t s i d e ,

r e f T r i g h t S i d e )

{

 

 

 

 

 

 

 

 

 

T t e m p ;

 

 

 

 

 

 

 

t e m p = l e f t s i d e ;

 

 

 

 

 

l e f t s i d e

= r i g h t S i d e , -

 

 

 

 

r i g h t S i d e = t e m p ;

Первая версия Swap () в предыдущем примере (всего

в нем рассмотрены две вер­

сии) — статическая функция класса P r o g r a m , объявленная следующим образом:

p u b l i c s t a t i c v o i d S w a p < T > ( r e f T l e f t s i d e ,

r e f T r i g h t S i d e )

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

В примере функция M a i n () дважды вызывает статическую функцию Swap ( ) , спер­ ва инстанцируя ее для i n t , а затем для s t r i n g (в листинге эти места выделены полу­ жирным шрифтом). Вот как выглядят вызовы этих методов (все верно, инстанцирование

выполняется при вызове метода):

 

 

S w a p < i n t > ( r e f n O n e ,

 

 

r e f n T w o ) ;

/ / И н с т а н ц и р о в а н и е д л я

i n t

S w a p < s t r i n g > ( r e f s O n e ,

 

 

r e f s T w o ) ; / / И н с т а н ц и р о в а н и е д л я

s t r i n g

362

Часть V. За базовыми классами


При инстанцировании Swap () для i n t вы можете использовать i n t в качестве типа

^гументов в вызове.

Аналогично, при инстанцировании Swap () для s t r i n g можно

применять аргументы

типа s t r i n g . .

Обобщенный метод Swap () в обобщенном классе несколько отличается от описан­ ного метода. Эти отличия рассматриваются в следующем подразделе.

Обобщенные методы в обобщенных классах

В предыдущем примере имеется обобщенный класс G e n e r i c C l a s s , в котором со­ держится обобщенный метод Swap ( ) , объявленный следующим образом (здесь показан и заголовок класса, чтобы было понятно, откуда берется тип Т):

class G e n e r i c C l a s s < T > // П а р а м е т р <Т> и с п о л ь з у е т с я ниже в

//функции Swap()

public v o i d S w a p ( r e f

T l e f t s i d e ,

r e f T r i g h t S i d e ) . . .

 

Основное отличие между методом Swap ()

и его статическим аналогом в

классе

Program заключается в том,

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

класс

GenericClass, так что метод Swap ()

в ней не нуждается (и более того, не может ее

использовать). В этой версии Swap ()

вы не найдете выражения <Т>, а сам Т применя­

ется только в самом методе Swap ( ) .

 

 

 

Кроме этого отличия, версии Swap ()

практически идентичны. Вот несколько вызо-

метода Swap () в функции Main ( ) :

 

 

G e n e r i c C l a s s < i n t > i n t C l a s s =

 

/ / С о з д а н и е о б ъ е к т а

new G e n e r i c C l a s s < i n t > ( ) ;

 

/ /

д л я i n t

intClass . Swap ( r e f n O n e , r e f

nTwo) ; //

Вызов е г о S w a p O

G e n e r i c C l a s s < s t r i n g > s t r C l a s s = new G e n e r i c C l a s s < s t r i n g > ( ) ;

/ / С о з д а н и е о б ъ е к т а / / д л я s t r i n g

StrClass. Swap ( r e f s O n e ,

r e f

sTwo) ; //

Вызов е г о S w a p O

В данном случае для типа

i n t

или

s t r i n g

инстанцируется сам класс. Тип Т

может использоваться в методе Swap ()

точно так же, как и в его версии в необоб­

щенном классе.

 

 

 

 

Ограничения для обобщенного метода

Вам могут понадобиться ограничения для обобщенного метода, с тем чтобы он мог принимать только определенные виды типов, отвечающих некоторым требованиям — ^какэто было сделано для класса P r i o r i t y Q u e u e ранее в настоящей главе. В этом слу­ чае вы должны объявить метод примерно таким образом:

s t a t i c v o i d S o r t < T > ( T [ ] t A r r a y ) w h e r e T : I C o m p a r a b l e < T > ( . . . }

Например, если методу необходимо сравнение параметров типа Т, то лучше потребо­ вать от Т реализации интерфейса I C o m p a r a b l e и указать это ограничение в описании обобщенного метода.

Шва 15. Обобщенное программирование 363


Вы уже встречались с обобщенными классами и методами, и вполне логично задана] вопросом — могут ли быть обобщенные интерфейсы! (Необобщенные интерфейсы рас сматривались в главе 14, "Интерфейсы и структуры".)

В этом разделе вы познакомитесь с примером, объединяющим обобщенные классы методы и интерфейсы.

Обобщенные и необобщенные интерфейсы

Давайте сравним обобщенные и необобщенные интерфейсы.

// Необобщенный

// Обобщенный

i n t e r f a c e I D i s p l a y a b l e

i n t e r f a c e I C e r t i f i a b l e < T >

{

{

v o i d D i s p l a y ( ) ;

v o i d C e r t i f y ( T c r i t e r i a ) ;

}

}

Вот как выглядит шаблон использования интерфейса. Сначала его надо объявить, и показано в приведенном фрагменте. Затем он должен быть реализован в вашем исходно» тексте некоторым классом следующим образом:

//Необобщенный

c l a s s M y C l a s s : I D i s p l a y a b l e . . .

//Обобщенный

c l a s s M y C l a s s : I C e r t i f i a b l e < M y C r i t e r i a > . . .

После этого следует завершить реализацию интерфейса в вашем классе:

//Необобщенный

c l a s s M y C l a s s : I D i s p l a y a b l e

{

p u b l i c v o i d D i s p l a y ( )

{

//Обобщенный

c l a s s M y C l a s s

: I C e r t i f i a b l e < M y C r i t e r i a > / / И н с т а н ц и р о в а н и е

{

 

p u b l i c v o i d

C e r t i f y ( M y C r i t e r i a c r i t e r i a )

{

 

Обратите внимание, что когда вы реализуете обобщенный интерфейс в классе,] вы инстанцируете его обобщенную часть с использованием имени реального] типа, такого как M y C r i t e r i a .

Теперь вы можете видеть, почему интерфейс является обобщенным: он требует заме­ ны <Т>, используемого в качестве типа параметра или возвращаемого типа в одном или нескольких методах интерфейса. Другими словами, как и в случае обобщенного класса, вы указываете замещаемый тип для применения в обобщенных методах. Возможно, эти методы нужны для работы с различными типами данных, как и в случае класса коллек­ ции L i s t < T > или метода Swap<T> (Т i t e m l , Т i t e m 2 ) .

Часть V. За базовыми классами


Обобщенные интерфейсы — новинка, и я еще не рассматривал здесь все способы их _ использования. Основное их применение — в обобщенных коллекциях. Использование

Iобобщенной версии распространенного интерфейса С#, такого как I C o m p a r a b l e < T > , поможет избежать упаковки/распаковки типов-значений. Обобщенные интерфейсы мо­

гут также помочь реализовать такие вещи, как функции сортировки для применения коллекциями. (О том, что такое упаковка/распаковка, было рассказано в главе 14,

| "Интерфейсы и структуры".)

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

Использование (необобщенной) фабрики классов

Ранее в главе уже использовалась фабрика классов — хотя само назва­ ние "фабрика" вводится только сейчас — для генерации бесконечного потока объектов P a c k a g e со случайными приоритетами. Вот как вы­ глядит этот класс:

// P a c k a g e F a c t o r y я в л я е т с я ч а с т ь ю д е м о н с т р а ц и о н н о й программы // P r i o r i t y Q u e u e , н а х о д я щ е й с я на п р и л а г а е м о м к о м п а к т - д и с к е

//

P a c k a g e F a c t o r y -

нам нужен к л а с с ,

к о т о р ы й

з н а е т , к а к

//

создаются новые

п а к е т ы

нужного

нам

т и п а по

т р е б о в а н и ю ;

//

такой к л а с с н а з ы в а е т с я

ф а б р и к о

й

 

 

class P a c k a g e F a c t o r y

 

 

 

 

 

 

 

 

Random

r a n d

=

new

R a n d o m ( ) ;

//

Г е н е р а т о р

случайных

ч и с е л

/ / C r e a t e P a c k a g e

-

э т о т

м е т о д

фабрики в ы б и р а е т

случайный

// п р и о р и т е т , а з а т е м с о з д а е т п а к е т с э т и м п р и о р и т е т о м

public

P a c k a g e

C r e a t e P a c k a g e ( )

 

 

 

 

{

 

 

 

 

 

 

 

 

 

 

 

 

// В о з в р а щ а е т

с л у ч а й н о

выбранный п р и о р и т е т

п а к е т а .

Нам

//

нужны

з н а ч е н и я 0,

1

или

2

(меньшие

3)

 

 

i n t

nRand

= r a n d . N e x t ( 3 ) ;

 

 

 

 

 

/ / И с п о л ь з у е т с я д л я г е н е р а ц и и н о в о г о п а к е т а // П р и в е д е н и е к п е р е ч и с л е н и ю н е с к о л ь к о г р о м о з д к о , но

/ /

з а т о

п е р е ч и с л е н и я удобны при и с п о л ь з о в а н и и

/ /

к о н с т р у к ц и и s w i t c h

r e t u r n

new P a c k a g e ( ( P r i o r i t y ) n R a n d ) ;

Класс P a c k a g e F a c t o r y имеет один член-данные и один метод (фабрику легко реализовать и не как класс, а как метод — например, метод класса Program). Когда вы инстанцируете объект P a c k a g e F a c t o r y , он создает объект класса Random и сохраняет его в члене r a n d . R a n d o m — библиотечный класс С#, предназначенный для генерации случайных чисел. (Взгляните также на демонст­ рационную программу P a c k a g e F a c t o r y W i t h l t e r a t o r на прилагаемом компакт-диске.)

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

Для генерации объектов P a c k a g e со случайными приоритетами вызывается метод объ­ ЕКТА фабрики C r e a t e P a c k a g e ( ) , как показано в следующем фрагменте исходного текста:

Гшэ 15. Обобщенное программирование

365