/ / П р о в е р к а |
обобщенного |
м е т о д а и з |
необобщенног о |
к л а с с а |
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 |