Компилятор не может придать смысл ключевому слову null в исходном тек сте обобщенного класса, поскольку обобщенный класс может быть инстанци
рован для любых типов данных. Вот почему в исходном тексте метода De q u e u e () используется следующая конструкция:
r e t u r n d e f a u l t ( Т ) ; / / З н а ч е н и е n u l l дл я т и п а Т
Эта строка указывает компилятору, что нужно посмотреть, что собой представляет тип Т и вернуть верное значение n u l l для этого типа. В случае P a c k a g e , который в ка честве класса представляет собой ссылочный тип, верным возвращаемым значением у дет n u l l . Однако для некоторых других Т это значение может быть иным, и компилятор сможет верно определить, что именно следует вернуть.
Если вы думаете, что обобщенный класс P r i o r i t y Q u e u e достаточно ги бок, то на прилагаемом компакт-диске вы можете найти еще более гибкую версию обобщенного класса P r i o r i t y Q u e u e и познакомиться с некоторыми принципами объектно-ориентированного дизайна, обратившись к де-| монстрационной программе P r o g r a m m i n g T o A n l n t e r f а с е .
Часто методы в обобщенных классах также должны быть обобщенными. Вы уже ви
дели это |
в примере в предыдущем разделе. Метод D e q u e u e () в классе Priori |
t y Q u e u e |
имеет возвращаемый тип Т. В этом разделе рассказывается, как можно ис |
пользовать обобщенные методы как в обобщенных, так и в необобщенных классах. Обобщенными могут быть даже методы в обычных необобщенных классах. Напри
мер, приведенный далее код показывает обобщенный метод Swap ( ) , разработанный дм обмена двух своих аргументов. Первый аргумент получает значение второго аргумента и наоборот (такой обмен проиллюстрирован на рис. 6.2 в главе 6, "Объединение дан
н ы х — |
классы и массивы"). Чтобы |
увидеть, что он работает, объявите два параметра |
Swap () |
с применением ключевого |
слова r e f , чтобы аргументы типов-значений также |
можно было передавать по ссылке, и посмотрите на результат работы метода (об исполь зовании ключевого слова r e f рассказывается в главе 7, "Функции функций").
Перед вами исходный текст демонстрационной программы, в которой объ является и используется метод Swap ( ) .
// G e n e r i c M e t h o d - м е т о д , к о т о р ы й может р а б о т а т ь с данными / / р а з н ы х т и п о в
u s i n g S y s t e m ;
n a m e s p a c e G e n e r i c M e t h o d
{
c l a s s P r o g r a m
{
//Main -
//р а б о т а е т
// M a i n ( ) ;
s t a t i c v o i d
{
п р |
о в е р к а д в у х |
в е р с и й обобщенного |
м е т о д а ; один |
с |
к л а с с о м на |
том |
же у р о в н е , |
ч т о |
и функция |
в т о р о й н а х о д и т с я |
в обобщенном |
к л а с с е |
M a i n ( s t r i n g [ ] |
a r g s ) |
|
|
360 |
Часть V. За базовыми классам» |
/ / П р о в е р к а |
обобщенного |
м е т о д а и з |
необобщенног о |
к л а с с а |
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 |
P a c k a g e F a c t o r y |
f a c t |
= |
new P a c k a g e F a c t o r y ( ) ; |
|
|
I P r i o r i t i z a b l e |
p a c k |
= |
f a c t . C r e a t e P a c k a g e ( ) ; |
/ / |
Обратите |
|
|
|
// внимание |
на |
интерфей с |
C r e a t e P a c k a g e () использует генератор случайных чисел для генерации случая го числа от 0 до 2 включительно, и применяет сгенерированное число в качестве приори тета нового объекта P a c k a g e , возвращаемого этим методом (и сохраняемого в пере менной типа P a c k a g e , а еще лучше — I P r i o r i t i z a b l e ) .
Еще немного о фабриках
Фабрики очень удобны для генерации большого количества тестовых дани! (фабрика не обязательно использует генератор случайных чисел — он потребо вался для конкретной демонстрационной программы P r i o r i t y Q u e u e ) .
Фабрики усовершенствуют программу, изолируя создание объектов. Каш раз при упоминании имени определенного класса в вашем исходном тексте! создаете зависимость (dependency) от этого класса. Чем больше таких завися мостей, тем больше степень связности классов, тем "теснее" они связаны дм с другом. Программистам давно известно, что следует избегать тесного CBHL вания. (Один из многих методов развязки (decoupling) заключается в примеви нии фабрик посредством интерфейсов, как, например, IPrioritizable! а не с использованием конкретных классов наподобие Package . ) Программ сты постоянно создают объекты непосредственно, с применением операм new, и это нормальная практика. Однако использование фабрик может сдем| код менее тесно связанным, а следовательно, более гибким.
Построение обобщенной фабрики
А если бы у вас был класс, который мог бы создавать любые необходима! вам объекты? Эту интересную концепцию достаточно легко спрограммирсвать, как видно из демонстрационной программы G e n e r i c l n t e r f a c e a прилагаемом компакт-диске.
/ / |
G e n e r i c l n t e r f а с е - и с п о л ь з о в а н и е обобщенного |
и н т е р ф е й с а |
/ / |
д л я |
р е а л и з а ц и и обобщенной |
фабрики |
|
|
u s i n g S y s t e m ; |
|
|
|
|
u s i n g S y s t e m . C o l l e c t i o n s . G e n e r i c ; |
|
|
|
n a m e s p a c e G e n e r i c l n t e r f a c e |
|
|
|
|
{ |
|
|
|
|
|
|
|
c l a s s |
P r o g r a m |
|
|
|
|
|
{ |
|
|
|
|
|
|
s t a t i c v o i d M a i n ( s t r i n g [ ] |
a r g s ) |
|
|
|
{ |
|
|
|
|
|
|
C o n s o l e . W r i t e L i n e ( " С о з д а н и е |
фабрики д л я |
" |
+ |
|
|
" п р о и з в о д с т в а B l o b б е з п а р а м е т р о в " ) ; |
|
G e n e r i c F a c t o r y < B l o b > b l o b F a c t = |
|
|
|
|
new G e n e r i c F a c t o r y < B l o b > ( ) ; |
|
|
|
C o n s o l e . W r i t e L i n e ( " С о з д а н и е |
фабрики для |
" |
+ |
|
|
" п р о и з в о д с т в а S t u d e n t s , " + |
|
|
" п а р а м е т р и з о в а н н ы х с т р о к о й " ) ; |
|
G e n e r i c F a c t o r y l < S t u d e n t , s t r i n g > s t u F a c t = |
|
|
new G e n e r i c F a c t o r y l < S t u d e n t , |
s t r i n g > ( ) ; |
|
|
366 |
Часть V. За базовыми классам. |
// |
См. д о п о л н и т е л ь н ы е |
к л а с с ы |
на п р и л а г а е м о м |
/ / к о м п а к т - д и с к е |
|
|
|
|
/ / |
Готовим |
м е с т о |
д л я |
х р а н е н и я |
о б ъ е к т о в |
L i s t < B l o b > |
b L i s t |
= |
new L i s t < B l o b > ( ) ; |
S t u d e n t [ ] |
s t u d e n t s |
= |
new S t u d e n t [ 1 0 ] ; |
C o n s o l e . W r i t e L i n e ( " С о з д а н и е и с о х р а н е н и е о б ъ е к т о в : " ) ; f o r ( i n t i = 0; i < 10 ; i + + )
{
C o n s o l e . W r i t e L i n e ( " ^ С о з д а н и е B l o b - " + |
|
|
" в ы з о в к о н с т р у к т о р а б е з " + |
|
|
" п а р а м е т р о в . " ) ; |
|
|
B l o b b = b l o b F a c t . C r e a t e ( ) ; |
|
|
b . n a m e = " b l o b " + i . T o S t r i n g ( ) ; |
|
|
b L i s t . A d d ( b ) ; |
|
|
|
C o n s o l e . W r i t e L i n e ( " ^ С о з д а н и е S t u d e n t с " + |
|
|
" у с т а н о в к о й имени - |
" |
+ |
|
|
" в ы з о в |
к о н с т р у к т о р а |
с |
одним " + |
|
|
" п а р а м е т р о м . " ) ; |
|
|
s t r i n g |
sName |
= " s t u d e n t " |
+ i . T o S t r i n g ( ) ; |
s t u d e n t s [ i ] |
= s t u F a c t . C r e a t e ( s N a m e ) ; |
|
|
/ / . . . |
с м . |
полный т е к с т |
н а п р и л а г а е м о м |
к о м п а к т - д и с к е |
}
|
/ / |
Вывод |
р е з у л ь т а т о в . |
|
|
|
|
|
|
|
|
f o r e a c h ( B l o b b i n b L i s t ) |
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
C o n s o l e . W r i t e L i n e ( b . T o S t r i n g ( ) ) ; |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
f o r e a c h ( S t u d e n t |
s |
i n s t u d e n t s ) |
|
|
|
|
|
|
{ C o n s o l e . W r i t e L i n e ( s . T o S t r i n g ( ) |
|
|
|
|
|
|
II |
Ожидаем |
п о д т в е р ж д е н и я |
п о л ь з о в а т е л я |
|
|
|
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 ( ) ; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/ / |
Классы |
д а н н ы х : |
S t u d e n t , B l o b |
( с м . |
|
также |
к о м п а к т - д и с к ) |
// |
B l o b |
- |
п р о с т о й |
к л а с с |
с к о н с т р у к т о р о м |
по |
умолчанию ( б е з |
/ / |
п а р а м е т р о в , |
|
п р е д о с т а в л я е м ы м С # ) . |
Для |
э к о н о м и и м е с т а к о д |
// |
опущен |
( с м . |
к о м п а к т - д и с к ) |
|
|
|
|
|
|
|
// |
S t u d e n t |
- к л а с с |
с |
к о н с т р у к т о р о м по |
умолчанию |
и |
// |
к о н с т р у к т о р о м с |
одним п а р а м е т р о м |
|
|
|
|
|
c l a s s S t u d e n t |
: |
ISettable<string> |
// |
Обобщенный |
интерфейс |
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/ / Ч а с т ь |
к о д а |
опущена, |
с м . |
к о м п а к т - д и с к |
|
|
p u b l i c S t u d e n t ( ) |
{ } |
|
/ / В ы |
должны |
о б е с п е ч и т ь |
н а л и ч и е |
|
|
|
|
|
|
|
/ / э т о г о к о н с т р у к т о р а |
|
p u b l i c S t u d e n t ( s t r i n g |
name) |
/ / |
к о н с т р у к т о р с |
одним |
{ |
|
|
|
|
|
|
|
|
// п а р а м е т р о м |
|
|
t h i s . n a m e |
|
name; |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Реализация ISettable |
|
|
|
|
|
|
|
public void SetParameter(string name) |
|
|
|
классами |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
367 |
this.name |
= |
name; |
// T o S t r i n g ( ) |
- на к о м п а к т - д и с к е |
} |
|
|
// См. также |
исходный т е к с т на к о м п а к т - д и с к е |
// Интерфейсы ISettable, используемые фабриками interface ISettable<U>
{
void SetParameter(U u ) ;
}
interface ISettable2<U, V>
{ void SetParameter(U u, V v ) ;
}
//Фабрика для объектов с конструктором без параметров не
//требует реализации ISettable
class GenericFactory<T> where T : new()
{ public T Create()
{
return new T ( ) ;
}
}
//Фабрика для создания объектов с конструктором с одним
//параметром
class GenericFactoryl<T, U> where T : ISettable<U>, new()
{
//Создает новый объект типа Т с параметром U и
//возвращает Т
{ |
|
|
|
|
|
public Т |
Create(U и) |
|
|
|
|
Т t = new Т ( ) ; |
|
|
|
|
t.SetParameter(и); |
// Т должен реализовать ISettable, |
|
// так что у него есть метод SetParameter() |
return |
t; |
|
|
|
|
} |
|
|
|
|
|
} |
|
|
|
|
|
/ / Н а п р и л а г а е м о м к о м п а к т - д и с к е е с т ь к о д |
фабрики |
дл я |
/ / с о з д а н и я |
о б ъ е к т о в , |
к о н с т р у к т о р которых |
т р е б у е т д в а |
/ / п а р а м е т р а |
|
|
|
|
} |
|
|
|
|
|
Демонстрационная программа |
G e n e r i c l n t e r f а с е |
на |
самом |
деле создает два |
обобщенных класса. |
|
|
|
|
Фабрику для создания объектов, которые имеют только конструктор по умолча нию, без параметров.
Фабрику для создания объектов, имеющих конструктор с одним параметром. Данный подход легко расширить для работы с объектами, конструкторы которых содержат
368 |
Часть V. За базовыми классами |
произвольное число параметров. На прилагаемом компакт-диске имеется исходный текст фабрики для создания объектов, имеющих конструктор с двумя параметрами.
Обобщенное создание объектов
Создание объектов, конструктор которых не имеет параметров, очень простое, так как при этом отсутствуют аргументы, о которых вам пришлось бы беспокоиться (за ис ключением <Т> — параметра типа):
// Метод |
C r e a t e ( ) к л а с с а G e n e r i c F a c t o r y < T > |
public Т |
C r e a t e () |
{
return new Т(); // Вызов конструктора без параметров
}
Обобщенный интерфейс вступает в игру из-за пределов обобщенных ограничений, рассматривавшихся ранее в этой части. Вот несколько способов наложения ограничений на параметр типа в заголовках обобщенных классов:
// Т |
должен быть |
к л а с с о м |
MyBase или е г о п о д к л а с с о м |
class |
MyClass<T> : |
w h e r e |
Т: |
MyBase |
// Т |
должен р е а л и з о в ы в а т ь |
I M y l n t e r f a c e |
class |
M y C l a s s < T > : |
w h e r e |
T : I M y l n t e r f a c e |
// T |
может быть т о л ь к о |
ссылочным типом |
class |
MyClass<T> : |
w h e r e |
Т : c l a s s |
// Т |
может быть т о л ь к о |
т и п о м - з н а ч е н и е м |
class |
M y C l a s s < T > : |
w h e r e |
Т : s t r u c t |
// Т |
должен и м е т ь |
к о н с т р у к т о р б е з п а р а м е т р о в |
class |
MyClass<T> : |
w h e r e |
Т.- |
new( ) |
Именно это последнее ограничение и устанавливает пределы вашим возможностям по написанию мощных обобщенных фабрик. Оно требует, чтобы тип Т имел конструктор по умолчанию, т.е. конструктор, у которого нет параметров. Тип Т может иметь и другие конструкторы, но один из них обязательно должен быть конструктором по умолчанию — напишете ли вы его сами или он будет сгенерирован С#.
Ограничение new () представляет собой требование для каждого обобщенного класса или метода, которые хотят создавать объекты типа Т. Однако у этого ограничения нет версий наподобие new (U) или new (U, V) для конструкторов с параметрами.
Обобщенный интерфейс также может иметь ограничения:
i n t e r f a c e I S e t t a b l e < T > : w h e r e Т : n e w ( ) . . .
Поиск обобщенного решения
Вопрос в том, как поступить в случае, когда конструктору надо передавать парамет ры. Фабрика объектов, конструктор которых требует одного параметра, должна исполь
зовать в методе C r e a t e () |
код наподобие приведенного: |
|
public |
Т |
C r e a t e ( U |
и) |
/ / и — а р г у м е н т , к о т о р ы й мы |
хотим |
|
|
|
|
/ / п е р е д а т ь к о н с т р у к т о р у |
|
| { |
|
|
|
|
|
Т t |
= |
new Т ( ) ; |
/ / Н о new Т( ) не может п р и н и м а т ь |
а р г у м е н т ы |
. . . |
|
|
/ / И ч т о т е п е р ь ? . . . |
|
Глава |
15. |
Обобщенное программирование |
369 |