1629295403-b876e2087bddebea4bc9666fb2377a02 (846199), страница 71
Текст из файла (страница 71)
Вы уже видели это в примере в предыдущем разделе. Метод D e q u e u e () в классе Priorit 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 - м е т о д ,// разных типовusing System;namespace GenericMethodкоторый можетработатьс данными{classProgram{// Main - п р о в е р к а двух в е р с и й обобщенного м е т о д а ; один// р а б о т а е т с к л а с с о м на том же у р о в н е , ч т о и функция// M a i n ( ) ; в т о р о й н а х о д и т с я в обобщенном к л а с с еs t a t i c void Main(string[] args){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 : n O n e = { 0 } , nTwo = {1} " ,nOne, nTwo);// Инстанцирование для i n tSwap<int>(ref nOne,ref n T w o ) ;C o n s o l e . W r i t e L i n e ( " \ ь \ Ъ П о с л е : n O n e = { 0 } , nTwo = { l } " ,. nOne, nTwo);Console . 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 : s O n e = { o } , sTwo = { l } " ,sOne,sTwo);// Инстанцирование для s t r i n gSwap<string>(ref sOne,ref s T w o ) ;C o n s o l e .
W r i t e L i n e ( " \ t \ t r i o a n e : s O n e = { o } , sTwo =sOne,sTwo);Console.WriteLine("\пОбобщенный метод в " +"обобщенном к л а с с е " ) ;Console . W r i t e L i n e ( " ^ П р о в е р к а для аргументов типаConsole.WriteLine("\t GenericClass.Swap с " +"аргументами типа i n t " ) ;n O n e = 1;nTwo = 2;{l}",int");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 : nOnenOne, nTwo);intClass.Swap(ref nOne,={o},nTwo ={1}",ref n T w o ) ;C o n s o l e . W r i t e L i n e ( " \ t \ t n o ^ e : n O n e = { o } , nTwo =nOne, nTwo);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 " ) ;Console.WriteLine("\t GenericClass.Swap с " +"аргументами типа s t r i n g " ) ;sOne = " o n e " ;sTwo = " t w o " ;{l}",GenericClass<string> strClass =new GenericClass<string>();C o n s o l e .
W r i t e L i n e ( " Д о : sOne = { o } ,sOne,sTwo);strClass.Swap(ref sOne,sTwo={l}",ref sTwo);C o n s o l e . W r i t e L i n e ( " П о с л е : s O n e = { o } , sTwo =sOne,sTwo);/ / Ожидаем п о д т в е р ж д е н и я п о л ь з о в а т е л яC o n s o l e . W r i t e L i n e ( " Н а ж м и т е <Enter> для " +"завершения п р о г р а м м ы . . .
" ) ;Console.Read();(лава15.Обобщенное программирование{l}",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 Swap<T>(ref T l e f t s i d e ,ref T rightSide)классе{T temp;temp = l e f t s i d e ;leftside = rightSide;r i g h t S i d e = temp;}} // e n d P r o g r a m/ / G e n e r i c C l a s s - обобщенный/ / Swapclassклассссобственным методомGenericClass<T>{//Swap '- метод обобщенный, п о с к о л ь к у принимает параметры/ / т и п а Т ; о б р а т и т е в н и м а н и е , ч т о м ы н е можем/ / и с п о л ь з о в а т ь Swap<T>, и н а ч е получим п р е д у п р е ж д е н и е// компилятора о дублировании параметра к л а с с аp u b l i c void Swap(ref T l e f t s i d e ,ref T rightSide){T temp;temp = l e f t s i d e ;l e f t s i d e = rightSide,r i g h t S i d e = temp;Первая версия Swap () в предыдущем примере (всего в нем рассмотрены две версии) — статическая функция класса P r o g r a m , объявленная следующим образом:publicstaticvoidSwap<T>(refTleftside,refTrightSide)Объявление обобщенного метода схоже с объявлением обобщенного к л а с с а — заименем метода следуют обобщенные параметры наподобие <Т>.
После этого можно использовать Т в методе в качестве произвольного типа, включая параметры метода и возвращаемый тип.В примере функция M a i n () дважды вызывает статическую функцию Swap ( ) , сперва инстанцируя ее для i n t , а затем для s t r i n g (в листинге эти места выделены полужирным шрифтом). Вот как выглядят вызовы этих методов (все верно, инстанцированиевыполняется при вызове метода):Swap<int>(refnOne,r e f nTwo);// Инстанцирование для i n tSwap<string>(refref362sOne,sTwo);//ИнстанцированиедляstringЧасть 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 >publicvoid////Swap(refTП а р а м е т р <Т> и с п о л ь з у е т с яфункции Swap()leftside,refTrightSide)нижев...Основное отличие между методом Swap () и его статическим аналогом в классеProgram заключается в том, что обобщенную параметризацию предоставляет классG e n e r i c C l a s s , так что метод Swap () в ней не нуждается (и более того, не может ееиспользовать).
В этой версии Swap () вы не найдете выражения <Т>, а сам Т применяется только в самом методе Swap ( ) .Кроме этого отличия, версии Swap () практически идентичны. Вот несколько вызометода Swap () в функции M a i n ( ) :GenericClass<int> 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//ВызовGenericClass<string> 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 gS t r C l a s s .
Swap ( r e f//Вызовi n t C l a s s . Swap ( r e fnOne,sOne,refrefnTwo) ;sTwo) ;егоегообъектаSwapOSwapOВ данном случае для типа i n t или s t r i n g инстанцируется сам класс. Тип Тможет использоваться в методе S w a p () точно так же, как и в его версии в необобщенном классе.Ограничения д л я обобщенного методаВам могут понадобиться ограничения для обобщенного метода, с тем чтобы он могпринимать только определенные виды типов, отвечающих некоторым требованиям —^какэто было сделано для класса P r i o r i t y Q u e u e ранее в настоящей главе.
В этом случае вы должны объявить метод примерно таким образом:static void Sort<T>(T[]tArray) where T:IComparable<T>( ... }Например, если методу необходимо сравнение параметров типа Т, то лучше потребовать от Т реализации интерфейса I C o m p a r a b l e и указать это ограничение в описанииобобщенного метода.Шва15.Обобщенноепрограммирование363Вы уже встречались с обобщенными классами и методами, и вполне логично задана]вопросом — могут ли быть обобщенные интерфейсы! (Необобщенные интерфейсы рассматривались в главе 14, "Интерфейсы и структуры".)В этом разделе вы познакомитесь с примером, объединяющим обобщенные классыметоды и интерфейсы.Обобщенные и необобщенные интерфейсыДавайте сравним обобщенные и необобщенные интерфейсы.// Необобщенныйinterface IDisplayable/ / ОбобщенныйinterfaceICertifiable<T>{{void Display();}voidCertify(Tcriteria);}Вот как выглядит шаблон использования интерфейса.
Сначала его надо объявить, ипоказано в приведенном фрагменте. Затем он должен быть реализован в вашем исходно»тексте некоторым классом следующим образом:// Необобщенныйc l a s s MyClass :/ / Обобщенныйc l a s s MyClass:IDisplayable...ICertifiable<MyCriteria>...После этого следует завершить реализацию интерфейса в вашем классе://Необобщенныйc l a s s MyClass:IDisplayable{publicvoidDisplay(){/ / Обобщенныйc l a s s MyClass:ICertifiable<MyCriteria>//Инстанцирование{publicvoidCertify(MyCriteriacriteria){Обратите внимание, что когда вы реализуете обобщенный интерфейс в классе,]вы инстанцируете его обобщенную часть с использованием имени реального]типа, такого как 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 со случайными приоритетами.